Considere una expresión 2^2^...^2
con n
operadores ^
. Operador ^
significa exponenciación ("al poder de"). Suponga que no tiene una asiatividad predeterminada, por lo que la expresión debe estar completamente entre paréntesis para dejar de ser ambigua. La cantidad de formas de paréntesis de la expresión están dadas por números catalanes C_n=(2n)!/(n+1)!/n!
.
A veces, diferentes paréntesis dan el mismo resultado numérico, por ejemplo (2^2)^(2^2)=((2^2)^2)^2
, por lo que el número de diferentes resultados numéricos posibles para un determinado n
es menor que C_n
para todos n>1
. La secuencia comienza 1, 1, 2, 4, 8, ...
en oposición a los números catalanes.1, 2, 5, 14, 42, ...
El problema es escribir el programa (o función) más rápido que acepta n
como entrada y devuelve el número de diferentes resultados numéricos posibles de la expresión 2^2^...^2
con n
operadores ^
. El rendimiento no debería deteriorarse significativamente a medida que n
crece, por lo que el cálculo directo de torres de alta potencia es probablemente una mala idea.
fuente
2^n
, y por lo tanto sería innecesario hacer un seguimiento de cualquier cosa, excepton
. Es decir, solo usar las reglas de exponenciación parece sabio. Sin embargo, seguramente hay una forma más inteligente y completamente algebraica de hacer esto.n
que todavía es demasiado grande para calcular. Aún así, bien notado. Tal vez una representación recursiva en la forma "1 o 2 ^ (...) o (...) + (...)"; pero aún tiene el problema de cómo normalizar dicha representación de un número (o comparar dos representaciones para la igualdad de valores).n
dos yC_n=(2n)!/(n+1)!/n!
debe ser el número de paréntesis, entonces para n = 3 debería ser 5, ¿correcto? Ya veo(2^2)^2
y2^(2^2)
, pero ¿cuáles son las otras tres combinaciones? Creo que C_n te da el número de paréntesis para n + 1 dos.Respuestas:
Python 2.7
Este enfoque aprovecha las siguientes consideraciones:
Cualquier número entero puede representarse como una suma de potencias de dos. Los exponentes en las potencias de dos también se pueden representar como potencias de dos. Por ejemplo:
Estas expresiones con las que terminamos se pueden representar como conjuntos de conjuntos (en Python, utilicé el incorporado
frozenset
):0
se convierte en el conjunto vacío{}
.2^a
se convierte en el conjunto que contiene el conjunto que representaa
. Por ejemplo:1 = 2^0 -> {{}}
y2 = 2^(2^0) -> {{{}}}
.a+b
se convierte en la concatenación de los conjuntos que representana
yb
. P.ej,3 = 2^(2^0) + 2^0 -> {{{}},{}}
Resulta que las expresiones de la forma
2^2^...^2
pueden transformarse fácilmente en su representación de conjunto única, incluso cuando el valor numérico es demasiado grande para ser almacenado como un entero.Para
n=20
, esto se ejecuta en 8.7s en CPython 2.7.5 en mi máquina (un poco más lento en Python 3 y mucho más lento en PyPy):(El concepto del decorador de memorias se copia de http://code.activestate.com/recipes/578231-probably-the-fastest-memoization-decorator-in-the-/ ).
Salida:
Tiempos para diferentes
n
:Cualquiera de los
n
anteriores 21 produce un error de memoria en mi máquina.Me interesaría si alguien puede hacer esto más rápido traduciéndolo a un idioma diferente.
Editar: Optimizado la
get_results
función. Además, usar Python 2.7.5 en lugar de 2.7.2 lo hizo correr un poco más rápido.fuente
(a^b)^c = (a^c)^b
, y aún es mucho más lenta que esta implementación de Python.C#
Esta es una traducción del código Python de flornquake a C # usando una rutina de adición de nivel inferior que proporciona una aceleración moderada sobre una traducción directa. No es la versión más optimizada que tengo, pero es bastante más larga porque tiene que almacenar la estructura de árbol y los valores.
fuente