Valuta l'ennesima iperoperazione


12

Mi rendo conto che questo è un po 'matematico, ma ... ecco qui.

In matematica, la sequenza di iperoperazione è una sequenza infinita di operazioni aritmetiche (chiamate iperoperazioni) che inizia con l'operazione unaria del successore, quindi continua con le operazioni binarie di addizione, moltiplicazione ed esponenziazione, dopo di che la sequenza procede con ulteriori operazioni binarie che si estendono oltre esponenziazione, usando la giusta associatività.

Il tuo obiettivo è quello di scrivere un programma che accetta tre numeri interi x, y e n come input e genera il risultato dell'ennesimo hyperoperation su xey.

Per esempio

1 1 1 uscite 2

2 4 4 uscite 65536

3 3 4 uscite 7625597484987

  • Il programma deve essere scritto nel bit più breve del codice.
  • È possibile accettare input da STDINo da un file.
  • Funzioni di libreria non consentite.
  • Vincoli di input: n sarà ≥ 1.

http://en.wikipedia.org/wiki/Tetration ha una buona spiegazione nel caso in cui non riesci a farcela.


Che cosa è n=1? Se è x+yo x+1, 1 1 1dovrebbe tornare2
John Dvorak,

Sapevo di aver fatto un errore da qualche parte :) risolto, grazie.
Soham Chowdhury,

1
Mi ho scritto uno pseudo-codice, poi mi sono reso conto che in realtà è un codice Ruby valido (quasi :-()
John Dvorak,

1
No, solo n> = 1.
Soham Chowdhury,

Risposte:


4

Rubino, lento, 86 84 83 caratteri

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, veloce, 96 94 93 caratteri

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)

La prima versione è modo troppo lento con l'ultimo caso di test, così ho aggiunto una versione che utilizza moltiplicazione come il caso base invece di aggiunta. La prima versione richiede anni per calcolare 3 3 4; il secondo è istantaneo (nella console IRB nativa; la versione web è un po 'più lenta).

Diverse bellezze di Ruby appaiono qui:

Quasi ogni affermazione è un'espressione in rubino. Pertanto, puoi inserire punti e virgola all'interno dell'operatore ternario, a condizione che tu abbia abbastanza parentesi in giro. Coffeescript lo prese in prestito. Ha anche preso in prestito la sintassi della chiamata "senza parenti necessari" di Ruby.

Ritorni impliciti: questa è una funzionalità interessante e segue la precedente. In effetti, iniziare l'ultima riga di una funzione con returnè considerato zoppo, anche quando non si gioca a golf.

I numeri sono oggetti in rubino (anche nullun oggetto). In ruby, gli interi hanno il metodo times, che esegue il blocco passato più volte ad esso. Questo è solo uno dei molti metodi iteratori di Ruby. Qui, il uptometodo ci consente di salvare altri due caratteri rispetto a ciò timesche ci consente.

unario *è l'operatore splat qui. Trasforma un array in un elenco di argomenti. Proprio come Javascript Function#apply, ma è più breve e migliore.

unario &trasforma una procedura in un blocco. Mentre :to_iè un simbolo, si converte abbastanza bene in una procedura. Vale a dire, si trasforma in una procedura che chiama to_iil suo argomento e restituisce il risultato. Ulteriori informazioni su StackTranslate.it.

Sarebbe possibile ottenerlo ancora più velocemente usando n=3come case base, ma temo che non sia necessario. Costerebbe solo 11 caratteri, però, grazie a un'altra bellezza del rubino: l'operatore esponenziale **. Python ha questo operatore, ma non è il primo (come ha notato @ aka.nice - grazie -, Fortran aveva già questo operatore).

interprete di rubini online disponibile qui: http://repl.it/Ikj/1


Bello, ma sto ancora aspettando un output da 3 3 4:) è molto lento.
Soham Chowdhury,

@SohamChowdhury il caso base è un'aggiunta. Con un caso base di moltiplicazione, sarebbe anche molto lento (e più lungo). Raccomando invece i test con esponenziazione ;-)
John Dvorak,

