Considere uma expressão 2^2^...^2
com n
operadores ^
. Operador ^
significa exponenciação ("ao poder de"). Suponha que ela não tenha associação padrão, portanto a expressão precisa ser totalmente entre parênteses para se tornar inequívoca. O número de maneiras entre parênteses da expressão é dado por números catalães C_n=(2n)!/(n+1)!/n!
.
Às vezes, parênteses diferentes dão o mesmo resultado numérico, por exemplo (2^2)^(2^2)=((2^2)^2)^2
, portanto, o número de diferentes resultados numéricos possíveis para um dado n
é menor do que C_n
para todos n>1
. A sequência começa 1, 1, 2, 4, 8, ...
em oposição aos números catalães1, 2, 5, 14, 42, ...
O problema é escrever o programa (ou função) mais rápido que aceita n
como entrada e retorna o número de diferentes resultados numéricos possíveis da expressão 2^2^...^2
com os n
operadores ^
. O desempenho não deve se deteriorar significativamente à medida que n
cresce, portanto, o cálculo direto de torres de alta potência é provavelmente uma má ideia.
fonte
2^n
e, portanto, seria desnecessário acompanhar qualquer coisa, exceton
. Ou seja, apenas usar as regras da exponenciação parece sábio. No entanto, certamente existe uma maneira mais inteligente e completamente algébrica de fazer isso.n
é muito grande para calcular. Ainda assim, bem observado. Talvez uma representação recursiva na forma "1 ou 2 ^ (...) ou (...) + (...)"; mas você ainda tem o problema de como normalizar essa representação de um número (ou comparar duas representações para igualdade de valor).n
dois eC_n=(2n)!/(n+1)!/n!
deve ser o número de parênteses, então para n = 3 deve ser 5, correto? Eu vejo(2^2)^2
e2^(2^2)
, mas quais são as outras três combinações? Eu acho que C_n fornece o número de parênteses para n + 1 dois.Respostas:
Python 2.7
Essa abordagem aproveita as seguintes considerações:
Qualquer número inteiro pode ser representado como uma soma de potências de dois. Os expoentes nas potências de dois também podem ser representados como potências de dois. Por exemplo:
Essas expressões com as quais terminamos podem ser representadas como conjuntos de conjuntos (em Python, usei o built-in
frozenset
):0
torna-se o conjunto vazio{}
.2^a
torna-se o conjunto que contém o conjunto que representaa
. Por exemplo:1 = 2^0 -> {{}}
e2 = 2^(2^0) -> {{{}}}
.a+b
torna-se a concatenação dos conjuntos que representama
eb
. Por exemplo,3 = 2^(2^0) + 2^0 -> {{{}},{}}
Acontece que expressões do formulário
2^2^...^2
podem ser facilmente transformadas em sua representação de conjunto exclusiva, mesmo quando o valor numérico é muito grande para ser armazenado como um número inteiro.Pois
n=20
, isso é executado em 8.7s no CPython 2.7.5 na minha máquina (um pouco mais lento no Python 3 e muito mais lento no PyPy):(O conceito do decorador de memoização é copiado de http://code.activestate.com/recipes/578231-probably-the-fastest-memoization-decorator-in-the-/ .)
Resultado:
Horários para diferentes
n
:Qualquer um
n
acima de 21 resulta em um erro de memória na minha máquina.Eu estaria interessado se alguém puder tornar isso mais rápido traduzindo-o para um idioma diferente.
Editar: otimizou a
get_results
função. Além disso, o uso do Python 2.7.5 em vez do 2.7.2 fez com que fosse executado um pouco mais rápido.fonte
(a^b)^c = (a^c)^b
, e ainda é muito mais lenta que esta implementação em Python.C #
Esta é uma tradução do código Python do flornquake para C # usando uma rotina de adição de nível inferior que fornece uma aceleração moderada em relação à tradução direta. Não é a versão mais otimizada que eu tenho, mas é um pouco mais longa porque precisa armazenar a estrutura da árvore e os valores.
fonte