Obteniendo 39 bytes
Esta es una explicación de cómo obtuve una solución de 39 bytes, que Dennis y JonathanFrech también encontraron por separado. O, más bien, explica cómo se puede llegar a la respuesta en retrospectiva, de una manera que es mucho mejor que mi camino real, que estaba lleno de razonamientos fangosos y callejones sin salida.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Escribiendo esto un poco menos golfizado y con más padres, esto se ve así:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
Paridades de bits
Comenzamos con una idea de mi solución de 47 bytes para generar todos los números del formulario n=2*k+bdonde kcuenta 0,1,...,399y bes un bit de paridad que iguala el número total de 1.
Escribamos par(x)por la paridad de bits de x, que es el xor ( ^) en el que están todos los bits x. Esto es 0 si hay un número par de 1 bits (el número es malo) y 1 si hay un número impar de 1 bits. Para n=2*k+b, tenemos par(n) = par(k)^b, así que para lograr el mal par(n)==0necesitamos b=par(k), es decir, el último bit de nser la paridad de bits de los bits anteriores.
Mis primeros esfuerzos en el golf fueron expresar par(k), al principio directamente con bin(k).count('1')%2, y luego con la manipulación de bits .
Actualizaciones de paridad
Aún así, no parecía haber una expresión corta. En cambio, ayudó a darse cuenta de que hay más información para trabajar. En lugar de simplemente calcular la paridad de bits del número actual,
k ----> par(k)
podemos actualizar el bit de paridad como incrementamos ka k+1.
k ----> par(k)
|
v
k+1 ----> par(k+1)
Es decir, dado que estamos contando k=0,1,2,..., simplemente necesitamos mantener la paridad de bits actual en lugar de calcularla desde cero cada vez. La actualización del bit de paridad par(k+1)^par(k)es la paridad del número de bits volteado al ir de ka k+1, es decir 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
Ahora necesitamos calcular par((k+1)^k). Puede parecer que no hemos llegado a ninguna parte porque calcular la paridad de bits es exactamente el problema que estamos tratando de resolver. Pero, los números expresados como (k+1)^ktienen la forma 1,3,7,15,.., es decir, uno por debajo de una potencia de 2, un hecho que a menudo se usa en hacks de bits . Veamos por qué es eso.
Cuando incrementamos k, el efecto de los acarreos binarios es invertir el último 0y todo 1a su derecha, creando un nuevo líder 0si no hubiera ninguno. Por ejemplo, tomek=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
Las columnas que causan un acarreo están marcadas con *. Estos tienen un 1cambio en ay 0pasan un bit de acarreo de 1, que se sigue propagando a la izquierda hasta que llega a un 0in k, que cambia a 1. Cualquier parte más a la izquierda no se ve afectada. Por lo tanto, cuando se k^(k+1)comprueba qué bit posiciones cambian ka k+1, encuentra las posiciones de la derecha 0y de la 1's de su derecha. Es decir, los bits modificados forman un sufijo, por lo que el resultado son 0 seguidos de uno o más 1. Sin los ceros a la izquierda, hay números binarios 1, 11, 111, 1111, ...que están uno debajo de una potencia de 2.
Informática par((k+1)^k)
Ahora que entendemos que (k+1)^kse limita a 1,3,7,15,..., busquemos una manera de calcular la paridad de bits de dichos números. Aquí, un hecho útil es ese 1,2,4,8,16,...módulo alternativo 3entre 1y 2, desde 2==-1 mod 3. Entonces, tomar 1,3,7,15,31,63...módulos 3da 1,0,1,0,1,0..., que son exactamente sus paridades de bits. ¡Perfecto!
Entonces, podemos hacer la actualización par(k+1) = par(k) ^ par((k+1)^k)como
par(k+1) = par(k) ^ ((k+1)^k)%3
Usando bcomo la variable en la que estamos almacenando la paridad, esto parece
b^=((k+1)^k)%3
Escribiendo el código
Poniendo esto junto en el código, comenzamos ky el bit de paridad ben ambos 0, luego imprimimos n=2*k+by actualizamos repetidamente b=b^((k+1)^k)%3y k=k+1.
46 bytes
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
Pruébalo en línea!
Hemos eliminado parens alrededor k+1de ((k+1)^k)%3porque Python precedencia hace la adición primero de todos modos, raro como parece.
Mejoras de codigo
Sin embargo, podemos hacerlo mejor si trabajamos directamente con una sola variable n=2*k+by realizamos las actualizaciones directamente en ella. Hacer k+=1corresponde a n+=2. Y, la actualización b^=(k+1^k)%3corresponde a n^=(k+1^k)%3. Aquí, k=n/2antes de actualizar n.
44 bytes
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Pruébalo en línea!
Podemos acortar n/2+1^n/2(recuerde que esto es (n/2+1)^n/2) reescribiendo
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Como /2elimina el último bit, no importa si lo hacemos antes o después de xor-ing. Entonces, tenemos n^=(n+2^n)/2%3. Podemos ahorrar otro byte por señalar que en módulo 3, /2es equivalente a *2es equivalente a -, señalando que n+2^nes aun así la división es reducir a la mitad real y sin suelo. Esto dan^=-(n+2^n)%3
41 bytes
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Pruébalo en línea!
Finalmente, podemos combinar las operaciones n^=c;n+=2en n=(n+2)^c, donde ces un poco. Esto funciona porque ^cactúa solo en el último bit y +2no le importa el último bit, por lo que las operaciones conmutan. Nuevamente, la precedencia nos permite omitir parens y escribir n=n+2^c.
39 bytes
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Pruébalo en línea!