Si potrebbe risparmiare tempo per uso Memoizzazione, ma che sarebbe costato alcuni byte (un bel po ')
John Dvorak

Aggiungi un'altra versione quindi :)
Soham Chowdhury,

1
l'operatore ** esisteva già in FORTRAN negli anni '50 e ALGOL avrebbe avuto 1 carattere in meno con la freccia in su
aka.nice

6

APL, 62

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

{...}⎕: Accetta l'input valutato (i numeri separati da spazio vengono valutati in una matrice numerica) e applica la funzione ad esso.

1=3⌷⍵:: Se n è uguale a 1 ...
2⌷+\⍵: restituisce la somma dei primi 2 elementi (x + y) ...
⋄0=2⌷⍵:: altrimenti se y è uguale a 0 ...
(⍵[3]⌊3)⌷⍵[1],0,1: crea un array numerico [x, 0,1] e restituisce indice min(n,3)...
⋄∇⍵[1],(∇⍵-0 1 0),3⌷⍵-1: Altrimenti restituisce ∇ (x, ∇ (x, y-1, n), n-1). (∇ è autoreferenziale)


Ho un operatore "hyper-raiser", che prende una funzione e restituisce il prossimo hyperoperation

{⍺⍺/⊃⍴/⌽⍵}

Ad esempio, +{⍺⍺/⊃⍴/⌽⍵}sarebbe la funzione di moltiplicazione e le +{⍺⍺/⊃⍴/⌽⍵}5 3uscite 15.

Ma non riesco a farla valere. Forse qualcun altro può farlo.


Ah, APL. Batte Python per semplicità ogni giorno. </sarcasm> Come eseguo questo?
Soham Chowdhury,

2

Python, 83

(Basato sulla risposta di 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())

Molto lento per grandi risultati.

Per 2, 4, 4l'output è 65536.


"molto lento" è il motivo per cui la mia soluzione di 86 caratteri è stata considerata negativa.
John Dvorak,

1
@JanDvorak: Perché pensi che sia stato considerato negativo? Soham Chowdhury ha appena detto che è lento e che è necessario aggiungere un'altra versione, non sostituire la soluzione. (Ma forse l'ho capito male.)
Reinstalla Monica il

Hai ragione; ripristinata la versione corta. Ora sono solo un personaggio più lungo di te.
John Dvorak,

@WolframH esattamente. Sempre bello avere versioni.
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())

Ingresso: 3, 3, 4
Uscita:7625597484987

Abbreviato usando un paio di idee di @ WolframH .


2

Golfscript, lento, 39 caratteri

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

(collegamento live)

Questo è l'algoritmo standard ricorsivo con un caso base di n = 1 (addizione) (cioè lento). Lo stesso che ho usato nella mia soluzione Ruby

Ecco una versione con le mie annotazioni (per lo più mantenimento dello stack). Non include un'ottimizzazione che ho aggiunto in seguito:

~{            #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

~è l'operatore eval. In caso di stringhe, tratta la stringa come un programma GolfScript. Fortunatamente, un elenco di numeri interi separato da spazi è un programma GolfScript valido che inserisce tali numeri nello stack. Rispetto a questo, la mia versione precedente della routine di input ( " "/{~}/, divisa per spazio ed eval ciascuna) è piuttosto zoppa. In caso di funzioni, chiama la funzione. Se preceduto da .(clone), chiama la funzione con se stesso come primo argomento.

Golfscript non sembra essere esattamente adatto per creare algoritmi ricorsivi. Se si desidera un algoritmo ricorsivo non ottimizzabile per la coda, è necessario creare e distruggere i frame dello stack per preservare le variabili. Nella maggior parte delle lingue, questo viene fatto automaticamente. In golfscript, devi effettivamente clonare le variabili (in realtà, le voci dello stack) e distruggere le voci dello stack che non ti servono più. Golfscript non ha il concetto di stack frame. Ho detto che GolfScript è un linguaggio basato su stack?

Il primo requisito è comprensibile. Devi specificare gli argomenti in qualche modo. È bello solo se mantengono anche le loro posizioni originali. Il secondo requisito è sfortunato, soprattutto perché il valore di ritorno è in cima allo stack e golfscript non ha la possibilità di eliminare qualsiasi elemento dello stack. Puoi ruotare la pila e scartare il nuovo elemento superiore, ma questo si accumula rapidamente. \;è ok. \;\;\;\;\;non lo è. Puoi fare \;in un ciclo ( {\;}9*; costo: 6 caratteri per scartare fino a 9 elementi o 7 caratteri per scartare fino a 99 elementi), ma possiamo fare meglio:

Golfscript ha array di prima classe. Ha anche la sintassi letterale dell'array [1 2 3 4]. Ciò che è inaspettato è che [e in ]realtà non fanno parte della sintassi. Sono semplicemente due operatori: [segna la posizione corrente sullo stack e ]raccoglie tutti gli elementi fino a quando non trova il segno di inizio dell'array o si esaurisce lo stack e scarta il segno. Puoi anche separare questi due e vedere cosa succede. Bene, una cosa abbastanza interessante:

Ho detto che golfscript non ha il concetto di stack frame? Ho mentito. Questo è un frame dello stack: [. È possibile eliminare tutto in una volta: ];. E se vogliamo mantenere il valore di ritorno? Potresti chiudere il frame dello stack alla voce della funzione (quindi abbiamo una versione leggermente distorta del pass-by-array - non un concetto interessante), oppure possiamo chiudere il frame dello stack e prendere il suo ultimo elemento invece di scartarlo completamente: ]-1=oppure possono uncons l'ultimo elemento invece, e quindi scartare il frame: ])\;. Hanno la stessa lunghezza, ma quest'ultima è leggermente più fredda, quindi la sto usando.

