Avaliar a enésima hiperoperação

12

Eu percebo que isso é um pouco matemático, mas - aqui vai.

Em matemática, a sequência de hiperoperação é uma sequência infinita de operações aritméticas (chamadas hiperoperações) que começa com a operação unária do sucessor e continua com as operações binárias de adição, multiplicação e exponenciação, após as quais a sequência prossegue com outras operações binárias que se estendem além exponenciação, usando a associatividade correta.

Seu objetivo é escrever um programa que use três números inteiros x, y e n como entrada e produza o resultado da enésima hiperoperação em x e y.

Por exemplo

1 1 1 saídas 2

2 4 4 saídas 65536

3 3 4 saídas 7625597484987

  • O programa deve ser escrito no menor pedaço de código.
  • Você pode receber informações de STDINou de um arquivo.
  • Funções de biblioteca não permitidas.
  • Restrições de entrada: n será ≥ 1.

http://en.wikipedia.org/wiki/Tetration tem uma boa explicação, caso você não consiga entender isso.

Soham Chowdhury
fonte
O que é n=1? Se é x+you x+1, 1 1 1deve retornar2
John Dvorak
Eu sabia que tinha cometido um erro em algum lugar :) fixo, thx.
Soham Chowdhury
1
Eu já me escreveu um pseudo-código, então eu percebi que na verdade é um código Ruby válido (quase :-()
John Dvorak
1
Não, n> = 1 apenas.
Soham Chowdhury

Respostas:

4

Ruby, lento, 86 84 83 caracteres

def f x,y,n
n>1?(c=x;2.upto(y){c=f(x,c,n-1)};c):x+y
end
p f *gets.split.map(&:to_i)

Ruby, rápido, 96 94 93 caracteres

def f x,y,n
n>1?(n>2?(c=x;2.upto(y){c=f(x,c,n-1)};c):x*y):x+y
end
p f *gets.split.map(&:to_i)

A primeira versão é maneira muito lento com o último caso de teste, então eu adicionei uma versão que usa multiplicação como o caso base, em vez de adição. A primeira versão leva idades para ser calculada 3 3 4; o segundo é instantâneo (no console nativo do IRB; a versão da Web é um pouco mais lenta).

Várias belezas de Ruby aparecem aqui:

Quase toda declaração é uma expressão em rubi. Assim, você pode colocar ponto e vírgula dentro do operador ternário, desde que haja parênteses suficientes. Coffeescript pediu emprestado esse. Também emprestou a sintaxe de chamada "sem parênteses necessária" de Ruby.

Retornos implícitos: esse é um recurso interessante e segue o anterior. De fato, iniciar a última linha de uma função com returné considerado manco, mesmo quando não estiver jogando golfe.

Números são objetos em rubi (par nullé um objeto). Em ruby, números inteiros têm o método times, que executa o bloco passado a ele várias vezes. Este é apenas um dos muitos métodos de iterador do Ruby. Aqui, o uptométodo permite salvar mais dois caracteres sobre o que timesnos permite.

unário *é o operador splat aqui. Transforma uma matriz em uma lista de argumentos. Assim como o Javascript Function#apply, mas é mais curto e melhor.

unário &transforma um procedimento em um bloco. Embora :to_iseja um símbolo, ele se converte em um procedimento muito bem. Ou seja, ele se transforma em um procedimento que invoca to_iseu argumento e retorna o resultado. Mais informações sobre estouro de pilha.

Seria possível obtê-lo ainda mais rápido usando n=3como caso base, mas receio que não seja necessário. Porém, custaria apenas 11 caracteres, graças a outra beleza do rubi: o operador de exponenciação **. O Python possui esse operador, mas não é o primeiro (como o @ aka.nice observou - obrigado -, o Fortran já tinha esse operador).

intérprete de rubi on-line disponível aqui: http://repl.it/Ikj/1

John Dvorak
fonte
Bom, mas ainda estou esperando uma saída de 3 3 4:) é muito lento.
Soham Chowdhury
@SohamChowdhury o caso base é uma adição. Com um caso básico de multiplicação, também seria muito lento (e mais longo). Eu recomendo testar com exponenciação vez ;-)
John Dvorak
Ele pode economizar tempo ao uso memoization, mas que custaria alguns bytes (muito poucos)
John Dvorak
Adicionar outra versão então :)
Soham Chowdhury
1
operador ** já existia em FORTRAN nos anos 50 e o ALGOL teria 1 caractere a menos com seta para cima
aka.nice
6

