Numeri fissili


47

Ho trovato questa sequenza mentre lavoravo su Evolution of OEIS , ma non sono mai riuscito a pubblicarla come risposta. Dopo aver scritto un'implementazione di riferimento in Mathematica, ho pensato che questo fosse un esercizio divertente da fare come una sfida separata, quindi eccoci qui.

Costruiamo un reattore a fissione numerica! Considera un numero intero positivo N. Ad esempio, vedremo 24. Per fissare questo numero, dobbiamo trovare il maggior numero di numeri interi positivi consecutivi che sommano N. In questo caso, quello è 7 + 8 + 9 = 24. Quindi abbiamo diviso 24in tre nuovi numeri. Ma questo non sarebbe molto di un reattore a fissione senza reazioni a catena. Quindi ripetiamo in modo ricorsivo il processo per questi componenti:

       24
       /|\
      / | \
     /  |  \
    7   8   9
   / \     /|\
  3   4   / | \
 / \     /  |  \
1   2   2   3   4
           / \
          1   2

Si noti che interrompiamo il processo ogni volta che il numero non può essere scomposto in numeri interi consecutivi più piccoli. Si noti inoltre che avremmo potuto scrivere 9come 4 + 5, ma 2 + 3 + 4ha più componenti. Il numero di Fissione di Nè ora definito come il numero di numeri interi ottenuti in questo processo, incluso Nse stesso. L'albero sopra ha 13 nodi, quindi F(24) = 13.

Questa sequenza è la voce OEIS A256504 .

I primi 40 termini, a partire da N = 1, sono

1, 1, 3, 1, 5, 6, 5, 1, 6, 7, 12, 10, 12, 11, 12, 1, 8, 16, 14, 17, 18, 18,
23, 13, 21, 18, 22, 23, 24, 19, 14, 1, 22, 20, 23, 24, 31, 27, 25, 26

I primi 1000 termini possono essere trovati in questo pastebin .

La sfida

Dato un numero intero positivo N, determinare il suo numero di Fission F(N). (Quindi non è necessario coprire il leader 0elencato su OEIS.)

È possibile scrivere un programma o una funzione, prendendo l'input tramite STDIN (o l'alternativa più vicina), l'argomento della riga di comando o l'argomento della funzione e producendo il risultato tramite STDOUT (o l'alternativa più vicina), il valore di ritorno della funzione o il parametro della funzione (out).

Questo è il golf del codice, quindi vince la risposta più breve (in byte).

Domanda bonus: puoi trovare proprietà interessanti di questa sequenza?


Ho notato che l'OEIS sembra avere un errore a n = 34: a partire da n = 32, elenca (attualmente) 1, 22, 22, 23, 24, 31, anziché 1, 22, 20, 23, 24, 31.
mathmandan,

1
@mathmandan Buona cattura, probabilmente proporrò una correzione (insieme al primo diagramma).
Martin Ender,


@mathmandan FYI, ho corretto la sequenza e l'esempio ora, e ho anche aggiunto la mia implementazione di riferimento e i primi termini da 10k.
Martin Ender,

Sembra buono! Grazie per il tuo lavoro!
Mathmandan,

Risposte:


16

Pyth, 23 22 21 byte

Lh&lJfqbsT.:tUb)syMeJ

Questo definisce una funzione ricorsiva y. Provalo online: dimostrazione

Spiegazione:

L                      define a function y(b): return ...
            tUb          the list [1, 2, ..., b-1]
          .:   )         generate all consecutive sub-sequences
     f                   filter for sub-sequences T, which satisfy:
      qbsT                   b == sum(T)
    J                    and store them in J

                         return 
   lJ                        len(J)
  &                        and (if len(J) == 0 then 0 else ...)
                    eJ       last element of J (=longest sub-sequence) 
                  yM         recursive calls for all these numbers
                 s           sum
 h                         incremented by one (counting the current node)

52

Fissione , 1328 989 887 797 byte

Questa risposta è un po 'irragionevolmente lunga (vorrei che avessimo regioni pieghevoli ) ... per favore, non dimenticare di scorrere oltre e mostrare alle altre risposte un po' d'amore!

Lavorare su questo codice è stato ciò che ha ispirato questa sfida. Volevo aggiungere una risposta in Fissione a EOEIS, che mi ha portato a questa sequenza. Tuttavia, in realtà l'apprendimento di Fission e l'attuazione di questo hanno richiesto alcune settimane lavorando su di esso. Nel frattempo, la sequenza era davvero cresciuta su di me, quindi ho deciso di postare una sfida separata (in più, comunque, non sarebbe stato particolarmente lontano su EOEIS).

