Buscando alternativas más cortas al `rango (...)`

8

La mejor solución que he encontrado hasta ahora para un rompecabezas de código de golf en el que estoy trabajando incluye dos invocaciones de aspecto bastante gordorange . Soy muy nuevo en code golf, especialmente en Python, por lo que podría usar algunos consejos.

El fragmento relevante es este

[x for x in range(n+1,7**6)if`x`==`x`[::-1]*all(x%i for i in range(2,x))]

El límite superior del primero rangeno es fuerte. Debe ser al menos 98690, y todo lo demás es igual (en cuanto al golf, es decir), cuanto menor sea la diferencia entre este límite superior y 98690, mejor, en cuanto al rendimiento 1 . Estoy usando 7 6 (= 117649) porque 7**6es la expresión de Python más corta que se me ocurre que se ajusta a la factura.

En contraste, el límite inferior en el primero range, así como ambos límites en el segundo, son firmes. IOW, el programa (en su forma actual) producirá resultados incorrectos si se cambian esos límites.

¿Hay alguna manera de acortar una o ambas expresiones?

range(n+1,7**6)
range(2,x)

?

Por cierto, en este caso, el alias rangepara, por ejemplo, rno gana nada:

r=range;rr
rangerange

EDITAR: FWIW, el programa completo es este:

p=lambda n:[x for x in range(n+1,7**6)if`x`==`x`[::-1]*all(x%i for i in range(2,x))][0]

p(n)debe ser el primo palindrómico más pequeño mayor que n. Además, pno debe ser recursivo. Advertencia: ¡ya es obscenamente lento!


1 Sí, lo sé: el rendimiento es irrelevante en el golf de código, pero es por eso que escribí "todo lo demás es igual (en cuanto al golf, eso es)". Por ejemplo, mi elección de 7**6, y no la alternativa más obvia, pero de menor rendimiento, "equivalente al golf" 9**9. Me gusta ejecutar realmente mis intentos de golf de código, lo que significa no permitir que el rendimiento se degrade hasta el punto de que llevará años ejecutar el código. Si puedo evitarlo, por supuesto.

kjo
fuente
1
fwiw, podrías hacer el tuyo mucho más rápido para propósitos de prueba usando generadores; que es equivalente a excepción de que: p=lambda n:(x for x in xrange(n+1,7**6)if`x`==`x`[::-1]*all(x%i for i in xrange(2,x))).next(). Por supuesto, mientras que su en que, como bien podría cambiar xrange(2,x)a xrange(2,int(x**.5+1))y hacer su prueba muy rápido. Claramente, este código es equivalente al suyo, solo que más largo y más rápido.
Justin
1
Es imposible encontrar muchos buenos trucos de golf con un fragmento corto aislado de su contexto. Los mejores programas de golf a menudo hacen conexiones sorprendentes entre diferentes partes de los programas. Por ejemplo, una variable descartada aparentemente inútil puede resultar clave, o dos bucles no relacionados combinados en uno.
feersum
@feersum: publiqué todo esto (en un EDIT) hace bastante tiempo. Fue antes de que publicaras tu comentario. ¿No lo viste?
kjo
@FryAmTheEggman: sí, la afirmación del rompecabezas es que tiene que ser una función y, lo que es peor, la función no puede ser recursiva.
kjo
1
Es posible que desee mirar aquí
Beta Decay

Respuestas:

7

Hazlo un solo bucle

Como es, tiene dos bucles: uno iterando sobre xeso podría ser primos palindrómicos, otro iterando ipara verificar si xes primo por división de prueba. Como notó, los bucles son Python toman muchos caracteres, a menudo para escribir range, pero también para escribir while _:o for x in _. Por lo tanto, una solución de Python con golf debería esforzarse por usar la menor cantidad de bucles posible.

El comentario de feersum "Los mejores programas de golf a menudo hacen conexiones sorprendentes entre diferentes partes de los programas" es muy aplicable aquí. La comprobación principal puede parecer una subrutina separada para la cual all(x%i for i in range(2,x))es la expresión clásica. Pero lo haremos de otra manera.

La idea es usar el teorema de Wilson . Para cada potencial prime k, mantenemos un producto en ejecución (k-1)!y verificamos si es un múltiplo de k. Podemos hacer un seguimiento (k-1)!mientras probamos el potencial kde ser palíndromos principales manteniendo un producto en funcionamiento P.

En realidad, usaremos la versión más fuerte del Teorema de Wilson que (k-1)! % kes igual a 0 para los compuestos ky solo para los números compuestos, excepto los k=4dados 2, por lo que (k-1)!**2 % kes 0exactamente igual para los números compuestos. Actualizaremos Pa igual a k!**2través de la actualización P*=k*k.