Quindi, invece di 6 o 7 personaggi per fare una ripulitura, possiamo fare con 5. Sento ancora che questo potrebbe essere giocato di più, ma hey, abbiamo salvato un personaggio.


"chiama la funzione con se stesso come primo argomento" - idea interessante per la ricorsione
aditsu smise perché SE è MALE

1

Smalltalk Squeak 4.x ha un sapore di molti byte!

Potrei implementare una delle forme ricorsive in Integer in 71 caratteri

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

Quindi leggere da un file o FileStream stdin mi costerà un braccio ... Squeak ovviamente non è stato progettato come linguaggio di scripting. Quindi spenderò molti byte per creare le mie utility di uso generale non correlate al problema:

Implementa questo metodo con 21 caratteri in Stream (per saltare i seaparator)

s self skipSeparators

Implementare questo metodo con 20 caratteri in Behavior (per leggere un'istanza da uno stream)

<s^self readFrom:s s

Quindi 28 caratteri in String (per creare un handle di file)

f^FileDirectory default/self

Quindi 59 caratteri in FileDirectory (per creare un readStream)

r^FileStream concreteStream readOnlyFileNamed:self fullName

Quindi 33 caratteri in BlockClosure (per valutarlo n volte)

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

Quindi 63 caratteri nell'array (valutare l'argomento con il ricevitore e gli argomenti presi dall'array)

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

quindi risolvi il problema valutando questo frammento di 31 caratteri ovunque da leggere dal file denominato x

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

Anche senza contare le utility, sono già 71 + 31 = 102 caratteri ...

Ora, poiché sono sicuro di perdere il codeGolf, ho un'implementazione più divertente in 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

Questo metodo definirà (compilerà) un messaggio binario fatto di n + se non esiste (non è compreso dal destinatario del messaggio m) e riavvierà l'esecuzione all'inizio del contesto del mittente. Ho inserito ulteriori ritorni a capo e spazi per la leggibilità.

Si noti che (m selector size-2min:1)hex lastè una forma abbreviata di (m selector size>2)asBit printString.

Se non fosse per dimostrare i superpoteri malvagi di Smalltalk, l'ultima affermazione potrebbe essere sostituita da più breve e più semplice

^m sendTo:self

Ora implementa l'utilità 28 caratteri in Carattere (per ripeterlo n volte in una stringa)

*n^String new:n withAll:self

Quindi valuta questa espressione di 43 caratteri:

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

Possiamo accelerare con altri 10 caratteri implementando in Integer:

++y^self*y

e in questo caso abbiamo anche un codice più breve perché possiamo sostituirlo ^',(m selector size-2min:1)hex lastcon^1'

Per un prezzo così alto, il codice funziona con il secondo numero intero = 0 :)


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: richiede quantità oscene di stack (e tempo) per argomenti non minuscoli.


0

AXIOM Computer Algebra System, byte 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))

test:

(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

Questo eliminerebbe un po 'di ricorsione ... Possibile scambio in xey in qualche ritorno ... ci sono altri valori di prova?


0

APL (NARS), caratteri 61, byte 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}

test:

  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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.