APL, 62

{1=3⌷⍵:2⌷+\⍵⋄0=2⌷⍵:(⍵[3]⌊3)⌷⍵[1],0,1⋄∇⍵[1],(∇⍵-0 1 0),3⌷⍵-1}⎕

{...}⎕: Recebe a entrada avaliada (os números separados por espaço são avaliados em uma matriz numérica) e aplica a função a ela.

1=3⌷⍵:: Se n for igual a 1 ...
2⌷+\⍵: retornar a soma dos 2 primeiros elementos (x + y) ...
⋄0=2⌷⍵:: senão se y for igual a 0 ...
(⍵[3]⌊3)⌷⍵[1],0,1: criar matriz numérica [x, 0,1] e retornar índice min(n,3)...
⋄∇⍵[1],(∇⍵-0 1 0),3⌷⍵-1: Outro retorno ∇ (x, ∇ (x, y-1, n), n-1). (∇ é auto-referência)


Eu tenho um operador "hyper-raiser", que assume uma função e retorna a próxima hiperoperação

{⍺⍺/⊃⍴/⌽⍵}

Por exemplo, +{⍺⍺/⊃⍴/⌽⍵}seria a função de multiplicação e +{⍺⍺/⊃⍴/⌽⍵}5 3gera 15.

Mas não é possível fazê-lo recuar. Talvez alguém possa fazer isso.

TwiNight
fonte
Ah, APL. Supera o Python pela simplicidade a qualquer dia. </sarcasm> Como executo isso?
Soham Chowdhury
2

Python, 83

(Com base na resposta do flornquake )

def h(x,y,n):r=n>2;exec"r=h(x,r,n-1);"*y*(n>1);return(x+y,r)[n>1]
print h(*input())

Muito lento para grandes resultados.

Para 2, 4, 4a saída é 65536.

Restabelecer Monica
fonte
"muito lento" é o motivo pelo qual minha solução de 86 caracteres foi considerada ruim.
precisa
1
@JanDvorak: Por que você acha que foi considerado ruim? Soham Chowdhury disse apenas que é lento e que você deve adicionar outra versão, não que substitua sua solução. (Mas talvez eu não entendi isso.)
Reintegrar Monica
Você está certo; restaurou a versão curta. Agora eu sou apenas um char mais do que você.
precisa
@WolframH exatamente. Sempre bom ter versões.
218132 Soham Chowdhury
2

Python, 96 92

def h(x,y,n):r=1;exec(n>2)*y*"r=h(x,r,n-1);";return(r,(x+y,x*y)[n>1])[n<3]
print h(*input())

Entrada: 3, 3, 4
Saída:7625597484987

Encurtado usando algumas idéias de @ WolframH .

flornquake
fonte
2