Quindi vi presento la mostruosità:

 R'0@+\
/  Y@</ /[@ Y]_L
[? % \  / \ J
   \$@  [Z/;[{+++++++++L
UR+++++++++>/;
9\   ;    7A9
SQS  {+L  /$     \/\/\/\/\/   5/ @  [~ &@[S\/ \  D /8/
~4X /A@[  %5                   /; &    K  } [S//~KSA /
  3    \  A$@S  S\/  \/\/\/   \/>\ /S]@A  /  \ { +X
W7           X  X    /> \      +\ A\ /   \ /6~@/ \/
        /   ~A\;     +;\      /@
    ZX [K    / {/  / @  @ }  \ X @
       \AS   </      \V /    }SZS S/
         X   ;;@\   /;X  /> \ ; X X
 ;       \@+  >/ }$S SZS\+;    //\V
           / \\  /\; X X @  @  \~K{
           \0X /     /~/V\V /   0W//
    \        Z      [K \  //\
W       /MJ $$\\ /\7\A  /;7/\/ /
       4}K~@\ &]    @\  3/\
 /     \{   }$A/1 2  }Y~K <\
[{/\  ;@\@  /   \@<+@^   1;}++@S68
@\ <\    2        ;   \    /
$  ;}++ +++++++L
%@A{/
M  \@+>/
~     @
SNR'0YK
  \  A!/

Si aspetta che non ci sia newline finale nell'input, quindi potresti voler chiamarlo come echo -n 120 | ./Fission oeis256504.fis.

Il layout potrebbe probabilmente essere ancora più efficiente, quindi penso che ci sia ancora molto spazio per miglioramenti qui (ad esempio, questo contiene 911 581 461 374 spazi).

Prima di arrivare alla spiegazione, una nota sul test di questo: l' interprete ufficiale non funziona del tutto così com'è. a) Mirror.cppnon compilare su molti sistemi. Se riscontri questo problema, commenta semplicemente la riga offensiva: il componente interessato (un mirror casuale) non viene utilizzato in questo codice. b) Ci sono un paio di bug che possono portare a comportamenti indefiniti (e probabilmente lo faranno per un programma così complesso). È possibile applicare questa patch per risolverli. Una volta fatto ciò, dovresti essere in grado di compilare l'interprete con

g++ -g --std=c++11 *.cpp -o Fission

Curiosità: questo programma utilizza quasi tutti i componenti che Fission ha da offrire, ad eccezione di #(mirror casuale), :(half mirror) -o |(plain mirror) e "(modalità di stampa).

Cosa diavolo?

Attenzione: questo sarà piuttosto lungo ... Suppongo che tu sia sinceramente interessato a come funziona Fission e come si potrebbe programmare in esso. Perché se non lo sei, non sono sicuro di come potrei riassumere questo. (Il prossimo paragrafo fornisce una descrizione generale della lingua.)

La fissione è un linguaggio di programmazione bidimensionale, in cui sia il flusso di dati che di controllo sono rappresentati da atomi che si muovono attraverso una griglia. Se hai già visto o usato Marbelous in precedenza, il concetto dovrebbe essere vagamente familiare. Ogni atomo ha due proprietà intere: una massa non negativa e un'energia arbitraria. Se la massa diventa negativa, l'atomo viene rimosso dalla griglia. Nella maggior parte dei casi puoi considerare la massa come il "valore" dell'atomo e l'energia come una sorta di meta-proprietà che viene utilizzata da diversi componenti per determinare il flusso degli atomi (cioè la maggior parte dei tipi di interruttori dipende dal segno di l'energia). Indicherò gli atomi con (m,E), quando necessario. All'inizio del programma, la griglia inizia con un gruppo di(1,0)atomi da qualsiasi posizione sui quattro componenti UDLR(dove la lettera indica la direzione in cui si muove inizialmente l'atomo). La scheda viene quindi popolata con un intero gruppo di componenti che cambiano la massa e l'energia degli atomi, cambiano direzione o fanno altre cose più sofisticate. Per un elenco completo vedere la pagina di esolangs , ma introdurrò la maggior parte di essi in questa spiegazione. Un altro punto importante (che il programma utilizza più volte) è che la griglia è toroidale: un atomo che colpisce uno dei lati riappare dalla parte opposta, muovendosi nella stessa direzione.

Ho scritto il programma in diverse parti più piccole e le ho assemblate alla fine, quindi è così che passerò attraverso la spiegazione.

atoi

Questo componente può sembrare piuttosto poco interessante, ma è carino e semplice e mi permette di introdurre molti dei concetti importanti del flusso aritmetico e di controllo di Fission. Pertanto, analizzerò questa parte con un dettaglio abbastanza meticoloso, in modo da poter ridurre le altre parti all'introduzione di nuove meccaniche di Fissione e sottolineando i componenti di livello superiore di cui dovresti essere in grado di seguire te stesso il flusso di controllo dettagliato.

La fissione può leggere solo valori di byte da singoli caratteri, non interi numeri. Mentre questa è una pratica accettabile da queste parti, ho pensato che mentre ero lì, avrei potuto farlo bene e analizzare numeri interi reali su STDIN. Ecco il atoicodice:

     ;
 R'0@+\
/  Y@</ /[@ Y]_L
[? % \  / \ J 
   \$@  [Z/;[{+++++++++L
UR+++++++++>/;
           O

Due dei componenti più importanti in Fissione sono i reattori a fissione e fusione. I reattori a fissione sono uno dei V^<>(utilizza il codice sopra <e >). Un reattore a fissione può immagazzinare un atomo (inviandolo nel cuneo del personaggio), di default (2,0). Se un atomo colpisce l'apice del personaggio, due nuovi atomi verranno inviati ai lati. La loro massa viene determinata dividendo la massa in arrivo per la massa immagazzinata (cioè dimezzando di default) - l'atomo di sinistra ottiene questo valore e l'atomo di destra ottiene il resto della massa (cioè la massa viene conservata nella fissione) . Entrambi gli atomi in uscita avranno meno energia in entratal'energia immagazzinata. Ciò significa che possiamo usare i reattori a fissione per l'aritmetica, sia per la sottrazione che per la divisione. Se un reattore a fissione viene colpito dal sito, l'atomo viene semplicemente riflesso in diagonale e quindi si sposta nella direzione dell'apice del personaggio.

I reattori a fusione sono uno dei YA{}(utilizza il codice sopra Ye {). La loro funzione è simile: possono immagazzinare un atomo (impostazione predefinita (1,0)) e quando colpiti dall'apice due nuovi atomi verranno inviati ai lati. Tuttavia, in questo caso i due atomi saranno identici, mantenendo sempre l'energia in arrivo e moltiplicando la massa in arrivo per la massa immagazzinata. Cioè, per impostazione predefinita, il reattore a fusione duplica semplicemente qualsiasi atomo che colpisce il suo apice. Quando viene colpito dai lati, reattori a fusione sono un po 'più complicato: l'atomo è anchememorizzato (indipendentemente dall'altra memoria) fino a quando un atomo colpisce il lato opposto. Quando ciò accade, un nuovo atomo viene rilasciato nella direzione dell'apice la cui massa ed energia sono la somma dei due vecchi atomi. Se un nuovo atomo colpisce lo stesso lato prima che un atomo corrispondente raggiunga il lato opposto, il vecchio atomo verrà semplicemente sovrascritto. I reattori a fusione possono essere utilizzati per implementare l'addizione e la moltiplicazione.

Un altro componente semplice voglio ottenere fuori strada è [e ]che semplicemente imposta la direzione della atomo destra e sinistra, rispettivamente (indipendentemente dalla direzione in entrata). Gli equivalenti verticali sono M(giù) e W(su) ma non sono usati per il atoicodice. UDLRagiscono anche come WM][dopo aver rilasciato i loro atomi iniziali.

Comunque, diamo un'occhiata al codice lassù. Il programma inizia con 5 atomi:

  • Il Re Lin basso ottengono semplicemente il loro incremento di massa (con +) per diventare (10,0)e quindi immagazzinare rispettivamente in una fissione e in un reattore a fusione. Useremo questi reattori per analizzare l'ingresso in base 10.
  • Il Lnell'angolo superiore destro ottiene la sua massa decrementato (con _) per diventare (0,0)e viene memorizzato nel lato di un reattore a fusione Y. Questo per tenere traccia del numero che stiamo leggendo: aumenteremo e moltiplicheremo gradualmente mentre leggiamo i numeri.
  • Il Rnell'angolo in alto a sinistra ottiene il suo set massa al codice di carattere 0(48) con '0, quindi massa ed energia sono scambiati con @e infine massa incrementato una volta con +dare (1,48). Viene quindi reindirizzato con specchi diagonali \e /conservato in un reattore a fissione. Useremo la 48sottrazione per trasformare l'ingresso ASCII nei valori effettivi delle cifre. Abbiamo anche dovuto aumentare la massa 1per evitare la divisione di 0.
  • Infine, Unell'angolo in basso a sinistra è ciò che in realtà mette tutto in movimento e viene inizialmente utilizzato solo per il flusso di controllo.

Dopo essere stato reindirizzato a destra, l'atomo di controllo colpisce ?. Questo è il componente di input. Legge un personaggio e imposta la massa dell'atomo sul valore ASCII letto e l'energia su 0. Se invece colpiamo EOF, l'energia sarà impostata su 1.

L'atomo continua e poi colpisce %. Questo è un interruttore mirror. Per energia non positiva, questo si comporta come uno /specchio. Ma per l'energia positiva agisce come un \(e diminuisce anche l'energia di 1). Quindi, mentre stiamo leggendo i personaggi, l'atomo verrà riflesso verso l'alto e potremo elaborare il personaggio. Ma quando avremo finito con l'input, l'atomo verrà riflesso verso il basso e potremo applicare una logica diversa per recuperare il risultato. Cordiali saluti, il componente opposto è &.

Quindi per ora abbiamo un atomo che sale. Quello che vogliamo fare per ogni personaggio è leggere il suo valore in cifre, aggiungerlo al nostro totale corrente e quindi moltiplicare quel totale corrente per 10 per preparare la cifra successiva.

L'atomo personaggio colpisce prima un reattore a fusione (predefinito) Y. Questo divide l'atomo e usiamo la copia di sinistra come atomo di controllo per tornare indietro nel componente di input e leggere il carattere successivo. La copia corretta verrà elaborata. Considera il caso in cui abbiamo letto il personaggio 3. Lo sarà il nostro atomo (51,0). Scambiamo massa ed energia @, in modo tale da poter sfruttare la sottrazione del prossimo reattore a fissione. Il reattore sottrae 48l'energia (senza cambiare la massa), quindi invia due copie di (0,3)- l'energia ora corrisponde alla cifra che abbiamo letto. La copia in uscita viene semplicemente scartata con ;(un componente che distrugge tutti gli atomi in arrivo). Continueremo a lavorare con la copia discendente. Dovrai seguire il suo percorso attraverso/e \rispecchia un po '.

Il @poco prima che il reattore a fusione scambia nuovamente massa ed energia, in modo da aggiungere (3,0)al totale corrente il Y. Si noti che lo stesso totale corrente avrà quindi sempre 0energia.

Adesso Jè un salto. Ciò che fa è saltare qualsiasi atomo in arrivo in avanti con la sua energia. Se lo è 0, l'atomo continua a muoversi dritto. In questo caso 1salterà una cella, in caso contrario 2salterà due celle e così via. L'energia viene spesa nel salto, quindi l'atomo finisce sempre con energia 0. Poiché il totale parziale non ha energia, il salto viene ignorato per ora e l'atomo viene reindirizzato nel reattore a fusione {che moltiplica la sua massa per 10. La copia discendente viene scartata ;mentre la copia ascendente viene reimmessa nel Yreattore come nuovo totale parziale .

Quanto sopra continua a ripetersi (in un modo divertente pipeline in cui vengono elaborate nuove cifre prima di quelle precedenti) fino a quando non colpiamo EOF. Ora %invierà l'atomo verso il basso. L'idea è di trasformare questo atomo in (0,1)ora prima di colpire il reattore totale in funzione in modo che a) il totale non sia influenzato (massa zero) eb) otteniamo un'energia 1per saltare sopra [. Possiamo facilmente prenderci cura dell'energia con $, che aumenta l'energia.

Il problema è che ?non reimposta la massa quando si colpisce EOF, quindi la massa sarà ancora quella dell'ultimo personaggio letto e l'energia sarà 0(perché %diminuita di 1nuovo 0). Quindi vogliamo sbarazzarci di quella massa. Per fare ciò, scambiamo @nuovamente massa ed energia .

Ho bisogno di introdurre un componente di più prima di terminare questa sezione: Z. Questo è essenzialmente lo stesso di %o &. La differenza è che consente agli atomi di energia positiva di passare direttamente (mentre diminuisce l'energia) e devia gli atomi di energia non positiva di 90 gradi a sinistra. Possiamo usarlo per eliminare l'energia di un atomo facendola scorrere Zripetutamente, non appena l'energia sarà andata via, l'atomo verrà deviato e lascerà il circuito. Questo è questo modello:

/ \
[Z/

dove l'atomo si sposterà verso l'alto una volta che l'energia è zero. Userò questo schema in una forma o nell'altra più volte nelle altre parti del programma.

Quindi quando l'atomo lascia questo piccolo anello, sarà (1,0)e sostituito (0,1)da @prima di colpire il reattore a fusione per rilasciare il risultato finale dell'input. Tuttavia, il totale parziale sarà disattivato di un fattore 10, perché lo abbiamo già moltiplicato provvisoriamente per un'altra cifra.

Quindi ora con energia 1, questo atomo salterà [e salterà dentro /. Questo lo devia in un reattore a fissione che abbiamo preparato per dividere per 10 e correggere la nostra moltiplicazione estranea. Ancora una volta, scartiamo la metà con ;e manteniamo l'altro come output (qui rappresentato con il Oquale semplicemente stampare il personaggio corrispondente e distruggere l'atomo - nel programma completo continuiamo invece a usare l'atomo).

itoa

           /     \
input ->  [{/\  ;@
          @\ <\   
          $  ;}++ +++++++L
          %@A{/
          M  \@+>/
          ~     @
          SNR'0YK
            \  A!/

Naturalmente, dobbiamo anche riconvertire il risultato in una stringa e stamparlo. Ecco a cosa serve questa parte. Ciò presuppone che l'input non arrivi prima del segno di spunta 10 o giù di lì, ma nel programma completo viene facilmente fornito. Questo bit si trova in fondo al programma completo.

Questo codice introduce un nuovo componente Fission molto potente: lo stack K. Lo stack è inizialmente vuoto. Quando un atomo con energia non negativa colpisce la pila, l'atomo viene semplicemente spinto sulla pila. Quando un atomo con energia negativa colpisce la pila, la sua massa ed energia saranno sostituite dall'atomo nella parte superiore della pila (che viene così fatto scoppiare). Se lo stack è vuoto, la direzione dell'atomo viene invertita e la sua energia diventa positiva (cioè viene moltiplicata per -1).

Ok, torniamo al codice attuale. L'idea dello itoasnippet è di prendere ripetutamente l'input modulo 10 per trovare la cifra successiva dividendo l'input per 10 per la successiva iterazione. Ciò produrrà tutte le cifre in ordine inverso (dal meno significativo al più significativo). Per correggere l'ordine, spingiamo tutte le cifre in una pila e alla fine le estraiamo una per una per stamparle.

La metà superiore del codice esegue il calcolo delle cifre: Lcon i plus si ottiene un 10 che cloniamo e alimentiamo in una fissione e in un reattore a fusione in modo che possiamo dividere e moltiplicare per 10. Il loop inizia essenzialmente dopo il [nell'angolo in alto a sinistra . Il valore corrente viene diviso: una copia viene divisa per 10, quindi moltiplicata per 10 e memorizzata in un reattore a fissione, che viene quindi colpito dall'altra copia all'apice. Questo calcola i % 10come i - ((i/10) * 10). Si noti inoltre che Adivide il risultato intermedio dopo la divisione e prima della moltiplicazione, in modo che possiamo alimentare i / 10la successiva iterazione.

Si %interrompe il ciclo quando la variabile di iterazione raggiunge 0. Dato che si tratta più o meno di un ciclo do-while, questo codice funzionerebbe anche per la stampa 0(senza creare zero iniziali altrimenti). Una volta lasciato il ciclo vogliamo svuotare la pila e stampare le cifre. Sè l'opposto di Z, quindi è un interruttore che devia un atomo in arrivo con energia non positiva di 90 gradi a destra. Quindi l'atomo in realtà si sposta sul bordo dal Srettilineo al Kpop-up di una cifra (nota ~che assicura che l'atomo in arrivo abbia energia -1). Quella cifra viene incrementata di 48per ottenere il codice ASCII del carattere della cifra corrispondente. Il Adivide la cifra per stampare una copia con!e reinserire l'altra copia nel Yreattore per la cifra successiva. La copia stampata viene utilizzata come innesco successivo per la pila (si noti che anche gli specchi lo inviano attorno al bordo per colpire Mda sinistra).

Quando la pila è vuota, la Kvolontà rifletterà l'atomo e trasformerà la sua energia in +1modo che passi direttamente attraverso S. Nstampa una nuova riga (solo perché è pulita :)). E poi l'atomo va di R'0nuovo attraverso per finire nel lato del Y. Poiché non ci sono altri atomi in giro, questo non verrà mai rilasciato e il programma termina.

Calcolo del numero di fissione: il framework

Andiamo alla carne reale del programma. Il codice è sostanzialmente una porta della mia implementazione di riferimento di Mathematica:

fission[n_] := If[
  (div = 
    SelectFirst[
      Reverse@Divisors[2 n], 
      (OddQ@# == IntegerQ[n/#] 
       && n/# > (# - 1)/2) &
    ]
  ) == 1,
  1,
  1 + Total[fission /@ (Range@div + n/div - (div + 1)/2)]
]

dove divè il numero di numeri interi nella partizione massima.

Le differenze principali sono che non possiamo gestire valori a mezzo numero intero in Fission, quindi sto facendo molte cose moltiplicate per due e che non c'è ricorsione in Fission. Per ovviare a questo, sto spingendo tutti gli interi in una partizione in una coda per essere elaborati in seguito. Per ogni numero che elaboriamo, incrementeremo un contatore di uno e una volta che la coda è vuota, rilasceremo il contatore e lo spediremo per la stampa. (Una coda, Qfunziona esattamente come K, solo nell'ordine FIFO.)

Ecco un framework per questo concetto:

                      +--- input goes in here
                      v 

                     SQS ---> compute div from n          D /8/
                     ~4X               |                /~KSA /
                       3               +----------->    { +X
initial trigger ---> W                               6~@/ \/
                              4                   
                     W        ^                     /
                              |              3
                     ^     generate range    |
                     |     from n and div  <-+----- S6
                     |         -then-      
                     +---- release new trigger

I nuovi componenti più importanti sono le cifre. Questi sono teletrasporto. Tutti i teletrasporto con la stessa cifra appartengono insieme. Quando un atomo colpisce un qualsiasi teletrasporto, verrà immediatamente spostato il teletrasporto successivo nello stesso gruppo, dove il successivo viene determinato nel solito ordine da sinistra a destra, dall'alto verso il basso. Questi non sono necessari, ma aiutano con il layout (e quindi il golf un po '). C'è anche quello Xche duplica semplicemente un atomo, inviando una copia direttamente davanti e l'altra all'indietro.

Ormai potresti riuscire a risolvere da solo la maggior parte del framework. L'angolo in alto a sinistra ha la coda di valori ancora da elaborare e rilascia uno nalla volta. Una copia di nviene teletrasportata in basso perché ne abbiamo bisogno durante il calcolo dell'intervallo, l'altra copia va nel blocco in alto che calcola div(questa è di gran lunga la sezione più grande del codice). Una volta divcalcolato, viene duplicato: una copia incrementa un contatore nell'angolo in alto a destra, in cui è memorizzato K. L'altra copia viene teletrasportata verso il basso. Se lo divfosse 1, lo deviamo immediatamente verso l'alto e lo usiamo come trigger per la successiva iterazione, senza accodare alcun nuovo valore. Altrimenti usiamo diven nella sezione in basso per generare il nuovo intervallo (ovvero un flusso di atomi con le masse corrispondenti che vengono successivamente inserite nella coda), quindi rilasciare un nuovo trigger dopo che l'intervallo è stato completato.

Una volta che la coda è vuota, il grilletto verrà riflesso, passando direttamente attraverso Se riapparendo nell'angolo in alto a destra, da cui rilascia il contatore (il risultato finale) A, che viene poi teletrasportato su itoavia 8.

Calcolo del numero di fissione: il corpo del loop

Quindi tutto ciò che rimane sono le due sezioni per calcolare dive generare l'intervallo. Il calcolo divè questa parte:

 ;    
 {+L  /$     \/\/\/\/\/   5/ @  [~ &@[S\/ \
/A@[  %5                   /; &    K  } [S/
   \  A$@S  S\/  \/\/\/   \/>\ /S]@A  /  \ 
         X  X    /> \      +\ A\ /   \ /
    /   ~A\;     +;\      /@
ZX [K    / {/  / @  @ }  \ X @
   \AS   </      \V /    }SZS S/
     X   ;;@\   /;X  /> \ ; X X
     \@+  >/ }$S SZS\+;    //\V
       / \\  /\; X X @  @  \~K{
       \0X /     /~/V\V /   0W//
\        Z      [K \  //\
           \ /\7\A  /;7/\/

Probabilmente hai visto abbastanza ora per risolverlo da solo con un po 'di pazienza. La suddivisione di alto livello è questa: le prime 12 colonne o giù di lì generano un flusso di divisori di 2n. Le prossime 10 colonne filtrano quelle che non soddisfano OddQ@# == IntegerQ[n/#]. Le successive 8 colonne filtrano quelle che non soddisfano n/# > (# - 1)/2). Alla fine spingiamo tutti i divisori validi su uno stack e, una volta terminato, svuotiamo l'intero stack in un reattore a fusione (sovrascrivendo tutti tranne l'ultimo / il più grande divisore) e quindi rilasciamo il risultato, seguito eliminando la sua energia (che non era -zero dal controllo della disuguaglianza).

Ci sono molti percorsi folli lì dentro che non fanno davvero nulla. Prevalentemente, la \/\/\/\/follia in alto (anche le 5s ne fanno parte) e un percorso intorno al fondo (che attraversa la 7s). Ho dovuto aggiungere questi per affrontare alcune brutte condizioni di gara. Fission potrebbe utilizzare un componente di ritardo ...

Il codice che genera il nuovo intervallo da ned divè questo:

 /MJ $$\
4}K~@\ &]    @\  3/\
\{   }$A/1 2  }Y~K <\
 \@  /   \@<+@^   1;}++@
  2        ;   \    /

Prima calcoliamo n/div - (div + 1)/2(entrambi i termini floored, che produce lo stesso risultato) e archiviamo per dopo. Quindi generiamo un intervallo da divgiù a 1e aggiungiamo il valore memorizzato a ciascuno di essi.

Ci sono due nuovi schemi comuni in entrambi, che dovrei menzionare: uno è SXo ZXcolpito dal basso (o versioni ruotate). Questo è un bel modo per duplicare un atomo se vuoi che una copia vada avanti (dal momento che il reindirizzamento delle uscite di un reattore a fusione a volte può essere ingombrante). Il So Zruota l'atomo nel Xe quindi ruota la copia specchiata nella direzione originale di propagazione.

L'altro modello è

[K
\A --> output

Se memorizziamo un valore qualsiasi, Kpossiamo recuperarlo ripetutamente colpendo Kcon energia negativa dall'alto. Il Aduplica il valore che ci interessa e invia ciò che copiare posteriore destra nello stack per la prossima volta ne abbiamo bisogno.

Beh, è ​​stato un bel po '... ma se hai davvero superato questo, spero che tu abbia avuto l'idea che Fission i͝s̢̘̗̗ ͢i̟nç̮̩r̸̭̬̱͔e̟̹̟̜͟d̙i̠͙͎̖͓̯b̘̠͎̭̰̼l̶̪̙̮̥̮y̠̠͎̺͜ ͚̬̮f̟͞u̱̦̰͍n͍ ̜̠̙t̸̳̩̝o ̫͉̙͠p̯̱̭͙̜͙͞ŕ̮͓̜o̢̙̣̭g̩̼̣̝r̤͍͔̘̟ͅa̪̜͇m̳̭͔̤̞ͅ ͕̺͉̫̀ͅi͜n̳̯̗̳͇̹.̫̞̲̞̜̳


1
Now with 100% fewer scrollbars.quindi dici .. ed è ancora da continuare ...
Ottimizzatore

13
Ancora più leggibile della maggior parte dei codici che i nostri sviluppatori junior generano.
corsiKa,

Come creatore di Fission, anche io non ho ancora scritto un programma così grande! Sono impressionato! La tua spiegazione è spettacolare e potrebbe sicuramente servire da tutorial per la lingua.
C0deH4cker

Inoltre, l'ultima riga della tua risposta sembra un programma Fission;)
C0deH4cker

12

CJam, 42 41 byte

ri]{_{:X,:)_few:+W%{1bX=}=}%{,(},e_}h]e_,

Un semplice primo attraversamento di Breadth e una condizione di arresto del livello successivo vuoto.

Come funziona :

ri]                                       e# This represents the root of the fissile tree
   {                               }h     e# Now we run a do-while loop
    _{                    }%              e# Copy the nodes at the current level and map them
                                          e# via this code block to get next level nodes
      :X,:)                               e# Store the node value in X and get array [1..X]
           _few                           e# Copy the array and get continuous slices of
                                          e# length 1 through X from the array [1..X]
               :+W%                       e# Right now, we have an array of array with each
                                          e# array containing slice of same length. We join
                                          e# those arrays and reverse them to get slices of
                                          e# higher length in front of lower lengths
                   {1bX=}=                e# Choose the first slice whose sum is same as X
                                          e# The reversal above makes sure that we give
                                          e# preference to slice of higher length in case of
                                          e# multiple slices add up to X
                            {,(},         e# Filter out slices of length 1 which basically
                                          e# mean that the current node cannot be split up
                                 e_       e# Join all slices in a single array. This is our
                                          e# next level in the Fissile tree. If this is empty
                                          e# it means that all no further node can be
                                          e# decomposed. In an {}h do-while loop, this fact
                                          e# itself becomes the stopping condition for the
                                          e# loop
                                     ]e_, e# Wrap all levels in an array. Flatten the array
                                          e# and take its length

Provalo online qui


Questo può probabilmente essere golfato a circa 35 byte. Sto cercando di capire come ..
Ottimizzatore

10

Python 3, 112 byte

def f(n,c=0):
 d=n-c;s=(n-d*~-d/2)/d
 return(s%1or s<1)and f(n,c+1)or+(d<2)or-~sum(f(int(s)+i)for i in range(d))

4 byte salvati grazie a @FryAmTheEggman.

Spiegazione in arrivo ...

Fatto bonus: ogni potenza di 2 ha un numero di Fissione di 1. Questo perché la somma di una sequenza di lunghezza pari è sempre la somma dei due numeri medi, che è dispari, moltiplicata per la metà della lunghezza della sequenza, che è intero . La somma di una sequenza di lunghezza dispari è il numero medio moltiplicato per la lunghezza della sequenza, che è dispari. Quindi, poiché un potere di 2 non ha un divisore dispari, può essere espresso solo come la somma di se stesso.


2 + 3 + 4 + 5 = 14 che non è dispari. Il tuo argomento per le sequenze di lunghezza pari dovrebbe essere modificato in "la somma di una sequenza di lunghezza pari è la somma dei due numeri medi, che è dispari, moltiplicata per metà della lunghezza". Il resto della tua affermazione passa inalterato.
Bruno Le Floch,

@BrunoLeFloch Grazie! Corretto.
randomra,

Il tuo titolo non dovrebbe riflettere i miglioramenti? cioè <strike> 117 </strike> <strike> 113 </strike> 112
corsiKa

@corsiKa Di solito lo faccio solo per importanti miglioramenti. Altrimenti ci sarebbero troppi numeri colpiti.
randomra,

8

Python 2, 111 102 97 byte

Leggibile:

def f(n,c=0):a=n-c;b=n-a*~-a/2;return 1/a or-~sum(map(f,range(b/a,b/a+a)))if b>b%a<1else f(n,c+1)

Non-così-leggibile:

def f(n,a=0):b=n-a*~-a/2;return b>0and(f(n,a+1)or b%a<1and(1/a or-~sum(map(f,range(b/a,b/a+a)))))

Entrambi 97 byte.

bè il nmeno il (a-1)thnumero triangolare. Se b % a == 0, allora nè la somma di anumeri consecutivi a partire da b.


8
Consideravo Python un linguaggio generalmente leggibile fino a quando non mi sono unito a PPCG.
Alex A.

Penso che tu debba migliorare la tua definizione di leggibile ..: P
Ottimizzatore,

Python 2 non lo consente 1else. Funziona solo la seconda soluzione. Non è fino a Python 3 che elsepuò immediatamente seguire un numero.
mbomb007,

@ mbomb007 Per quanto ne so, funziona benissimo dal 2.7.8 in poi
Sp3000,

Ok, stavo usando 2.7.2.
mbomb007,

7

Python 2, 86

f=lambda n,R={1}:n-sum(R)and f(n,R^{[min(R),max(R)+1][n>sum(R)]})or-~sum(map(f,R-{n}))

Meno golf:

def f(n,R={1}):
    d=sum(R)-n
    if d==0:return (sum(map(f,R-{n}))
    if d<0:return f(n,R|{max(R)+1})
    if d>0:return f(n,R-{min(R)})

L'idea è di testare potenziali esecuzioni di numeri interi consecutivi che si sommano n. La corsa viene memorizzata direttamente come set Ranziché tramite i suoi endpoint.

Controlliamo come la somma della corsa corrente viene confrontata con la somma desiderata ntramite la loro differenza.

  • Se la somma è troppo grande, tagliamo l'elemento più piccolo nella corsa.
  • Se la somma è troppo piccola, estendiamo la corsa aumentando il suo massimo di 1.
  • Se la somma è corretta, si ricorre, mappando fsulla corsa, sommando e aggiungendo 1 per il nodo corrente. Se la corsa è {n}, abbiamo provato tutte le somme possibili non banali, interrompere la ricorsione rimuovendo nprima.

Grazie a Sp3000 per aver salvato 3 caratteri.


7

Python 2, 85

Sono molto orgoglioso di questa risposta perché ci vogliono già decine di secondi per n = 9 e 5-10 minuti per n = 10. Nel code golf questo è considerato un attributo desiderabile di un programma.

f=lambda n,a=1,d=1:a/n or[f(a)+f(n-a,1+1%d*a)+1/d,f(n,a+d/n,d%n+1)][2*n!=-~d*(2*a+d)]

Esiste anche una versione di corto circuito che non richiede così tanto tempo e utilizza la stessa quantità di byte:

f=lambda n,a=1,d=1:a/n or~d*(2*a+d)+n*2and f(n,a+d/n,d%n+1)or f(a)+f(n-a,1+1%d*a)+1/d 

Potrebbe essere più veloce, ma almeno supera il limite di ricorsione predefinito quando n supera leggermente i 40.

L'idea è quella di fare una ricerca della forza bruta per numeri ae dtali che a + a+1 + ... + a+d == n, su valori compresi tra 1 e n. Il f(n,a+d/n,d%n+1)ramo della ricorsione scorre attraverso le (a, d)coppie. Nel caso in cui l'uguaglianza sia soddisfatta, riesco a evitare una map(range(...))chiamata costosa suddividendola in soli due rami indipendentemente da quanto sia lunga la sequenza. I numeri a+1attraverso dvengono raggruppati in una chiamata fimpostando il aparametro in modo che non sia possibile utilizzare un modo diverso di suddividere la sequenza.


Come funziona?
xnor

"Sono molto orgoglioso di questa risposta perché ci vogliono già decine di secondi per n = 9 e 5-10 minuti per n = 10. Nel codice golf questo è considerato un attributo desiderabile di un programma." Ho fatto +1 per quello.
Soham Chowdhury,

6

Haskell, 76 69 byte

f x=head$[1+sum(map f[y..z])|y<-[1..x-1],z<-[y..x],sum[y..z]==x]++[1]

Uso:

*Main> map f [1..40]
[1,1,3,1,5,6,5,1,6,7,12,10,12,11,12,1,8,16,14,17,18,18,23,13,21,18,22,23,24,19,14,1,22,20,23,24,31,27,25,26]

Come funziona:

[  [y..z] |y<-[1..x-1],z<-[y..x],sum[y..z]==x]

           make a list of lists with all consecutive integers (at least 2 elements)
           that sum up to x, sorted by lowest number, e.g. 9 -> [[2,3,4],[4,5]].

1+sum(map f[...]) 

           recursively calculate the Fission Number for each list

[...]++[1]

           always append the 1 to the list of Fission Numbers.

head

           take the first element, which is either the Fission Number of the
           longest list or if there's no list, the 1 appended in the step before.  

3

Retina , 66 byte

^|$
,
(`,(1+?)(?=(?<1>1\1)+\D)
$0;
+)`,(1*);1\1
,$1,1$1;
^,|1

.
1

Accetta input e stampa output in modo unario.

Puoi mettere ogni riga in un singolo file o eseguire il codice come con il -sflag. Per esempio:

> echo -n 1111111|retina -s fission
11111

Spiegazione:

  • Manteniamo un elenco delimitato da virgole dei numeri da suddividere.
  • Per ogni numero prendiamo il valore iniziale più piccolo che può creare uno splitup valido e delimitarlo dal resto con un punto e virgola.
  • Se c'è un punto e virgola all'interno di un numero, lo cambiamo in una virgola e delimitiamo la parte successiva del numero correttamente dimensionata (lunghezza dell'elemento precedente + 1).
  • Ripetiamo i passaggi 2 e 3 fino a quando non si verificano cambiamenti.
  • Otteniamo una virgola per ogni foglia e un punto e virgola per ogni nodo interno più una virgola aggiuntiva perché abbiamo iniziato con due virgole. Quindi rimuoviamo una virgola e le parti dei numeri 1e convertiamo il resto in 1"s".

Gli stati della stringa durante il processo con input 11111111111111 (unary 14):

,11111111111111,
,11;111111111111,
,11,1;11,1111,11;111;,
,11,1,11;,1111,11,1;11;;,
,11,1,11;,1111,11,1,11;;;,
,,;,,,,;;;,
11111111111

Mille grazie per @MartinButtner per l'aiuto nella chat!


3

CJam (43 byte)

qi,{):X),_m*{{_)*2/}/-X=}=~\,>0\{~$+}/)}%W=

Demo online

Sono sicuro che mi mancano alcuni trucchi con i loop avanzati, ma questo sfrutta nettamente la proprietà CJam (che in precedenza mi ha infastidito) che all'interno di una %mappa i risultati rimangono nello stack e quindi è possibile accedervi utilizzando $un offset negativo.


Domani darò un'occhiata più da vicino, ma per cominciare non è necessario ,all'inizio. /e %pochi altri implicitamente trasformano i numeri in intervalli.
Martin Ender,

,_m*può essere sostituito con 2m*. La formula di progressione aritmetica può essere sostituita con ~,>:Y_,+:+e ~\,>0\ diventa !Y. Infine, se usi {}#invece di {}=, non hai bisogno del )dopo X. Mettendo tutto insieme:ri{):X2m*{~,>:Y_,+:+X=}#!Y{~$+}/)}%W=
Dennis

2

Vai, 133 byte

func 算(n int)int{Σ:=1;for i:=0;i<n;i++{for j:=0;j<n;j++{if i*i+i-j*j-j==2*n{for k:=j+1;k<=i;k++{Σ+=算(k)};j,i=n,n}}};return Σ}

Questo è il mio primo codice golf, scusate se ho commesso degli errori.

Questo usa l'idea che la "composizione" fissile può anche essere vista come una differenza tra due sequenze di numeri interi ordinati. Ad esempio, prendi la "composizione" fissile per il numero 13. È 6,7. Ma può essere visto come la somma di numeri interi 1 ... 7 meno la somma di numeri interi 1 ... 5

  A: 1 2 3 4 5 6 7  sum = 28
  B: 1 2 3 4 5      sum = 15 
A-B:           6 7  sum = 13, which is also 28-15 = 13

Richiama la formula dai giorni di scuola di Gauss, somma 1 ... n = (n ^ 2 + n) / 2. Quindi, per trovare una composizione di numeri interi sequenziali per un dato n, potremmo anche dire che stiamo cercando 'end points' peq nell'intervallo 1 ... n in modo che (p ^ 2 + p) / 2 - ( q ^ 2 + q) / 2 = n. Nell'esempio sopra, avremmo cercato 'end points' 5 e 7 perché 7 ^ 2 + 7 = 56/2, 5 ^ 2 + 5 = 30/2, 56 / 2-30 / 2 = 28-15 = 13.

Ora ci sono molti modi possibili per comporre un numero fissile, come Martin ha notato 9 = 2 + 3 + 4 ma anche 4 + 5. Ma sembra ovvio che la sequenza di partenza "più bassa" sarà anche la più lunga, perché richiede più piccoli numeri per riassumere un numero elevato rispetto ai numeri di medie dimensioni. (Purtroppo non ho prove)

Quindi, per trovare la composizione di 9, prova ogni 'coppia di punti finali', peq, ripetendo p e q separatamente da 0 a 9, e verifica se p ^ p + p / 2 - q ^ 2 + q / 2 = 9. O, più semplicemente, moltiplicare l'equazione per 2, per evitare i problemi di divisione dell'arrotondamento e mantenere tutta la matematica in numeri interi. Quindi stiamo cercando p e q tale che (p ^ p + p) - (q ^ q + q) = 9 * 2. La prima corrispondenza che troviamo saranno gli endpoint della composizione fissile perché, come notato, anche il gruppo di numeri più basso sarà il più lungo e stiamo cercando da basso ad alto (da 0 a 9). Usciamo dal circuito non appena troviamo una corrispondenza.

Ora la funzione ricorsiva trova quei "punti finali fissili" p e q per il dato n, quindi si richiama da sé per ciascuno dei "figli" dell'albero da p a q. Per 9, troverà 1 e 4, (20-2 = 18), quindi si richiamerebbe su 2, 3 e 4, sommando i risultati. Per numeri come 4 semplicemente non trova mai una corrispondenza, quindi restituisce '1'. Questo potrebbe essere abbreviabile, ma è come il mio terzo programma go, e non sono un esperto di ricorsione.

Grazie per aver letto.


Bel lavoro! Ma perché la funzione Unicode / i nomi delle variabili. Questo costa byte inutili e sicuramente avresti potuto usare solo una lettera normale?
Martin Ender,

Grazie per il tuo gentile commento. Ma mi sono chiesto, perché non contare i caratteri anziché i byte :)
don luminoso

Perché quelle sono le regole predefinite qui intorno. :) Il motivo per cui di solito contiamo i byte e non i caratteri è perché altrimenti ciò accade , il che è poco divertente per tutte le parti coinvolte. ;) (Detto questo, qualsiasi autore di sfide è libero di specificare il conteggio per carattere anziché per byte, ma non l'ho fatto in modo specifico.)
Martin Ender

1

CJam, 40 35 33 byte

ri{__,f-_few:+{1b1$=}=|{F}*}:F~],

Grazie a @Optimizer per il suggerimento few, che ha salvato 2 byte.

Provalo online nell'interprete CJam .

Come funziona

ri      e# Read an integer from STDIN.
{       e# Define function F(X):
  _     e# Push X.
  _,    e# Push [0 ... X-1].
  f-    e# Subract each from X. Pushes Y := [X ... 1].
  _few  e# Push all overlapping slices of Y of length in Y.
  :+    e# Consolidate the slices of different lenghts in a single array.
  {     e# Find the first slice S such that...
    1b  e#   the sum of its elements...
    1$= e#   equals X.
  }=    e#   Since Y is in descending order, the first matching slice is also the longest.
  |     e# Set union with [X]. This adds X to the beginning of the S if S != [X].
  {F}*  e# Execute F for each element of S except the first (X).
}:F     e#
~       e# Execute F for the input.
],      e# Count the integers on the stack.

Se combini il mio primo tempo con il secondo tempo, otterrai 34:ri{_,:)_few:+W%{1b1$=}=|{F}*}:F~],
Ottimizzatore

@Optimizer: Nice. Ciò consente un ulteriore miglioramento. Grazie!
Dennis,
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.