(Consulte esta respuesta para este método utilizado para encontrar primos en Python).

Poniendolo todo junto:

def p(n):
 k=P=1
 while(`k`!=`k`[::-1])+(k<=n)+(P%k==0):P*=k*k;k+=1
 return k

Esto aún no está totalmente desarrollado: la condición en particular se escribe de manera ineficiente. Podemos comprimir la condición para verificar que ksea ​​un palíndromo mientras que al mismo tiempo hacemos cumplir las otras condiciones a través de una desigualdad encadenada.

def p(n):
 k=P=1
 while`k`*(P%k>0>n-k)!=`k`[::-1]:P*=k*k;k+=1
 return k
xnor
fuente
1
Una solución deslumbrante. Estoy agradecido de verlo, a pesar de que va más allá de mi alcance ... Hasta ahora, mis mejoras en el golf habían sido una cuestión de recoger trucos como usar backticks en lugar de str, pero estos trucos solo tienen uno. mucho ... las grandes mejoras provienen de mejores algoritmos, como siempre.
kjo
1
Siguiendo la solución de FryAmTheEggman, uno puede reescribir la condición como `k`*(k>n)*(P%k>0)!=`k`[::-1], lo que
elimina
1
@kjo Sí, y aún más similar si encadena las desigualdades en una sola. También deberías echar un vistazo a Python Golf Practice y ver trucos como este
xnor
eso está muy bien hecho! ¡gracias a todos! este ha sido un hilo muy revelador para mí ... las longitudes de solución más cortas que conozco, para Python 2.7 y 3.3 son, respectivamente, 28 y 32 caracteres más cortos que la versión que publiqué originalmente (mi mejor esfuerzo absoluto), y cuando me enteré de esto, estaba incrédulo; Pensé que tenía que haber algún juego sucio en alguna parte (por ejemplo, mediante ingeniería inversa del programa automatizado de prueba de solución). pero después de ver cómo los expertos aquí rápidamente recortaron hasta 16 caracteres de mi mejor esfuerzo, ahora estoy más dispuesto a creer esos números.
kjo
@kjo Me alegro de que estés viendo la magia, la magia del golf. ¿Estás diciendo que hay una solución de 55 char? Si es así, estoy intrigado. ¿Es este un problema de golf de anarquía? ¿Podría por favor vincularme a la declaración del problema? Puede haber atajos posibles de lagunas en los casos de prueba, en particular la excepción con el número 4.
xnor
3

AFAIK, no realmente.

El rango se usa comúnmente en los golfs de python porque es la forma más corta de generar listas de números crecientes / decrecientes.

Dicho esto, parece ser un poco (7 bytes) más corto para evitar usar el rango y, en su lugar, llamar a un ciclo while envuelto:

def p(n):
    n+=1
    while`n`*all(n%i for i in range(2,n))!=`n`[::-1]:n+=1
    return n

Gracias a @xnor (como siempre) por mejorar la lógica de la condición while :)

FryAmTheEggman
fuente
@kjo No hay problema :) Es posible que desee agregar a la pregunta lo que me dijo acerca de que tenía que ser una función no recursiva, ya que de lo contrario esta respuesta es bastante pobre;)
FryAmTheEggman
2
Usted puede ahorrar en la condición implícita por su negación: while(`n`!=`n`[::-1])+0in(n%i for i in range(2,n)):n+=1. Parece que no puedo deshacerme del primer par de padres debido a problemas de precedencia del operador.
xnor
2
Deshacerse de los padres:while`n`*all(n%i for i in range(2,n))!=`n`[::-1]:n+=1
xnor
@FryAmTheEggman: ¡eres muy deportista de tu parte! hecho
kjo
1

Cuando se utilizan algoritmos iterativos como el método de Newton o el cálculo de fractales, en los que generalmente debe realizar iteraciones sin preocuparse realmente por el índice , puede guardar algunos caracteres iterando sobre cadenas cortas.

for i in range(4):x=f(x)
for i in'asdf':x=f(x)

A las siete iteraciones esto se iguala con range. Para más iteraciones use backtics y números grandes

for i in`9**9**5`:pass

Esto se ejecuta 56349 veces, lo que debería ser suficiente para todos los fines prácticos. Jugar con números y operadores le permite codificar una amplia gama de números de esta manera.

DenDenDo
fuente
Si bien esto es interesante, puede ver claramente que a él le importa el índice (ya que el primer candidato es el índice). Creo que esto encaja mejor como parte de esta respuesta en la página de consejos :) (También tenga en cuenta que '1'*4es más corto que 'asdf')
FryAmTheEggman