Golfscript, lento, 39 caracteres

 ~{\(.{3${[4$\2$4$.~}4$(*}{;;+}if])\;}.~

(link ao vivo)

Este é o algoritmo recursivo padrão com um caso base de n = 1 (adição) (isto é, lento). O mesmo que usei na minha solução Ruby

Aqui está uma versão com minhas anotações (principalmente manutenção de pilha). Não inclui uma otimização que adicionei mais tarde:

~{            #read the input and do (x y n f)
 \(.{         #(x y f n-1); if(n-1)
  3${         #(x y f n-1 c)
   4$\2$4$.~  #(x y f n-1 x c n-1 f); call
  }3$(*       #y-1 times
  {\;}4*
 }{           #else
  ;;+         #return (x+y)
 }if
}.~           #once

~é o operador eval. No caso de strings, ele trata a string como um programa GolfScript. Felizmente, uma lista de números inteiros separados por espaço é um programa GolfScript válido que coloca esses números inteiros na pilha. Comparado a isso, minha versão anterior da rotina de entrada ( " "/{~}/dividida por espaço e avaliada cada) é bastante ruim. No caso de funções, chama a função. Quando precedido por .(clone), ele chama a função consigo como o primeiro argumento.

O Golfscript não parece ser exatamente adequado para criar algoritmos recursivos. Se você deseja um algoritmo recursivo que não seja otimizável por chamada de cauda, ​​precisará criar e destruir quadros de pilha para preservar suas variáveis. Na maioria dos idiomas, isso é feito automaticamente. No golfscript, você precisa realmente clonar as variáveis ​​(na verdade, entradas da pilha) e destruir as entradas da pilha que não são mais necessárias. Golfscript não tem conceito de quadros de pilha. Eu disse que o GolfScript é uma linguagem baseada em pilha?

O primeiro requisito é compreensível. Você precisa especificar os argumentos de alguma forma. Só é bom se eles mantiverem suas posições originais também. O segundo requisito é lamentável, especialmente porque o valor de retorno está no topo da pilha e o golfscript não tem a capacidade de excluir apenas qualquer elemento da pilha. Você pode girar a pilha e descartar o novo elemento superior, mas isso se acumula rapidamente. \;está bem. \;\;\;\;\;não é. Você pode fazer isso \;em um loop ( {\;}9*; custo: 6 caracteres para descartar até 9 elementos ou 7 caracteres para descartar até 99 elementos), mas podemos fazer melhor:

O Golfscript possui matrizes de primeira classe. Ele também possui a sintaxe literal da matriz [1 2 3 4]. O que é inesperado é isso [e, ]na verdade, não faz parte da sintaxe. São apenas dois operadores: [marca a posição atual na pilha e ]coleta todos os elementos até encontrar a marca de início da matriz ou ficar sem pilha e descartá-la. Você pode até separar esses dois e ver o que acontece. Bem, uma coisa interessante:

Eu disse golfscript não tem conceito de frames de pilha? Eu menti. Este é um quadro de pilha: [. Você pode descartar tudo de uma vez: ];. Mas e se quisermos manter o valor de retorno? Você pode fechar o quadro da pilha na entrada da função (então temos uma versão ligeiramente confusa do passo a matriz - não é um conceito interessante), ou podemos fechar o quadro da pilha e pegar seu último elemento em vez de descartá-lo completamente: ]-1=ou nós pode uncons o último elemento em vez disso, e em seguida, descartar o quadro: ])\;. Eles têm o mesmo comprimento, mas o último é um pouco mais frio, então eu estou usando.

Então, em vez de 6 ou 7 caracteres para fazer uma limpeza, podemos fazer o 5. Ainda sinto que isso poderia ser mais praticado, mas ei, salvamos um personagem.

John Dvorak
fonte
"chama a função consigo mesmo como o primeiro argumento" - idéia interessante para recursão
aditsu encerra porque SE é MAU
1

Squeak Smalltalk 4.x sabor muitos bytes!

Eu poderia implementar uma das formas recursivas em Inteiro em 71 caracteres

f:y n:n n=1or:[^(2to:y)inject:self into:[:x :i|self f:x n:n-1]].^self+y

A leitura de um arquivo ou do FileStream stdin vai me custar um braço ... O Squeak obviamente não foi projetado como uma linguagem de script. Portanto, gastarei muitos bytes para criar meus próprios utilitários de uso geral não relacionados ao problema:

Implemente esse método de 21 caracteres no Stream (para pular os seaparators)

s self skipSeparators

Implemente esse método de 20 caracteres no comportamento (para ler uma instância de um fluxo)

<s^self readFrom:s s

Em seguida, 28 caracteres na String (para criar um identificador de arquivo)

f^FileDirectory default/self

Em seguida, 59 caracteres no FileDirectory (para criar um readStream)

