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+bcui kconta 0,1,...,399e brappresenta 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)==0abbiamo bisogno b=par(k), cioè l'ultimo bit di nessere 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 ka 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 ka 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)^khanno 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 0e tutto 1alla sua destra, creando un nuovo vantaggio 0se 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 1modifica a un 0e trasmettere un po 'di carry 1, che continua di moltiplicazione a sinistra fino a quando non colpisce un 0a k, che cambia a 1. Eventuali bit più a sinistra non sono interessati. Così, quando k^(k+1)i controlli quale bit posizioni cambiano ka k+1, che trova le posizioni della destra 0e 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 3tra 1e 2, da allora 2==-1 mod 3. Quindi, prendendo 1,3,7,15,31,63...modulo 3dà 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 bcome variabile in cui stiamo memorizzando la parità, questo sembra
b^=((k+1)^k)%3
Scrivere il codice
Mettendo questo insieme nel codice, iniziamo ke il bit di parità bsia su 0, quindi ripetutamente stampa n=2*k+be aggiornamento b=b^((k+1)^k)%3e 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+1a ((k+1)^k)%3causa 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+bed eseguendo gli aggiornamenti direttamente su di essa. Fare k+=1corrisponde a n+=2. E l'aggiornamento b^=(k+1^k)%3corrisponde a n^=(k+1^k)%3. Qui, k=n/2prima 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é /2rimuove 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+=2in n=(n+2)^c, dove cè un po '. Questo funziona perché ^cagisce solo sull'ultimo bit e +2non 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!