Ottenere 39 byte
Questa è una spiegazione di come ho ottenuto una soluzione a 39 byte, che Dennis e JonathanFrech hanno trovato anche separatamente. O meglio, spiega come si possa arrivare alla risposta col senno di poi, in un modo molto più bello del mio percorso effettivo, che era pieno di ragionamenti fangosi e vicoli ciechi.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Scrivendo un po 'meno golf e con più parentesi, sembra che:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
Parità di bit
Partiamo da un'idea della mia soluzione a 47 byte per generare tutti i numeri del modulo in n=2*k+b
cui k
conta 0,1,...,399
e b
rappresenta un bit di parità che rende il numero complessivo di 1 pari.
Scriviamo par(x)
per la parità dei bit di x
, ovvero xor ( ^
) tutti i bit in x
. Questo è 0 se c'è un numero pari di 1 bit (il numero è male) e 1 se c'è un numero dispari di 1 bit. Perché n=2*k+b
, abbiamo par(n) = par(k)^b
, quindi per raggiungere il male par(n)==0
abbiamo bisogno b=par(k)
, cioè l'ultimo bit di n
essere la parità dei bit dei bit precedenti.
I miei primi sforzi nel golf sono stati quelli di esprimere par(k)
, inizialmente direttamente con bin(k).count('1')%2
, e poi con un po 'di manipolazione .
Aggiornamenti di parità
Tuttavia, non sembrava esserci una breve espressione. Invece, ha aiutato a rendersi conto che ci sono più informazioni con cui lavorare. Piuttosto che calcolare semplicemente la parità dei bit del numero corrente,
k ----> par(k)
possiamo aggiornare la parità bit viene incrementato k
a k+1
.
k ----> par(k)
|
v
k+1 ----> par(k+1)
Cioè, dal momento che stiamo contando k=0,1,2,...
, dobbiamo semplicemente mantenere la parità di bit corrente anziché calcolarla ogni volta da zero. L'aggiornamento par(k+1)^par(k)
della parità dei bit è la parità del numero di bit capovolti passando da k
a k+1
, cioè par((k+1)^k)
.
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((k+1)^k)
Forma di (k+1)^k
Ora dobbiamo calcolare par((k+1)^k)
. Potrebbe sembrare che non siamo arrivati da nessuna parte perché il calcolo della parità dei bit è esattamente il problema che stiamo cercando di risolvere. Ma i numeri espressi come (k+1)^k
hanno la forma 1,3,7,15,..
, che è uno sotto una potenza di 2, un fatto spesso usato negli hack bit . Vediamo perché è così.
Quando incrementiamo k
, l'effetto del carry binario è di invertire l'ultimo 0
e tutto 1
alla sua destra, creando un nuovo vantaggio 0
se non ce ne fossero. Ad esempio, prendik=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
Le colonne che causano un carry sono contrassegnate con *
. Questi hanno una 1
modifica a un 0
e trasmettere un po 'di carry 1
, che continua di moltiplicazione a sinistra fino a quando non colpisce un 0
a k
, che cambia a 1
. Eventuali bit più a sinistra non sono interessati. Così, quando k^(k+1)
i controlli quale bit posizioni cambiano k
a k+1
, che trova le posizioni della destra 0
e la 1
's alla sua destra. Cioè, i bit modificati formano un suffisso, quindi il risultato è 0 seguito da uno o più 1. Senza gli zeri iniziali, ci sono numeri binari 1, 11, 111, 1111, ...
che sono uno sotto una potenza di 2.
Informatica par((k+1)^k)
Ora che comprendiamo che (k+1)^k
è limitato a 1,3,7,15,...
, troviamo un modo per calcolare la parità di bit di tali numeri. Qui, un fatto utile è che il 1,2,4,8,16,...
modulo alternativo 3
tra 1
e 2
, da allora 2==-1 mod 3
. Quindi, prendendo 1,3,7,15,31,63...
modulo 3
dà 1,0,1,0,1,0...
, che sono esattamente le loro parità di bit. Perfetto!
Quindi, possiamo fare l'aggiornamento par(k+1) = par(k) ^ par((k+1)^k)
come
par(k+1) = par(k) ^ ((k+1)^k)%3
Usando b
come variabile in cui stiamo memorizzando la parità, questo sembra
b^=((k+1)^k)%3
Scrivere il codice
Mettendo questo insieme nel codice, iniziamo k
e il bit di parità b
sia su 0
, quindi ripetutamente stampa n=2*k+b
e aggiornamento b=b^((k+1)^k)%3
e k=k+1
.
46 byte
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
Provalo online!
Abbiamo rimosso parentesi intorno k+1
a ((k+1)^k)%3
causa Python precedenza fa l'aggiunta prima in ogni caso, strano come sembra.
Miglioramenti del codice
Possiamo fare di meglio lavorando direttamente con una singola variabile n=2*k+b
ed eseguendo gli aggiornamenti direttamente su di essa. Fare k+=1
corrisponde a n+=2
. E l'aggiornamento b^=(k+1^k)%3
corrisponde a n^=(k+1^k)%3
. Qui, k=n/2
prima di aggiornare n
.
44 byte
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Provalo online!
Possiamo accorciare n/2+1^n/2
(ricordate che lo è (n/2+1)^n/2
) riscrivendo
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Poiché /2
rimuove l'ultimo bit, non importa se lo facciamo prima o dopo lo xoring. Quindi abbiamo n^=(n+2^n)/2%3
. Possiamo salvare un altro byte notando che modulo 3
, /2
è equivalente a *2
è equivalente -
, notando che n+2^n
è anche così la divisione è dimezzare senza pavimentazione. Questo dan^=-(n+2^n)%3
41 byte
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Provalo online!
Infine, possiamo combinare le operazioni n^=c;n+=2
in n=(n+2)^c
, dove c
è un po '. Questo funziona perché ^c
agisce solo sull'ultimo bit e +2
non si preoccupa dell'ultimo bit, quindi le operazioni commutano. Ancora una volta, la precedenza ci consente di omettere le parentesi e scrivere n=n+2^c
.
39 byte
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Provalo online!