Obtendo 39 bytes
Esta é uma explicação de como obtive uma solução de 39 bytes, que Dennis e JonathanFrech encontraram separadamente também. Ou melhor, explica como alguém poderia chegar à resposta em retrospectiva, de uma maneira muito melhor do que o meu caminho real para ela, que estava cheio de raciocínio enlameado e becos sem saída.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Escrevendo isso um pouco menos e com mais parênteses, isso se parece com:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
Paridades de bits
Começamos com uma idéia da minha solução de 47 bytes para gerar todos os números do formulário, n=2*k+b
onde k
conta 0,1,...,399
e b
é um bit de paridade que torna o número geral de 1s iguais.
Vamos escrever par(x)
para a paridade de bits de x
, ou seja, xor ( ^
) em todos os bits x
. Isso é 0 se houver um número par de 1 bits (número é mau) e 1 se houver um número ímpar de 1 bits. Pois n=2*k+b
temos que par(n) = par(k)^b
, para alcançar o mal par(n)==0
, precisamos b=par(k)
, isto é, o último pedaço de n
ser a paridade de bits dos bits anteriores.
Meus primeiros esforços no golfe foram expressar o par(k)
, primeiro diretamente com bin(k).count('1')%2
e depois com a manipulação de bits .
Atualizações de paridade
Ainda assim, não parecia haver uma expressão curta. Em vez disso, ajudou a perceber que há mais informações para trabalhar. Em vez de apenas calcular a paridade de bits do número atual,
k ----> par(k)
podemos atualizar o bit de paridade como incrementar k
a k+1
.
k ----> par(k)
|
v
k+1 ----> par(k+1)
Ou seja, como estamos contando k=0,1,2,...
, precisamos apenas manter a paridade de bits atual, em vez de calculá-la do zero a cada vez. A atualização de paridade de bits par(k+1)^par(k)
é a paridade do número de bits ativados passando de k
para k+1
, isto é par((k+1)^k)
.
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((k+1)^k)
Forma de (k+1)^k
Agora precisamos calcular par((k+1)^k)
. Pode parecer que não chegamos a lugar algum porque a paridade de bits de computação é exatamente o problema que estamos tentando resolver. Mas, os números expressos como (k+1)^k
têm a forma 1,3,7,15,..
, isto é, um abaixo da potência de 2, um fato frequentemente usado em hacks de bits . Vamos ver porque é isso.
Quando incrementamos k
, o efeito dos carregamentos binários é inverter o último 0
e tudo 1
à sua direita, criando um novo líder, 0
se não houver. Por exemplo, peguek=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
As colunas que causam um transporte estão marcadas com *
. Eles 1
mudam para 0
ae transmitem um bit de carry 1
, que continua propagando para a esquerda até atingir um 0
in k
, que muda para 1
. Quaisquer bits mais à esquerda não são afetados. Assim, quando k^(k+1)
verifica que picaram posições mudar k
para k+1
, ele encontra as posições da extrema direita 0
eo 1
's à direita. Ou seja, os bits alterados formam um sufixo, portanto, o resultado é 0 seguido de um ou mais 1. Sem os zeros à esquerda, existem números binários 1, 11, 111, 1111, ...
abaixo de uma potência de 2.
Informática par((k+1)^k)
Agora que entendemos que isso (k+1)^k
é limitado 1,3,7,15,...
, vamos encontrar uma maneira de calcular a paridade de bits desses números. Aqui, um fato útil é esse 1,2,4,8,16,...
módulo alternativo 3
entre 1
e 2
, desde 2==-1 mod 3
. Então, pegar 1,3,7,15,31,63...
módulos 3
dá 1,0,1,0,1,0...
, que são exatamente suas paridades de bits. Perfeito!
Então, podemos fazer a atualização par(k+1) = par(k) ^ par((k+1)^k)
como
par(k+1) = par(k) ^ ((k+1)^k)%3
Usando b
como a variável em que estamos armazenando a paridade, isso parece
b^=((k+1)^k)%3
Escrevendo o código
Juntando isso no código, iniciamos k
e o bit de paridade em b
at 0
, depois imprimimos n=2*k+b
e atualizamos repetidamente b=b^((k+1)^k)%3
e k=k+1
.
46 bytes
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
Experimente online!
Nós removemos parênteses em torno k+1
de ((k+1)^k)%3
porque Python precedência faz a adição primeiro de qualquer maneira, estranho quanto parece.
Aprimoramentos de código
Podemos fazer melhor trabalhando diretamente com uma única variável n=2*k+b
e executando as atualizações diretamente nela. Fazer k+=1
corresponde a n+=2
. E, a atualização b^=(k+1^k)%3
corresponde a n^=(k+1^k)%3
. Aqui, k=n/2
antes de atualizar n
.
44 bytes
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Experimente online!
Podemos encurtar n/2+1^n/2
(lembre-se disso (n/2+1)^n/2
) reescrevendo
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Como /2
remove o último bit, não importa se o fazemos antes ou depois do xoring. Então nós temos n^=(n+2^n)/2%3
. Podemos salvar outro byte observando que modulo 3
, /2
é equivalente a *2
equivale a -
, observando que n+2^n
é mesmo assim, a divisão é reduzir para metade real, sem pavimentação. Isto dán^=-(n+2^n)%3
41 bytes
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Experimente online!
Finalmente, podemos combinar as operações n^=c;n+=2
em n=(n+2)^c
, onde c
está um pouco. Isso funciona porque ^c
atua apenas no último bit e +2
não se importa com o último bit, portanto as operações são comutadas. Novamente, a precedência permite omitir parênteses e escrever n=n+2^c
.
39 bytes
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Experimente online!