Ceci est une question de astuce pour le golf en Python concernant la question de Evil Numbers sur Anarchy Golf .
Un nombre est mauvais si son extension binaire a un nombre pair de 1. Le défi consiste à imprimer les 400 premiers nombres pervers 0,3,5,...,795,797,798
, un par ligne.
Les soumissions Python 2 sont dirigées par llhuii avec une solution à 42 octets. Le second meilleur est 46 octets par mitchs, suivi de cinq soumissions de 47 octets. Il semble que llhuii ait trouvé quelque chose de vraiment magique qui a échappé à de nombreux golfeurs de Python depuis plus de 2 ans. Enregistrer 4 ou 5 octets est énorme pour un golf aussi court.
Je suis toujours à 47 octets. J'espère que nous pourrons résoudre ce casse-tête en tant que communauté. Si nous obtenons une réponse conjointement, je la soumettrais sous le nom de toutes les personnes qui ont contribué. Une réponse à cette question peut être un morceau de code, une nouvelle idée ou une analyse. Si vous êtes llhuii, s'il vous plait, ne le gâtez pas pour nous encore.
Bien que les soumissions ne soient pas révélées parce que ce problème est sans fin, nous avons quelques pistes. La soumission gagnante a pris 0,1699 secondes, beaucoup plus longtemps que toute autre, suggérant une méthode inefficace. Parmi les statistiques sur les octets, sur les 42 caractères, 23 sont alphanumériques [0-9A-Za-z]
et 19 sont des symboles ASCII. Cela signifie qu'il n'y a pas d'espace dans la solution de llhuii.
Vous pouvez tester votre code sur la page du problème , en choisissant Python dans la liste déroulante des langues ou en téléchargeant un .py
fichier. Notez que:
- Python 2.7 est utilisé
- Votre code doit être un programme complet qui imprime
- Il n'y a pas d'entrée pour ce problème, comme kolmogorov-complex
- Votre programme doit simplement imprimer les 400 valeurs telles qu'elles sont données, même s'il se casserait avec des valeurs plus grandes
- Les programmes ont 2 secondes pour s'exécuter
- Les programmes peuvent se terminer avec une erreur
- Vous pouvez utiliser
exec
; le "exec est refusé" se réfère à shell exec
Réponses:
Ce n'est pas la même solution que celle de llhuii, mais c'est aussi 42 octets.
Essayez-le en ligne!
Grâce à @JonathanFrech, nous sommes maintenant à 40 octets.
Essayez-le en ligne!
Un autre octet doit être sauvegardé, pour un total de 39.
Essayez-le en ligne!
la source
print+n
, leur solution doit être différente de la mienne.-
signe en déplaçant avecprint~n
ouprint-n
et en utilisant&
ou~
, même si je n'ai rien obtenu au travail. Aussi,n=0;exec"print n;d=n^n+2;n^=d^-d%3;"*400
c'est joli mais 40 octets.print-n
semble peu probable car il n’ya pas de relation facile entre les bits définis den
et-n
.print~n
semble plus prometteur en théorie, mais je ne peux pas descendre en dessous de 40 octets avec cette approche.Obtenir 39 octets
C'est une explication de la façon dont j'ai obtenu une solution de 39 octets, que Dennis et Jonathan Frech ont également trouvée séparément. Ou plutôt, il explique comment on pourrait arriver à la réponse avec le recul, d’une manière beaucoup plus agréable que mon chemin actuel, qui était plein de raisonnements boueux et d’impasses.
En écrivant ceci un peu moins golfé et avec plus de parens, cela ressemble à:
Bits parités
Nous commençons par une idée de ma solution de 47 octets qui consiste à sortir tous les nombres de la forme
n=2*k+b
où ilsk
comptent0,1,...,399
etb
constituent un bit de parité qui rend le nombre total de 1 égal.Écrivons
par(x)
pour la parité de bitsx
, c’est-à-dire que xor (^
) contient tous les bitsx
. C'est 0 s'il y a un nombre pair de 1 bits (le nombre est mauvais) et 1 s'il y a un nombre impair de 1 bits. Pourn=2*k+b
, nous avonspar(n) = par(k)^b
, pour ainsi atteindre le mal dontpar(n)==0
nous avons besoinb=par(k)
, à savoir le dernier bit den
soit la parité binaire des bits précédents.Mes premiers efforts au golf ont été d’exprimer le
par(k)
, d’abord directement avecbin(k).count('1')%2
, puis avec un peu de manipulation .Mises à jour de parité
Pourtant, il ne semblait pas y avoir d’expression courte. Au lieu de cela, cela a aidé à réaliser qu'il y avait plus d'informations avec lesquelles travailler. Plutôt que de simplement calculer la parité de bits du nombre actuel,
nous pouvons mettre à jour la parité bit comme on incrémente
k
àk+1
.En d'autres termes, puisque nous
k=0,1,2,...
comptons, nous devons simplement maintenir la parité de bits actuelle au lieu de la calculer à partir de zéro à chaque fois. La mise à jour de parité de bitpar(k+1)^par(k)
est la parité du nombre de bits basculés en allantk
versk+1
, soitpar((k+1)^k)
.Forme de
(k+1)^k
Maintenant, nous devons calculer
par((k+1)^k)
. Il peut sembler que nous n'ayons nulle part abouti, car le calcul de la parité entre bits est exactement le problème que nous essayons de résoudre. Mais, les nombres exprimés sous(k+1)^k
la forme1,3,7,15,..
, c’est-à-dire une valeur inférieure à une puissance de 2, fait souvent utilisé pour le piratage des bits . Voyons pourquoi.Lorsque nous incrémentons
k
, l’effet des transferts binaires est d’inverser le dernier0
et tout1
à sa droite, créant ainsi un nouveau principal0
s’il n’y en avait pas. Par exemple, prenezk=43=0b101011
Les colonnes provoquant un report sont identifiées par
*
. Celles-ci ont un1
changement en a0
et transmettent un peu de retenue1
, ce qui continue à se propager jusqu'à ce qu'il frappe un0
dansk
, qui devient1
. Les bits plus à gauche ne sont pas affectés. Ainsi, lorsque lesk^(k+1)
contrôles qui changent les positions de bitsk
àk+1
, il trouve les positions des plus à droite0
et1
est à sa droite. C'est-à-dire que les bits modifiés forment un suffixe, le résultat est donc un 0 suivi d'un ou plusieurs 1. Sans les zéros au début, il existe des nombres binaires1, 11, 111, 1111, ...
inférieurs à une puissance de 2.L'informatique
par((k+1)^k)
Maintenant que nous comprenons que cela
(k+1)^k
se limite à1,3,7,15,...
, trouvons un moyen de calculer la parité de bits de ces nombres. Ici, un fait utile est ce1,2,4,8,16,...
modulo alternatif3
entre1
et2
, depuis2==-1 mod 3
. Donc, en prenant1,3,7,15,31,63...
modulo3
donne1,0,1,0,1,0...
, qui sont exactement leurs parités de bits. Parfait!Donc, nous pouvons faire la mise
par(k+1) = par(k) ^ par((k+1)^k)
à jour en tant queEn utilisant
b
comme variable dans laquelle nous stockons la parité, cela ressemble àÉcrire le code
En regroupant cela dans le code, nous commençons
k
et le bit de paritéb
à0
, puis, imprimonsn=2*k+b
et mettons à jour de manière répétéeb=b^((k+1)^k)%3
etk=k+1
.46 octets
Essayez-le en ligne!
Nous avons retiré parens autour
k+1
de((k+1)^k)%3
parce que la priorité Python fait d'abord l'addition de toute façon, bizarre qu'il regarde.Améliorations du code
Nous pouvons toutefois faire mieux en travaillant directement avec une seule variable
n=2*k+b
et en effectuant les mises à jour directement sur celle-ci. Fairek+=1
correspond àn+=2
. Et, miseb^=(k+1^k)%3
à jour correspond àn^=(k+1^k)%3
. Ici,k=n/2
avant de mettre à journ
.44 octets
Essayez-le en ligne!
Nous pouvons raccourcir
n/2+1^n/2
(rappelez-vous(n/2+1)^n/2
) en réécrivantPuisque
/2
supprime le dernier bit, peu importe si nous le faisons avant ou après le xor-ing. Donc nous avonsn^=(n+2^n)/2%3
. Nous pouvons économiser un autre octet en notant que modulo3
,/2
est équivalent*2
à-
, en notant quen+2^n
même si la division est divisée en deux sans plancher. Cela donnen^=-(n+2^n)%3
41 octets
Essayez-le en ligne!
Enfin, nous pouvons combiner les opérations
n^=c;n+=2
enn=(n+2)^c
, oùc
est un peu. Cela fonctionne car^c
agit uniquement sur le dernier bit et+2
ne se soucie pas du dernier bit; les opérations sont donc commutées. Encore une fois, la préséance nous permet d'omettre les parens et d'écriren=n+2^c
.39 octets
Essayez-le en ligne!
la source
Cela donne la solution de 47 octets de mon (xnor) et la pensée qui l’a conduit à la solution. Ne lisez pas ceci si vous voulez comprendre cela vous-même.
Une première idée naturelle consiste à parcourir les nombres de 0 à 799, en imprimant uniquement ceux avec un nombre pair de 1 en binaire.
52 octets
Essayez-le en ligne!
Ici, le
~
bit est complémentaire pour pouvoir basculereven<->odd
dans le compte et ne donner une valeur de vérité que sur des comptes pairs.Nous pouvons améliorer cette méthode en générant toutes les valeurs au lieu de filtrer. Observez que les valeurs de sortie sont les nombres 0 à 399, chacune avec un bit ajouté pour rendre le nombre de 1 bits pair.
Ainsi, le
n
nombre e est soit2*n+b
avec soitb=0
oub=1
. Le bitb
peut être trouvé en comptant1
dans les bits den
et en prenant le compte modulo 2.49 octets
Essayez-le en ligne!
Nous pouvons couper les 2 octets pour
2*
en itérant0,2,4,...
, ce qui ne risque pas le nombre de1
. Nous pouvons le faire en utilisant uneexec
boucle qui s'exécute 400 fois et en augmentantn
de 2 chaque boucle.47 octets
Essayez-le en ligne!
Et c'est ma solution à 47 octets. Je soupçonne que la plupart, sinon toutes les autres solutions à 47 octets sont les mêmes.
la source
exec
autorisée?exec
mais à la ligne de commandeexec
.llhuii's Python 3 soumission
Voici les soumissions de Python 3 pour Evil Numbers au moment de la rédaction:
llhuii a probablement porté son astuce sur Python 3 et a proposé une solution
Portant littéralement 47B de xnor sur Python 3, nous obtenons ce 50B:
Je l'ai soumis comme
ppcg(xnor)
. (Il ajoute des parenthèses àexec
etprint
, qui sont maintenant des fonctions.) Il a des statistiques de code différentes de celles des autres réponses Python 3, qui contiennent toutes une certaine quantité d’espace. Intéressant!Il existe cependant un moyen plus court de le réécrire (il a
exec
tendance à perdre son avantage concurrentiel dans Python 3):C'est 49 octets. Je l'ai soumis comme
ppcg(xnor,alternative)
. Cela a deux octets d'espaces, comme la réponse de llhui! Cela me porte à croire que la réponse de llhuii à Python 3 ressemble à ceci (nouvelle ligne, puis unewhile
boucle). Donc llhuii a probablement été utiliséexec
dans Python 2 etwhile
dans Python 3, exactement comme nous; cela explique la différence d’espace.Notre 47B est devenu un 49B en Python 3. Ce qui est intéressant, c'est que le 42B de llhuii n'est pas devenu un 44B, il est devenu un 45B! Quelque chose à propos de la solution de llhuii prend un octet de plus dans Python 3. Cela pourrait vouloir dire différentes choses.
La première chose qui me vient à l’esprit est la division : peut-être que llhuii utilise
/
dans Python 2, qui est devenu//
Python 3. (s’ils comptent par deux comme nous, ilsn/2
pourraient alors être utilisés pourn
revenir d’un cran à la droite?)L’autre chose qui me vient à l’esprit est celle des opérateurs unaires après impression . Notre
print blah
est devenuprint(blah)
(1 octet en plus), mais si llhuii écrivait quelque chose commeprint~-blah
dans Python 2, cela deviendraitprint(~-blah)
en Python 3.Peut-être y a-t-il d'autres idées. S'il vous plaît, faites-moi savoir.
Statistiques de code pour toutes les solutions Py3, y compris la mienne:
la source
Autres approches
1) Utilisation d'une formule pour A001969
Plutôt que de convertir en binaire, il pourrait être possible de tirer parti de la formule suivante (à partir d' OEIS ):
Je suis très mauvais au golf en Python, donc je ne vais même pas essayer. Mais voici une tentative rapide dans JS.
NB: Je ne pense pas que ce serait une soumission JS valide, car il s'agit simplement de remplir un tableau sans l'afficher. Et même dans ce cas, il est 5 octets plus long que la meilleure solution JS actuelle (qui est de 45 octets). Mais ce n'est pas le point ici de toute façon.
Afficher l'extrait de code
Espérons que cela puisse vous inspirer.
L'utilisation d'un tableau n'est probablement pas une bonne idée car il doit être initialisé et mis à jour. Il pourrait être plus efficace (en termes de taille de code) d’utiliser une fonction récursive , ce qui expliquerait pourquoi la solution gagnante prend plus de temps que les autres.
2) Construire la séquence Thue-Morse avec substitutions
En théorie, ce code devrait fonctionner:
Essayez-le en ligne! (version exécutable limitée à 20 termes)
Il calcule la séquence Thue-Morse avec des substitutions consécutives et recherche la position de 1 (Evil Numbers) dans la même boucle.
Mais:
3) Construction de la séquence Thue-Morse avec des opérations au niveau des bits
En commençant par la définition directe par Wikipedia de la séquence Thue-Morse , je suis arrivé à cet algorithme (revenir à JS ... désolé):
où nous gardons une trace de la perversité actuelle de la séquence en e et utilisons 170 comme masque de bits de bits impairs dans un octet.
la source
f=lambda n:_
for n in range(400):print f(n)
prend déjà 43 octets. Peut-être existe-t-il un moyen de simuler la récursivité en construisant un tableau qui fait référence à lui-même ou un tableau qui ajoute des éléments futurs à la fin.def
,for
,while
,lambda
(avec un paramètre au moins), etc.while~0:print~1
ne nécessite pas d'espaces.((x=n++^n)^x/2)
semble quelque peu prolixe juste pour trouver le bit le plus bas. Tout ce gâchis peut être remplacé par++n&-n
. Essayez-le en ligne!Approche des compteurs imbriqués
J'ai une idée pour une approche différente, mais je ne suis pas assez expérimenté dans le golf python, je vais donc le laisser ici pour que vous puissiez le considérer comme un autre point de départ possible pour le golf.
L'idée ungolfed:
Essayez-le en ligne!
Neuf niveaux de profondeur de nidification, toutes les boucles sont les mêmes, donc dans mon esprit elles devraient être construites par
exec"something"*9+"deepest stuff"
. En pratique, je ne sais pas s'il est possible de faire quelque chose comme ceci avec un cycle pour.Points à considérer pour le golf:
peut-être y a-t-il une autre possibilité de faire deux fois le cycle en plus d'une boucle for (j'ai essayé une approche semblable à celle d'un quine avec la chaîne à exécuter passée à elle-même en tant qu'argument de formatage deux fois, mais ma tête a explosé).
il pourrait aussi y avoir une meilleure alternative à
if n<800:
, ce qui est nécessaire ici, sinon nous continuerions à imprimer des nombres pervers jusqu'à 2 ^ 10la source
print
est une déclaration, pas une fonction, et ne peut donc pas apparaître dans une compréhension.print '\n'.join([[[[[[[[[foo]foo]foo]foo]foo]foo]foo]foo]foo])
str.join
fonctionne uniquement sur les listes contenant des chaînes et les caractères de la liste supplémentaire ne doivent pas être imprimés. Le formatage seul prendrait un nombre d'octets important.Idée: Parité de bits plus courte
Il faut beaucoup de caractères pour
bin(n).count('1')%2
calculer la parité du nombre de bits. Peut-être qu'une méthode arithmétique est plus courte, surtout si la longueur de bit est limitée.Une façon sympa de même longueur
int(bin(n)[2:],3)%2
interprète la valeur binaire comme base3
(ou toute base impaire). Malheureusement, 4 octets sont utilisés pour supprimer le0b
préfixe. Cela fonctionne aussi à faireint(bin(n)[2:])%9%2
.Une autre idée vient de la combinaison de bits utilisant xor. Si
n
a une représentation binaireabcdefghi
, alorsAlors,
r=n/16^n%16
est le mal si et seulement sin
est le mal. On peut alors répéter ques=r/4^r%4
, une valeurs
dans0,1,2,3
, dont1
et2
ne sont pas mal, avec checkable0<s<3
.52 octets
Essayez-le en ligne!
Cela s'est avéré beaucoup plus longtemps. Il y a de nombreux boutons à tourner pour savoir comment scinder le nombre, vérifier le dernier nombre (peut-être une table de conversion basée sur des bits). Je soupçonne que ceux-ci ne peuvent aller aussi loin que cela.
la source
to_bytes
fonction d'entiers? J'en doute, mais quelque chose à considérer :)0b
:int(bin(n),13)%2
! : Dn=0;exec"print~int(bin(n),13)%2+n;n+=2;"*400
Par construction,
n+n^n
c'est toujours mauvais, mais mes pauvres compétences en Python ne pouvaient que trouver une solution à 61 octets:Merci à @Peilonrayz pour la sauvegarde de 5 octets et @ Mr.Xcoder pour la sauvegarde de 1 octet:
la source
for n in sorted(n^n*2for n in range(512))[:400]:print n
.n+n^n
est le même quen^n*2
Idée: A006068 («a (n) est codé en gris dans n»)
L'idée de Neil de trier tout ce qui
2n XOR n
m'intriguait, j'ai donc essayé de trouver les indices derrière ce genre. J'ai écrit ce code , et il révèle que nous pouvons écrire quelque chose comme ceci:Où
a(n)
est A006068 (n). Essayez-le en ligne!Cependant, cela suppose que nous disposons d’un moyen rapide de calculer A006068. C'est déjà 38 octets, en supposant que nous puissions le calculer en 4 octets (la
a(n)
partie). L'implémentation réelle (dans l'en-tête du TIO) est beaucoup plus longue que cela. Pas beaucoup d'espoir pour cela, je pense.la source
Idée: réduire plus de XOR
Si vous XOR tous les morceaux de
n
ensemble, ce sera0
pour le mal et1
pour le non-mal. Vous pouvez le faire avec une fonction récursive (qui aurait donc pris plus de temps?), Comme ceci:Cela retourne 1 pour le mal.
C'est 35 octets, et vérifie si un nombre est mal. Malheureusement, la valeur
filter
est déjà de 6 octets, ce n’était donc pas la solution optimale mais cette idée peut probablement être mise en pratique.la source
f=lambda n:n>1and f(n/2^n&1)or-~-n
pour -1 octet.f(n/2^n&1)
retourne 0 ...Méthode de substitution: {1 -> {1, -1}, -1 -> {-1, 1}}
Vous pouvez également effectuer cette substitution 10 fois {1 -> {1, -1}, -1 -> {-1, 1}}, puis aplatir et vérifier les positions des 1
voici le code mathematica
la source