Esta es una pregunta de consejos para jugar golf en Python con respecto a la pregunta de Evil Numbers en Anarchy Golf .
Un número es malo si su expansión binaria tiene un número par de 1. El desafío es imprimir los primeros 400 números malvados 0,3,5,...,795,797,798
, uno por línea.
Las presentaciones de Python 2 están dirigidas por llhuii con una solución de 42 bytes. El siguiente mejor es 46 bytes por mitchs, seguido de cinco envíos de 47 bytes. Parece que llhuii ha encontrado algo verdaderamente mágico que ha eludido a muchos golfistas fuertes de Python durante más de 2 años. Ahorrar 4 o 5 bytes es enorme para un golf tan corto.
Todavía estoy en 47 bytes. Espero que podamos resolver este rompecabezas como comunidad. Si recibimos una respuesta conjunta, la enviaría con los nombres de todos los que contribuyeron. Una respuesta a esta pregunta puede ser un código o una nueva idea o un análisis. Si eres llhuii, no nos lo estropees todavía.
Aunque las presentaciones no se revelan porque este problema es Infinito, se nos dan algunas pistas. La presentación ganadora tardó 0.1699 segundos en ejecutarse, mucho más tiempo que cualquier otra, lo que sugiere un método ineficiente. De las estadísticas de bytes, de los 42 caracteres, 23 son alfanuméricos [0-9A-Za-z]
y 19 son símbolos ASCII. Esto significa que no hay espacios en blanco en la solución de llhuii.
Puede probar su código en la página del problema , eligiendo Python en el menú desplegable de idioma o cargando un .py
archivo. Tenga en cuenta que:
- Python 2.7 es usado
- Su código debe ser un programa completo que imprima
- No hay entrada para este problema, como kolmogorov-complex
- Su programa solo tiene que imprimir los 400 valores como se indica, incluso si se rompería en valores más grandes
- Los programas tienen 2 segundos para ejecutarse
- Los programas pueden finalizar con error
- Puedes usar
exec
; el "exec es denegado" se refiere a shell exec
Respuestas:
Esta no es la misma solución que la de llhuii, pero también tiene 42 bytes de longitud.
Pruébalo en línea!
Gracias a @JonathanFrech, ahora estamos en 40 bytes.
Pruébalo en línea!
Hay otro byte para guardar, para un total de 39.
Pruébalo en línea!
fuente
print+n
, su solución debe ser diferente a la mía.-
signo cambiando conprint~n
oprint-n
y usando&
o~
, aunque no tengo nada para trabajar. Además,n=0;exec"print n;d=n^n+2;n^=d^-d%3;"*400
es bonito pero de 40 bytes.print-n
parece poco probable ya que no hay una relación fácil entre los bits de conjunto den
y-n
.print~n
Suena más prometedor en teoría, pero no puedo obtener menos de 40 bytes con este enfoque.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.
Escribiendo esto un poco menos golfizado y con más padres, esto se ve así:
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+b
dondek
cuenta0,1,...,399
yb
es un bit de paridad que iguala el número total de 1.Escribamos
par(x)
por la paridad de bits dex
, que es el xor (^
) en el que están todos los bitsx
. 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. Paran=2*k+b
, tenemospar(n) = par(k)^b
, así que para lograr el malpar(n)==0
necesitamosb=par(k)
, es decir, el último bit den
ser la paridad de bits de los bits anteriores.Mis primeros esfuerzos en el golf fueron expresar
par(k)
, al principio directamente conbin(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,
podemos actualizar el bit de paridad como incrementamos
k
ak+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 paridadpar(k+1)^par(k)
es la paridad del número de bits volteado al ir dek
ak+1
, es decirpar((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)^k
tienen la forma1,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 último0
y todo1
a su derecha, creando un nuevo líder0
si no hubiera ninguno. Por ejemplo, tomek=43=0b101011
Las columnas que causan un acarreo están marcadas con
*
. Estos tienen un1
cambio en ay0
pasan un bit de acarreo de1
, que se sigue propagando a la izquierda hasta que llega a un0
ink
, que cambia a1
. Cualquier parte más a la izquierda no se ve afectada. Por lo tanto, cuando sek^(k+1)
comprueba qué bit posiciones cambiank
ak+1
, encuentra las posiciones de la derecha0
y de la1
'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 binarios1, 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)^k
se limita a1,3,7,15,...
, busquemos una manera de calcular la paridad de bits de dichos números. Aquí, un hecho útil es ese1,2,4,8,16,...
módulo alternativo3
entre1
y2
, desde2==-1 mod 3
. Entonces, tomar1,3,7,15,31,63...
módulos3
da1,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)
comoUsando
b
como la variable en la que estamos almacenando la paridad, esto pareceEscribiendo el código
Poniendo esto junto en el código, comenzamos
k
y el bit de paridadb
en ambos0
, luego imprimimosn=2*k+b
y actualizamos repetidamenteb=b^((k+1)^k)%3
yk=k+1
.46 bytes
Pruébalo en línea!
Hemos eliminado parens alrededor
k+1
de((k+1)^k)%3
porque 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+b
y realizamos las actualizaciones directamente en ella. Hacerk+=1
corresponde an+=2
. Y, la actualizaciónb^=(k+1^k)%3
corresponde an^=(k+1^k)%3
. Aquí,k=n/2
antes de actualizarn
.44 bytes
Pruébalo en línea!
Podemos acortar
n/2+1^n/2
(recuerde que esto es(n/2+1)^n/2
) reescribiendoComo
/2
elimina el último bit, no importa si lo hacemos antes o después de xor-ing. Entonces, tenemosn^=(n+2^n)/2%3
. Podemos ahorrar otro byte por señalar que en módulo3
,/2
es equivalente a*2
es equivalente a-
, señalando quen+2^n
es aun así la división es reducir a la mitad real y sin suelo. Esto dan^=-(n+2^n)%3
41 bytes
Pruébalo en línea!
Finalmente, podemos combinar las operaciones
n^=c;n+=2
enn=(n+2)^c
, dondec
es un poco. Esto funciona porque^c
actúa solo en el último bit y+2
no le importa el último bit, por lo que las operaciones conmutan. Nuevamente, la precedencia nos permite omitir parens y escribirn=n+2^c
.39 bytes
Pruébalo en línea!
fuente
Esto le da a mi (xnor) una solución de 47 bytes y el pensamiento que me llevó a ello. No leas esto si quieres resolverlo tú mismo.
Una primera idea natural es iterar a través de los números del 0 al 799, imprimiendo solo aquellos con un número par de 1 en binario.
52 bytes
Pruébalo en línea!
Aquí,
~
toma el complemento de bits para cambiareven<->odd
el conteo y dar un valor de verdad solo en conteos pares.Podemos mejorar este método generando todos los valores en lugar de filtrar. Observe que los valores de salida son los números del 0 al 399, cada uno con un bit agregado para igualar el número de 1 bit.
Entonces, el
n
número th es2*n+b
con unob=0
o conb=1
. El bitb
se puede encontrar contando1
los bitsn
y tomando el módulo de conteo 2.49 bytes
Pruébalo en línea!
Podemos cortar los 2 bytes para
2*
iterar0,2,4,...
, lo que no da la posibilidad de contar1
. Podemos hacer esto usando unexec
ciclo que se ejecuta 400 veces y aumentandon
en 2 cada ciclo.47 bytes
Pruébalo en línea!
Y esa es mi solución de 47 bytes. Sospecho que la mayoría, si no todas las otras soluciones de 47 bytes son iguales.
fuente
exec
Está permitido su enfoque de 47 bytes de longitud ?exec
sino a la línea de comandoexec
.Presentación de Python 3 de llhuii
Estas son las presentaciones de Python 3 para Evil Numbers al momento de escribir:
llhuii probablemente portó su truco a Python 3, y se le ocurrió una solución que es
Portando el 47B de xnor a Python 3 literalmente, obtenemos este 50B:
Lo envié como
ppcg(xnor)
. (Agrega paréntesisexec
yprint
, que ahora son funciones). Tiene estadísticas de código diferentes de las otras respuestas de Python 3, que tienen una cierta cantidad de espacio en blanco. ¡Interesante!Sin embargo, hay una forma más corta de reescribirlo (
exec
tiende a perder su ventaja competitiva en Python 3):Son 49 bytes. Lo envié como
ppcg(xnor,alternative)
. ¡Esto tiene dos bytes de espacio en blanco, al igual que la respuesta de llhui! Esto me lleva a creer que la respuesta de Python 3 de llhuii se parece a esto (nueva línea, luego unwhile
bucle). Entonces, llhuii probablemente se usóexec
en Python 2 ywhile
en Python 3, al igual que nosotros; Esto explica la discrepancia de espacios en blanco.Nuestro 47B se convirtió en un 49B en Python 3. Lo interesante, ahora, es que el 42B de llhuii no se convirtió en un 44B, ¡se convirtió en un 45B! Algo sobre la solución de llhuii toma un byte extra en Python 3. Esto podría significar una variedad de cosas.
Lo primero que viene a la mente es la división : tal vez llhuii usa
/
en Python 2, que se convirtió//
en Python 3. (Si están contando en dos como nosotros, ¿n/2
podría usarse paran
retroceder un poco hacia la derecha?)La otra cosa que viene a la mente es operadores unarios después de la impresión . Nuestro se
print blah
convirtió enprint(blah)
(1 byte extra), pero si llhuii escribió algo así comoprint~-blah
en Python 2, se convertiríaprint(~-blah)
en Python 3.Tal vez hay otras ideas. Por favor hagamelo saber.
Estadísticas de código para todas las soluciones Py3, incluida la mía ahora:
fuente
Otros enfoques
1) Usando una fórmula para A001969
En lugar de convertir a binario, podría ser posible aprovechar la siguiente fórmula (de OEIS ):
Soy muy malo jugando al golf en Python, así que ni siquiera voy a intentarlo. Pero aquí hay un intento rápido en JS.
NB: No creo que sea una presentación JS válida, ya que solo está llenando una matriz sin mostrarla. Y aun así, es 5 bytes más largo que la mejor solución JS actual (que es de 45 bytes). Pero ese no es el punto aquí de todos modos.
Mostrar fragmento de código
Espero que pueda dar algo de inspiración.
Usar una matriz probablemente no sea una buena idea porque debe inicializarse y actualizarse. Podría ser más eficiente (en cuanto al tamaño del código) usar una función recursiva , lo que explicaría por qué la solución ganadora está tomando más tiempo que las otras.
2) Construyendo la secuencia Thue-Morse con sustituciones
En teoría, este código debería funcionar:
Pruébalo en línea! (versión ejecutable limitada a 20 términos)
Calcula la secuencia Thue-Morse con sustituciones consecutivas y busca la posición de 1 (Números malvados) en el mismo bucle.
Pero:
3) Construyendo la secuencia Thue-Morse con operaciones bit a bit
A partir de la definición directa de Wikipedia de la secuencia Thue-Morse , he llegado a este algoritmo (volviendo a JS ... lo siento):
donde hacemos un seguimiento de la maldad actual de la secuencia en e y utilizamos 170 como una máscara de bits de bits extraños en un byte.
fuente
f=lambda n:_
for n in range(400):print f(n)
ya ocupa 43 bytes. Tal vez haya una manera de simular la recursividad construyendo una matriz que haga referencia a sí misma, o una matriz que agregue elementos futuros al final.def
,for
,while
,lambda
(con un parámetro de por lo menos), etc.while~0:print~1
no requiere ningún espacio.((x=n++^n)^x/2)
parece algo detallado solo para encontrar el bit de ajuste más bajo. Todo ese desastre puede ser reemplazado por++n&-n
. Pruébalo en línea!Enfoque de contadores anidados
Tengo una idea para un enfoque diferente, pero no tengo suficiente experiencia en el golf de pitón, así que lo dejaré aquí para que ustedes lo consideren como otro posible punto de partida para el golf.
La idea no golfista:
Pruébalo en línea!
Nueve niveles de profundidad de anidación, todos los bucles son iguales, por lo que en mi opinión deberían ser construidos por
exec"something"*9+"deepest stuff"
. En la práctica, no sé si es posible hacer algo así con un ciclo for.Cosas a considerar para jugar al golf:
tal vez exista alguna otra posibilidad de hacer un ciclo dos veces que no sea un bucle for (probé un enfoque tipo quine con la cadena que se ejecutará y se pasó a sí misma como un argumento de formato dos veces, pero mi cabeza explotó).
también podría haber una mejor alternativa
if n<800:
, que es necesaria aquí porque de lo contrario seguiríamos imprimiendo números malvados hasta 2 ^ 10fuente
print
es una declaración, no una función, y por lo tanto no puede aparecer dentro de una comprensión.print '\n'.join([[[[[[[[[foo]foo]foo]foo]foo]foo]foo]foo]foo])
str.join
solo funciona en listas que contienen cadenas y no se deben imprimir los caracteres adicionales de la lista. Formatear solo tomaría una cantidad significativa de bytes.Idea: paridad de bits más corta
Se necesitan muchos caracteres para
bin(n).count('1')%2
calcular la paridad del recuento de bits. Tal vez una forma aritmética sea más corta, especialmente dada una longitud de bits limitada.Una manera linda de la misma longitud es
int(bin(n)[2:],3)%2
interpretar el valor binario como base3
(o cualquier base impar). Desafortunadamente, 4 de los bytes se gastan eliminando el0b
prefijo. También funciona para hacerint(bin(n)[2:])%9%2
.Otra idea viene de combinar bits usando xor. Si
n
tiene representación binariaabcdefghi
, entoncesEntonces,
r=n/16^n%16
es malo si y solo sin
es malo. Entonces podemos repetir eso comos=r/4^r%4
un valors
en el0,1,2,3
cual1
y2
no son malvables, comprobable con0<s<3
.52 bytes
Pruébalo en línea!
Esto resultó mucho más tiempo. Sin embargo, hay muchos botones para girar sobre cómo dividir el número, cómo verificar el número final (tal vez una tabla de búsqueda basada en bits). Sin embargo, sospecho que estos solo pueden llegar tan lejos.
fuente
to_bytes
función de enteros? Lo dudo, pero algo a tener en cuenta :)0b
:int(bin(n),13)%2
! : Dn=0;exec"print~int(bin(n),13)%2+n;n+=2;"*400
Por construcción,
n+n^n
siempre es malo, pero mis pobres habilidades en Python solo podrían llegar a una solución de 61 bytes:Gracias a @Peilonrayz por guardar 5 bytes y a @ Mr.Xcoder por guardar 1 byte:
fuente
for n in sorted(n^n*2for n in range(512))[:400]:print n
.n+n^n
es lo mismo quen^n*2
Idea: A006068 ("a (n) está codificado en gris en n")
La idea de Neil de ordenar todo
2n XOR n
me intrigó, así que traté de encontrar los índices detrás de este tipo. Escribí este código , y revela que podemos escribir algo como esto:¿Dónde
a(n)
está A006068 (n). Pruébalo en línea!Sin embargo, esto supone que tenemos una forma corta de calcular A006068. Esto ya es de 38 bytes, suponiendo que podamos calcularlo en 4 bytes (la
a(n)
parte). La implementación real (en el encabezado TIO) es mucho más larga que eso. No hay mucha esperanza para esto, creo.fuente
Idea: Reducir sobre XOR
Si haces XOR todos los fragmentos
n
juntos, será0
para el mal y1
para el no mal. Puede hacer esto con una función recursiva (que puede haber tomado más tiempo), de esta manera:Esto devuelve 1 para el mal.
Eso es 35 bytes y comprueba si un número es malo o no. Desafortunadamente,
filter
ya es de 6 bytes, por lo que esta no era la solución óptima literalmente, pero esta idea probablemente se pueda jugar.fuente
f=lambda n:n>1and f(n/2^n&1)or-~-n
por -1 byte.f(n/2^n&1)
devuelve 0 ...Método de sustitución: {1 -> {1, -1}, -1 -> {-1, 1}}
También puede hacer esta sustitución 10 veces {1 -> {1, -1}, -1 -> {-1, 1}}, luego aplanar y verificar las posiciones de 1
aquí está el código matemático
fuente