39 Bytes bekommen
Dies ist eine Erklärung, wie ich zu einer 39-Byte-Lösung gekommen bin, die Dennis und JonathanFrech ebenfalls separat gefunden haben. Oder vielmehr, es erklärt, wie man im Nachhinein zu der Antwort gelangen kann, und zwar auf eine Weise, die viel schöner ist als mein tatsächlicher Weg dahin, der voller schlammiger Überlegungen und Sackgassen war.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Wenn Sie dies etwas weniger golfen und mit mehr Parens schreiben, sieht das so aus:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
Bitparitäten
Wir beginnen mit einer Idee von meiner 47-Byte - Lösung alle Zahlen der Form ausgeben , n=2*k+b
wo k
aufwärts zählt, 0,1,...,399
und b
ist ein Paritätsbit, das die Gesamtzahl von 1 macht , auch.
Schreiben wir par(x)
für die Bitparität von x
, das ist das xor ( ^
) aller Bits in x
. Dies ist 0, wenn es eine gerade Anzahl von 1-Bits gibt (die Anzahl ist böse), und 1, wenn es eine ungerade Anzahl von 1-Bits gibt. Denn n=2*k+b
wir haben par(n) = par(k)^b
, um das Böse zu erreichen par(n)==0
, müssen wir b=par(k)
das letzte Bit n
der Bit-Parität der vorhergehenden Bits sein.
Meine ersten Bemühungen um Golf waren die auf ausdrücken par(k)
, zunächst direkt mit bin(k).count('1')%2
, und dann mit Bit - Manipulation .
Paritätsaktualisierungen
Trotzdem schien es keinen kurzen Ausdruck zu geben. Stattdessen hat es geholfen zu erkennen, dass es mehr Informationen gibt, mit denen man arbeiten kann. Anstatt nur die Bit-Parität der aktuellen Zahl zu berechnen,
k ----> par(k)
wir können die Bit - Parität aktualisieren , wie wir erhöhen k
zu k+1
.
k ----> par(k)
|
v
k+1 ----> par(k+1)
Das heißt, da wir aufwärts zählen k=0,1,2,...
, müssen wir nur die aktuelle Bitparität beibehalten, anstatt sie jedes Mal von Grund auf neu zu berechnen. Die Bitparitätsaktualisierung par(k+1)^par(k)
ist die Parität der Anzahl von Bits, die beim Wechseln von k
nach k+1
, d par((k+1)^k)
. H., Gewechselt werden .
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((k+1)^k)
Eine Form von (k+1)^k
Jetzt müssen wir rechnen par((k+1)^k)
. Es scheint, als hätten wir nichts erreicht, weil die Berechnung der Bit-Parität genau das Problem ist, das wir zu lösen versuchen. Zahlen, die als ausgedrückt werden, (k+1)^k
haben jedoch die Form 1,3,7,15,..
, dh eine unter einer Potenz von 2, eine Tatsache, die häufig in Bit-Hacks verwendet wird . Mal sehen, warum das so ist.
Wenn wir inkrementieren k
, besteht der Effekt der binären Überträge darin, die letzten 0
und alle 1
nach rechts zu invertieren und eine neue Führung zu erzeugen, 0
wenn keine vorhanden wäre. Nehmen wir zum Beispielk=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
Die Spalten, die einen Übertrag verursachen, sind mit gekennzeichnet *
. Diese haben eine 1
Änderung von a 0
und leiten ein Übertragsbit von weiter 1
, das sich so lange nach links ausbreitet, bis es ein 0
In trifft k
, das sich zu ändert 1
. Etwaige Bits weiter links bleiben davon unberührt. Also, wenn k^(k+1)
überprüft , welche Positionen ändern biß k
zu k+1
, findet er die Positionen der äußersten rechten 0
und der 1
‚s auf der rechten Seite . Das heißt, die geänderten Bits bilden ein Suffix. Das Ergebnis sind also Nullen, gefolgt von einer oder mehreren Einsen. Ohne die führenden Nullen gibt es Binärzahlen 1, 11, 111, 1111, ...
, die unter einer Zweierpotenz liegen.
Computing par((k+1)^k)
Nun, da wir verstehen, dass dies auf (k+1)^k
beschränkt ist 1,3,7,15,...
, wollen wir einen Weg finden, die Bitparität solcher Zahlen zu berechnen. Hier ist eine nützliche Tatsache, dass 1,2,4,8,16,...
abwechselnd modulo 3
zwischen 1
und 2
, da 2==-1 mod 3
. Also, unter 1,3,7,15,31,63...
Modulo 3
gibt 1,0,1,0,1,0...
, was genau ihre Bit-Paritäten sind. Perfekt!
So können wir das Update durchführen par(k+1) = par(k) ^ par((k+1)^k)
als
par(k+1) = par(k) ^ ((k+1)^k)%3
Unter Verwendung b
als Variable wir die Parität in sind speichern, das sieht aus wie
b^=((k+1)^k)%3
Code schreiben
Setzen wir dies in Code zusammen, starten wir k
und setzen das Paritätsbit b
beide auf 0
, drucken n=2*k+b
und aktualisieren dann wiederholt b=b^((k+1)^k)%3
und k=k+1
.
46 Bytes
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
Probieren Sie es online!
Wir haben Parens k+1
in entfernt, ((k+1)^k)%3
weil Python die Addition sowieso zuerst ausführt, komisch, wie es aussieht.
Code Verbesserungen
Wir können es jedoch besser machen, indem wir direkt mit einer einzelnen Variablen arbeiten n=2*k+b
und die Aktualisierungen direkt darauf durchführen. Tun k+=1
entspricht n+=2
. Und die Aktualisierung b^=(k+1^k)%3
entspricht n^=(k+1^k)%3
. Hier k=n/2
vor dem Update n
.
44 Bytes
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Probieren Sie es online!
Wir können n/2+1^n/2
dies (n/2+1)^n/2
durch Umschreiben verkürzen (denken Sie daran )
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Da /2
das letzte Bit entfernt wird, spielt es keine Rolle, ob wir es vor oder nach dem xor-ing machen. Also haben wir n^=(n+2^n)/2%3
. Wir können ein weiteres Byte speichern mit der Feststellung , dass Modulo 3
, /2
gleichwertig ist *2
äquivalent ist -
, unter Hinweis darauf , dass n+2^n
ist sogar so die Division ohne Boden tatsächliches Halbieren ist. Das gibtn^=-(n+2^n)%3
41 Bytes
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Probieren Sie es online!
Schließlich können wir die Operationen n^=c;n+=2
in kombinieren n=(n+2)^c
, wo c
ein bisschen ist. Dies funktioniert, weil ^c
nur das letzte Bit bearbeitet wird und das letzte Bit +2
nicht berücksichtigt wird, sodass die Operationen pendeln. Wieder lässt uns der Vorrang die Parens auslassen und schreiben n=n+2^c
.
39 Bytes
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Probieren Sie es online!