r^FileStream concreteStream readOnlyFileNamed:self fullName

Em seguida, 33 caracteres no BlockClosure (para avaliar n vezes)

*n^(1to:n)collect:[:i|self value]

Em seguida, 63 caracteres na matriz (avalie o argumento com o receptor e os argumentos extraídos da matriz)

`s^self first perform:s asSymbol withArguments:self allButFirst

em seguida, resolva o problema avaliando esse snippet de 31 caracteres em qualquer lugar para ler do arquivo chamado x

|s|s:='x'f r.[0class<s]*3`#f:n:

Mesmo sem contar os utilitários, já são 71 + 31 = 102 caracteres ...

Agora, como tenho certeza de perder o codeGolf, tenho uma implementação mais divertida no Integer:

doesNotUnderstand:m
    (m selector allSatisfy:[:c|c=$+])or:[^super doesNotUnderstand:m].
    self class compile:
        m selector,'y y=0or:[^(2to:y)inject:self into:[:x :i|self'
        ,m selector allButLast,'x]].^'
        ,(Character digitValue:()asBit)
        ,(m selector size-2min:1)hex last.
    thisContext sender restart

Este método definirá (compilar) uma mensagem binária feita de n + se ela não existir (não é entendida pelo destinatário da mensagem m) e reiniciará a execução no início do contexto do remetente. Inseri um retorno de carro adicional e espaços para facilitar a leitura.

Observe que (m selector size-2min:1)hex lasté uma forma abreviada de (m selector size>2)asBit printString.

Se não fosse para demonstrar superpoderes do Smalltalk, a última declaração poderia ser substituída por mais curta e simples

^m sendTo:self

Agora implemente o utilitário 28 chars em Character (para repeti-lo n vezes em uma String)

*n^String new:n withAll:self

Em seguida, avalie esta expressão de 43 caracteres:

|i s|i:=0class.s:='x'f r.[i<s]*2`($+*(i<s))

Podemos acelerar com mais 10 caracteres implementando em Inteiro:

++y^self*y

e, neste caso, também temos um código mais curto, porque podemos substituir ^',(m selector size-2min:1)hex lastpor^1'

Por um preço tão alto, o código funciona com o segundo número inteiro = 0 :)

aka.nice
fonte
0

Groovy - 77

h={x,y,n->n<2?x+y:y<2?x:h(x,h(x,y-1,n),n-1)}
print h(args.collect{it as int})

Nota: requer quantidades obscenas de pilha (e tempo) para argumentos não minúsculos.

aditsu sair porque SE é MAU
fonte
0

Sistema de álgebra computacional do AXIOM, bytes 69

p(x,y,n)==(n<=1=>y+x^n;n=2=>y*x;n=3=>x^y;y<=0=>1;p(x,p(x,y-1,n),n-1))

teste:

(2) -> p(1,1,1)
   (2)  2
                                                 Type: Expression Integer
                                   Time: 0.05 (IN) + 0.03 (OT) = 0.08 sec
(3) -> p(2,4,4)
   (3)  65536
                                                 Type: Expression Integer
                                                              Time: 0 sec
(4) -> p(3,3,4)
   (4)  7625597484987
                                                 Type: Expression Integer
                                                              Time: 0 sec

Isso eliminaria alguma recursão ... É possível trocar x e y em algum retorno ... existem outros valores de teste?

RosLuP
fonte
0

APL (NARS), caracteres 61, bytes 122

{(x y n)←⍵⋄n≤1:y+x*n⋄n=2:x×y⋄n=3:x*y⋄y≤0:1⋄∇x,(∇x(y-1)n),n-1}

teste:

  h←{(x y n)←⍵⋄n≤1:y+x*n⋄n=2:x×y⋄n=3:x*y⋄y≤0:1⋄∇x,(∇x(y-1)n),n-1}
  h 1 1 1
2
  h 2 4 4
65536
  h 3 4 4
∞
  h 3 3 4
7625597484987
RosLuP
fonte