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.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
En écrivant ceci un peu moins golfé et avec plus de parens, cela ressemble à:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
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ù ils k
comptent 0,1,...,399
et b
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 bits x
. 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. Pour n=2*k+b
, nous avons par(n) = par(k)^b
, pour ainsi atteindre le mal dont par(n)==0
nous avons besoin b=par(k)
, à savoir le dernier bit de n
soit la parité binaire des bits précédents.
Mes premiers efforts au golf ont été d’exprimer le par(k)
, d’abord directement avec bin(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,
k ----> par(k)
nous pouvons mettre à jour la parité bit comme on incrémente k
à k+1
.
k ----> par(k)
|
v
k+1 ----> par(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 bit par(k+1)^par(k)
est la parité du nombre de bits basculés en allant k
vers k+1
, soit par((k+1)^k)
.
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((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 forme 1,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 dernier 0
et tout 1
à sa droite, créant ainsi un nouveau principal 0
s’il n’y en avait pas. Par exemple, prenezk=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
Les colonnes provoquant un report sont identifiées par *
. Celles-ci ont un 1
changement en a 0
et transmettent un peu de retenue 1
, ce qui continue à se propager jusqu'à ce qu'il frappe un 0
dans k
, qui devient 1
. Les bits plus à gauche ne sont pas affectés. Ainsi, lorsque les k^(k+1)
contrôles qui changent les positions de bits k
à k+1
, il trouve les positions des plus à droite 0
et 1
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 binaires 1, 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 ce 1,2,4,8,16,...
modulo alternatif 3
entre 1
et 2
, depuis 2==-1 mod 3
. Donc, en prenant 1,3,7,15,31,63...
modulo 3
donne 1,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 que
par(k+1) = par(k) ^ ((k+1)^k)%3
En utilisant b
comme variable dans laquelle nous stockons la parité, cela ressemble à
b^=((k+1)^k)%3
Écrire le code
En regroupant cela dans le code, nous commençons k
et le bit de parité b
à 0
, puis, imprimons n=2*k+b
et mettons à jour de manière répétée b=b^((k+1)^k)%3
et k=k+1
.
46 octets
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
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. Faire k+=1
correspond à n+=2
. Et, mise b^=(k+1^k)%3
à jour correspond à n^=(k+1^k)%3
. Ici, k=n/2
avant de mettre à jour n
.
44 octets
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Essayez-le en ligne!
Nous pouvons raccourcir n/2+1^n/2
(rappelez-vous (n/2+1)^n/2
) en réécrivant
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Puisque /2
supprime le dernier bit, peu importe si nous le faisons avant ou après le xor-ing. Donc nous avons n^=(n+2^n)/2%3
. Nous pouvons économiser un autre octet en notant que modulo 3
, /2
est équivalent *2
à -
, en notant que n+2^n
même si la division est divisée en deux sans plancher. Cela donnen^=-(n+2^n)%3
41 octets
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Essayez-le en ligne!
Enfin, nous pouvons combiner les opérations n^=c;n+=2
en n=(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'écrire n=n+2^c
.
39 octets
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Essayez-le en ligne!