Nombres Fissiles


47

J'ai trouvé cette séquence alors que je travaillais sur Evolution of OEIS , mais je n'ai jamais réussi à l'afficher comme réponse. Après avoir écrit une implémentation de référence dans Mathematica, j’ai pensé que c’était un exercice amusant à faire en tant que défi à part, alors nous y voilà.

Construisons un réacteur à fission numérique! Considérons un entier positif N. À titre d'exemple, nous allons regarder 24. Pour fissionner ce nombre, nous devons trouver le plus grand nombre d’ entiers positifs consécutifs à cette somme N. Dans ce cas, c'est 7 + 8 + 9 = 24. Nous avons donc divisé 24en trois nouveaux numéros. Mais ce ne serait pas vraiment un réacteur à fission sans réactions en chaîne. Répétons donc le processus de manière récurrente pour ces composants:

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

Notez que nous arrêtons le processus chaque fois que le nombre ne peut pas être décomposé en entiers consécutifs plus petits. Notez également que nous aurions pu écrire en 9tant que 4 + 5, mais 2 + 3 + 4a plus de composants. Le nombre de fission de Nest maintenant défini comme le nombre d'entiers obtenus dans ce processus, y compris Nlui-même. L'arbre ci-dessus a 13 nœuds, donc F(24) = 13.

Cette séquence est l'entrée OEIS A256504 .

Les 40 premiers termes, à partir de N = 1, sont

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

Les 1000 premiers termes peuvent être trouvés dans cette pastebin .

Le défi

Étant donné un entier positif N, déterminez son numéro de fission F(N). (Donc, vous n'avez pas besoin de couvrir le leader 0énuméré sur OEIS.)

Vous pouvez écrire un programme ou une fonction en prenant l’entrée via STDIN (ou l’alternative la plus proche), un argument de ligne de commande ou une argumentation de fonction et en générant le résultat via STDOUT (ou l’alternative la plus proche), une valeur de retour de fonction ou un paramètre de fonction (out).

C'est le code de golf, donc la réponse la plus courte (en octets) gagne.

Question bonus: Pouvez-vous trouver des propriétés intéressantes de cette séquence?


Je remarque que l'OEIS semble avoir une erreur à n = 34: à partir de n = 32, il répertorie (actuellement) 1, 22, 22, 23, 24, 31 plutôt que 1, 22, 20, 23, 24, 31.
mathmandan

1
@mathmandan Bonne capture, je proposerai probablement une correction (avec le premier diagramme).
Martin Ender


@mathmandan FYI, j'ai corrigé la séquence et l'exemple maintenant, et également ajouté mon implémentation de référence et les 10 premiers termes.
Martin Ender

Cela semble bon! Merci pour votre travail!
mathmandan

Réponses:


16

Pyth, 23 22 21 octets

Lh&lJfqbsT.:tUb)syMeJ

Ceci définit une fonction récursive y. Essayez-le en ligne: démonstration

Explication:

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

Fission , 1328 989 887 797 octets

Cette réponse est un peu déraisonnablement longue (j'aimerais que nous ayons des régions réductibles ) ... s'il vous plaît, n'oubliez pas de faire défiler la page au-delà et de montrer aux autres réponses un peu d'amour!

Travailler sur ce code est ce qui a inspiré ce défi. Je voulais ajouter une réponse dans Fission à EOEIS, ce qui m'a amené à cette séquence. Cependant, pour apprendre réellement la fission et l’appliquer, il a fallu quelques semaines pour travailler dessus. Entre temps, la séquence avait vraiment pris de l'ampleur et j'ai donc décidé de poster un défi séparé pour elle (en plus, cela n'aurait pas été particulièrement long en bas de l'arbre sur EOEIS).

Je vous présente donc la monstruosité:

 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!/

Il s'attend à ce qu'il n'y ait pas de fin de ligne sur l'entrée, vous pouvez donc l'appeler comme echo -n 120 | ./Fission oeis256504.fis.

La mise en page pourrait probablement encore être plus efficace, alors je pense qu'il reste encore beaucoup à faire (par exemple, il contient 911 581 461 374 espaces).

Avant de passer à l'explication, une note sur le test: l' interprète officiel ne fonctionne pas entièrement tel quel. a) Mirror.cppne compile pas sur beaucoup de systèmes. Si vous rencontrez ce problème, commentez la ligne incriminée - le composant affecté (un miroir aléatoire) n'est pas utilisé dans ce code. b) Quelques bugs peuvent conduire à un comportement indéfini (et probablement pour un programme aussi complexe). Vous pouvez appliquer ce correctif pour les réparer. Une fois que vous avez fait cela, vous devriez pouvoir compiler l’interprète avec

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

Fait amusant: ce programme utilise presque tous les composants que la fission a à offrir, à l'exception de #(miroir aléatoire), :(demi-miroir) -ou |(miroir simple) et "(mode d'impression).

Quoi sur Terre?

Attention: Ce sera assez long ... Je suppose que vous êtes vraiment intéressé par le fonctionnement de Fission et par la façon dont vous pourriez le programmer. Parce que si vous ne l'êtes pas, je ne sais pas comment je pourrais résumer ceci. (Le paragraphe suivant donne cependant une description générale de la langue.)

La fission est un langage de programmation bidimensionnel où les données et le flux de contrôle sont représentés par des atomes se déplaçant à travers une grille. Si vous avez déjà vu ou utilisé Marbelous auparavant, le concept devrait être vaguement familier. Chaque atome a deux propriétés entières: une masse non négative et une énergie arbitraire. Si la masse devient jamais négative, l'atome est retiré de la grille. Dans la plupart des cas, vous pouvez traiter la masse comme la "valeur" de l'atome et l'énergie comme une sorte de méta-propriété utilisée par plusieurs composants pour déterminer le flux des atomes (c'est-à-dire que la plupart des commutateurs dépendent du signe de l'énergie). Je vais désigner des atomes par (m,E), si nécessaire. Au début du programme, la grille commence par un tas de(1,0)des atomes où que vous vous trouviez sur l'un des quatre composants UDLR(où la lettre indique la direction dans laquelle l'atome se déplace initialement). Le tableau est ensuite peuplé de nombreux composants qui changent la masse et l’énergie des atomes, changent leur direction ou font d’autres choses plus sophistiquées. Pour une liste complète, voir la page esolangs , mais je vais en présenter la plupart dans cette explication. Un autre point important (que le programme utilise plusieurs fois) est que la grille est toroïdale: un atome qui frappe l'un des côtés réapparaît du côté opposé, se déplaçant dans la même direction.

J'ai écrit le programme en plusieurs parties plus petites et les ai assemblées à la fin. C'est ainsi que je vais passer en revue les explications.

atoi

Ce composant peut sembler plutôt inintéressant, mais il est simple et agréable et me permet d’introduire un grand nombre des concepts importants de l’arithmétique et du flux de contrôle de Fission. Par conséquent, je vais passer en revue cette partie avec des détails assez minutieux afin de pouvoir réduire les autres parties à l'introduction de nouveaux mécanismes de fission et au fait de signaler des composants de niveau supérieur dont vous devez pouvoir suivre le flux de contrôle détaillé.

La fission peut uniquement lire les valeurs d'octet de caractères individuels, et non de nombres entiers. Bien que ce soit une pratique acceptable ici, je me suis dit que je pouvais le faire correctement et analyser les entiers réels sur STDIN. Voici le atoicode:

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

Deux des composants les plus importants de la fission sont les réacteurs de fission et de fusion. Les réacteurs à fission sont l’un des types V^<>(le code ci-dessus utilise <et >). Un réacteur à fission peut stocker un atome (en l’envoyant dans le coin du personnage), la valeur par défaut (2,0). Si un atome frappe le sommet du personnage, deux nouveaux atomes seront envoyés aux côtés. Leur masse est déterminée en divisant la masse entrante par la masse stockée (c'est-à-dire divisant par deux en moitié) - l'atome de gauche obtient cette valeur et l'atome de droite reçoit le reste de la masse (c'est-à-dire que la masse est conservée dans la fission). . Les deux atomes sortants auront l'énergie entrante moinsl'énergie stockée. Cela signifie que nous pouvons utiliser des réacteurs de fission pour l'arithmétique, aussi bien pour la soustraction que pour la division. Si un réacteur de fission est touché depuis le site, l'atome est simplement réfléchi en diagonale et se dirigera ensuite vers le sommet du personnage.

Les réacteurs de fusion sont l'un des YA{}(le code ci-dessus utilise Yet {). Leur fonction est similaire: ils peuvent stocker un atome (par défaut (1,0)) et, lorsqu'ils sont touchés au sommet, deux nouveaux atomes sont envoyés aux côtés. Cependant, dans ce cas, les deux atomes seront identiques, conservant toujours l’énergie entrante et multipliant la masse entrante par la masse stockée. Autrement dit, par défaut, le réacteur à fusion duplique simplement tout atome atteignant son sommet. Quand ils touchent des côtés, les réacteurs à fusion sont un peu plus compliqués: l’atome est égalementstocké (indépendamment de l'autre mémoire) jusqu'à ce qu'un atome frappe le côté opposé. Lorsque cela se produit, un nouvel atome est libéré en direction du sommet dont la masse et l’énergie sont la somme des deux atomes anciens. Si un nouvel atome frappe le même côté avant qu'un atome correspondant n'atteigne le côté opposé, l'ancien atome sera simplement écrasé. Les réacteurs de fusion peuvent être utilisés pour mettre en œuvre l'addition et la multiplication.

Une autre composante simple que je veux sortir de la voie est [et ]qui a simplement mis la direction de l'atome à droite et à gauche, respectivement (quelle que soit la direction entrante). Les équivalents verticaux sont M(bas) et W(haut) mais ils ne sont pas utilisés pour le atoicode. UDLRagissent également comme WM][après avoir libéré leurs atomes initiaux.

Quoi qu'il en soit, regardons le code là-haut. Le programme commence avec 5 atomes:

  • Les Ret Lau bas obtiennent simplement que leur incrément de masse (avec +) devienne (10,0), puis stocké dans un réacteur à fission et dans un réacteur à fusion, respectivement. Nous allons utiliser ces réacteurs pour analyser l’entrée base 10.
  • Le Ldans le coin supérieur droit obtient sa masse décrémentée (à _) pour devenir (0,0)et est stocké dans le côté d'un réacteur à fusion Y. C'est pour garder une trace du nombre que nous lisons - nous allons l'augmenter et le multiplier progressivement au fur et à mesure que nous lisons des chiffres.
  • Dans Rle coin supérieur gauche, la masse est réglée sur le code de caractère de 0(48) '0, puis la masse et l’énergie sont échangées contre la dernière @et enfin augmentées une fois avec +pour donner (1,48). Il est ensuite redirigé avec des miroirs diagonaux \et /stocké dans un réacteur à fission. Nous allons utiliser la 48soustraction pour transformer l'entrée ASCII en valeurs réelles des chiffres. Nous avons également dû augmenter la masse 1pour éviter la division par 0.
  • Enfin, le Ucoin en bas à gauche est ce qui met tout en mouvement et est utilisé à l’origine uniquement pour le contrôle du flux.

Après avoir été redirigé vers la droite, l'atome de contrôle frappe ?. C'est le composant d'entrée. Il lit un caractère et définit la masse de l'atome sur la valeur ASCII lue et sur l'énergie 0. Si nous touchons EOF à la place, l'énergie sera réglée sur 1.

L'atome continue et ensuite frappe %. Ceci est un commutateur de miroir. Pour les énergies non positives, cela agit comme un /miroir. Mais pour l'énergie positive, il agit comme un \(et diminue également l'énergie de 1). Ainsi, pendant que nous lisons les caractères, l'atome sera réfléchi vers le haut et nous pouvons traiter le personnage. Mais lorsque nous en aurons terminé avec l'entrée, l'atome sera réfléchi vers le bas et nous pouvons appliquer une logique différente pour extraire le résultat. Pour votre information, le composant opposé est &.

Nous avons donc un atome en mouvement pour le moment. Ce que nous voulons faire pour chaque caractère est de lire sa valeur numérique, de l'ajouter à notre total cumulé, puis de multiplier ce total cumulé par 10 pour préparer le prochain chiffre.

L'atome de caractère frappe d'abord un réacteur de fusion (par défaut) Y. Cela divise l'atome et nous utilisons la copie de gauche comme atome de contrôle pour revenir au composant d'entrée et lire le caractère suivant. La copie de droite sera traitée. Prenons le cas où nous avons lu le personnage 3. Notre atome sera (51,0). Nous échangeons de la masse et de l’énergie @, de manière à pouvoir utiliser la soustraction du prochain réacteur à fission. Le réacteur soustrait 48l'énergie (sans changer la masse), donc il envoie deux copies de (0,3)- l'énergie correspond maintenant au chiffre que nous avons lu. La copie montante est simplement supprimée avec ;(un composant qui détruit tous les atomes entrants). Nous continuerons à travailler avec la copie descendante. Vous devrez suivre son chemin à travers le/et \reflète un peu.

Le @réacteur juste avant le réacteur de fusion échange à nouveau de la masse et de l’énergie, de sorte que nous ajouterons (3,0)à notre total courant dans le Y. Notez que le total cumulé aura donc toujours de l' 0énergie.

Maintenant Jc'est un saut. Ce qu'il fait, c'est sauter tout atome entrant par son énergie. Si c'est le cas 0, l'atome continue à avancer tout droit. Si c'est le 1cas, une cellule sera ignorée, si c'est le cas, 2deux cellules, etc. L'énergie est dépensée dans le saut, de sorte que l'atome se termine toujours avec de l'énergie 0. Puisque le total en cours a une énergie nulle, le saut est ignoré pour le moment et l’atome est redirigé dans le réacteur de fusion {qui multiplie sa masse par 10. La copie descendante est rejetée avec ;la copie montante qui est réinjectée dans le Yréacteur en tant que nouveau total courant.

Ce qui précède continue à se répéter (d'une manière amusante, où les nouveaux chiffres sont traités avant les précédents) jusqu'à ce que nous atteignions EOF. Maintenant le %va envoyer l'atome vers le bas. L'idée est de transformer cet atome en (0,1)maintenant avant de frapper le réacteur à total en marche de manière à ce que a) le total ne soit pas affecté (masse nulle) et b) nous obtenions une énergie de 1sursaut [. Nous pouvons facilement prendre soin de l'énergie avec $, ce qui augmente l'énergie.

Le problème est que ?cela ne réinitialise pas la masse lorsque vous appuyez sur EOF, la masse sera donc toujours celle du dernier caractère lu et l'énergie sera 0(parce que %décrémenté le 1retour à 0). Nous voulons donc nous débarrasser de cette masse. Pour ce faire, nous échangeons masse et énergie avec à @nouveau.

Je dois introduire un élément supplémentaire avant de terminer cette section: Z. C'est essentiellement la même chose que %ou &. La différence est qu’elle laisse passer les atomes d’énergie positive (tout en décrémentant l’énergie) et fait dévier les atomes d’énergie non positive de 90 degrés vers la gauche. Nous pouvons utiliser ceci pour éliminer l'énergie d'un atome en l'enroulant à Zplusieurs reprises - dès que l'énergie aura disparu, l'atome sera dévié et quittera la boucle. C'est ce motif:

/ \
[Z/

où l'atome se déplacera vers le haut une fois que l'énergie sera nulle. Je vais utiliser ce modèle sous une forme ou une autre plusieurs fois dans les autres parties du programme.

Ainsi , lorsque l'atome quitte cette petite boucle, il sera (1,0)et swappé (0,1)par le @avant de frapper le réacteur de fusion pour libérer le résultat final de l'entrée. Cependant, le total cumulé sera multiplié par 10, car nous l'avons déjà provisoirement multiplié pour un autre chiffre.

Alors maintenant, avec l’énergie 1, cet atome sautera le [et sautera dans le /. Cela le dévie dans un réacteur à fission que nous nous sommes préparés à diviser par 10 et à réparer notre multiplication superflue. Encore une fois, nous jetons une moitié avec ;et conservons l’autre en sortie (ici, Onous affichons ici le caractère correspondant et la destruction de l’atome - dans le programme complet, nous continuons à utiliser l’atome à la place).

itoa

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

Bien entendu, nous devons également reconvertir le résultat en chaîne et l’imprimer. C'est ce que cette partie est pour. Cela suppose que l’entrée n’arrive pas avant la case 10, mais dans le programme complet qui est facilement donné. Ce bit peut être trouvé au bas du programme complet.

Ce code introduit un nouveau composant très puissant dans la fission: la pile K. La pile est initialement vide. Lorsqu'un atome d'énergie non négative frappe la pile, l'atome est simplement poussé sur la pile. Lorsqu'un atome d'énergie négative frappe la pile, sa masse et son énergie sont remplacées par l'atome situé en haut de la pile (qui est ainsi éclaté). Si la pile est vide, le sens de l'atome est inversé et son énergie devient positive (c'est-à-dire multipliée par -1).

Ok, revenons au code actuel. L'idée de l' itoaextrait de code est de prendre à plusieurs reprises l'entrée modulo 10 pour trouver le prochain chiffre tout en divisant l'entier par 10 pour la prochaine itération. Cela donnera tous les chiffres dans l'ordre inverse (du moins significatif au plus significatif). Pour fixer cet ordre, nous plaçons tous les chiffres sur une pile et, à la fin, les supprimons un par un pour les imprimer.

La moitié supérieure du code fait le calcul du chiffre: le Lavec les plus donne un 10 que nous clonons et introduisons dans un réacteur à fission et un réacteur à fusion afin que nous puissions diviser et multiplier par 10. La boucle commence essentiellement après le [dans le coin supérieur gauche . La valeur actuelle est divisée: une copie est divisée par 10, puis multipliée par 10 et stockée dans un réacteur de fission, qui est ensuite frappé par l'autre copie au sommet. Cela calcule i % 10comme i - ((i/10) * 10). Notez également que la Adivision du résultat intermédiaire après la division et avant la multiplication, de sorte que nous pouvons alimenter i / 10à la prochaine itération.

Il %abandonne la boucle une fois que la variable d'itération atteint 0. Puisqu'il s'agit plus ou moins d'une boucle do-while, ce code pourrait même fonctionner pour l'impression 0(sans créer de zéros non plus, sinon). Une fois que nous avons quitté la boucle, nous voulons vider la pile et imprimer les chiffres. Sest le contraire de Z, c’est donc un commutateur qui va dévier un atome entrant avec une énergie non positive de 90 degrés vers la droite. Ainsi, l'atome se déplace réellement sur le bord du Sdroit au premier Kpour faire apparaître un chiffre (notez le ~qui garantit que l'atome entrant a de l'énergie -1). Ce chiffre est incrémenté de 48pour obtenir le code ASCII du caractère correspondant. La Afractionne le chiffre pour imprimer une copie avec!et réintroduisez l'autre copie dans le Yréacteur pour le chiffre suivant. La copie imprimée est utilisée comme déclencheur suivant pour la pile (notez que les miroirs l’envoient également autour du bord pour la frapper Mde la gauche).

Lorsque la pile est vide, la Kvolonté reflète l'atome et convertit son énergie en une +1telle manière qu'il passe directement à travers le S. Naffiche une nouvelle ligne (juste parce que c'est soigné :)). Et puis, l’atome passe de R'0nouveau pour se retrouver dans le côté de la Y. Puisqu'il n'y a plus d'atomes autour, cela ne sera jamais publié et le programme se termine.

Calcul du nombre de fission: le cadre

Passons à la viande du programme. Le code est essentiellement un portage de mon implémentation de référence 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)]
]

divest le nombre d'entiers dans la partition maximale.

Les principales différences sont que nous ne pouvons pas traiter les valeurs semi-entières dans Fission, je fais donc beaucoup de choses multipliées par deux et qu'il n'y a pas de récursivité dans Fission. Pour contourner ce problème, j'inspire tous les entiers d'une partition d'une file d'attente pour qu'ils soient traités ultérieurement. Pour chaque numéro que nous traitons, nous incrémentons un compteur de un et une fois la file d'attente vide, nous libérons le compteur et l'envoyons pour qu'il soit imprimé. (Une file d'attente Qfonctionne exactement comme Kdans l'ordre FIFO.)

Voici un cadre pour ce concept:

                      +--- 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

Les nouveaux composants les plus importants sont les chiffres. Ce sont des téléporteurs. Tous les téléporteurs avec le même chiffre vont ensemble. Lorsqu'un atome frappe un téléporteur, il déplace immédiatement le téléporteur suivant dans le même groupe, où le prochain est déterminé dans l'ordre habituel de gauche à droite et de haut en bas. Celles-ci ne sont pas nécessaires, mais aident à la mise en page (et donc au golf un peu). Il y a aussi Xqui duplique simplement un atome, en envoyant une copie tout droit et l’autre en arrière.

À ce stade, vous pourrez peut-être régler vous-même la plupart des éléments du cadre. Le coin supérieur gauche contient la file de valeurs à traiter et en libère une nà la fois. Une copie de nest téléportée vers le bas parce que nous en avons besoin lors du calcul de la plage, l'autre copie va dans le bloc en haut qui calcule div(c'est de loin la plus grande section du code). Une fois divcalculé, il est dupliqué - une copie incrémente un compteur dans le coin supérieur droit, qui est stocké dans K. L'autre copie est téléportée au bas. Si divc'était le cas 1, nous le renvoyons immédiatement vers le haut et l'utilisons comme déclencheur de la prochaine itération, sans mettre en attente de nouvelles valeurs. Sinon, nous utilisons divetn dans la section du bas pour générer la nouvelle plage (c’est-à-dire un flux d’atomes avec les masses correspondantes qui sont ensuite placées dans la file d’attente), puis relâchez un nouveau déclencheur une fois la plage complétée.

Une fois la file d'attente vide, le déclencheur sera reflété, passant directement à travers Set réapparaissant dans le coin supérieur droit, où il relâche le compteur (le résultat final) A, qui est ensuite téléporté vers itoavia 8.

Calcul du nombre de fission: le corps de boucle

Il ne reste donc que les deux sections pour calculer divet générer la plage. L'informatique divest cette partie:

 ;    
 {+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/\/

Vous en avez probablement déjà vu assez pour résoudre ce problème vous-même avec un peu de patience. La décomposition en haut niveau est la suivante: Les 12 premières colonnes environ génèrent un flux de diviseurs de 2n. Les 10 prochaines colonnes filtrent celles qui ne satisfont pas OddQ@# == IntegerQ[n/#]. Les 8 prochaines colonnes filtrent celles qui ne satisfont pas n/# > (# - 1)/2). Enfin, nous plaçons tous les diviseurs valides sur une pile et une fois que nous avons terminé, nous viderons toute la pile dans un réacteur à fusion (en écrasant tout le dernier / le plus grand diviseur), puis nous relâcherons le résultat, puis nous éliminerons son énergie -zéro de vérifier l'inégalité).

Il y a beaucoup de chemins fous là-dedans qui ne font rien vraiment. De manière prédominante, la \/\/\/\/folie au sommet (les 5s en font également partie) et un chemin autour du bas (qui passe par les 7s). Je devais les ajouter pour faire face à de mauvaises conditions de course. La fission pourrait utiliser un composant de retard ...

Le code qui génère la nouvelle plage à partir de net divest-ce:

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

Nous calculons d’abord n/div - (div + 1)/2(les deux termes pondérés, ce qui donne le même résultat) et enregistrons pour plus tard. Ensuite, nous générons une plage allant de divbas à 1et ajoutons la valeur stockée à chacun d’eux.

Il y a deux nouveaux modèles communs dans ces deux, que je dois mentionner: l' un est SXou ZXfrappé par le bas (ou versions pivotés). C'est un bon moyen de dupliquer un atome si vous voulez qu'une copie aille droit devant vous (dans la mesure où rediriger les sorties d'un réacteur à fusion peut parfois être fastidieux). Le Sou Zfait pivoter l'atome dans le X, puis fait pivoter la copie mise en miroir dans la direction de propagation d'origine.

L'autre motif est

[K
\A --> output

Si nous stockons une valeur, Knous pouvons la récupérer à plusieurs reprises en frappant Kavec une énergie négative du haut. Les Adoublons de la valeur qui nous intéresse et renvoient quelle copie sur la pile pour la prochaine fois que nous en avons besoin.

Eh bien, c'était assez long ... mais si vous avez vraiment traversé cela, j'espère que vous avez eu l'idée que Fission est i̟nç̮̩red̙ibl̶̪̙̮̥̮y̶̠̠͎̺̪̙̮̥̮͍͍͍̱̦̰͍͜


1
Now with 100% fewer scrollbars.donc tu dis .. et c'est toujours à suivre ...
Optimizer

13
Encore plus lisibles que la plupart des codes de nos développeurs juniors.
CorsiKa

En tant que créateur de Fission, même si je n’ai pas encore écrit un programme aussi volumineux! Je suis impressionné! Votre explication est spectaculaire et pourrait certainement servir de tutoriel pour la langue.
C0deH4cker

En outre, la dernière ligne de votre réponse ressemble un peu à un programme de fission;)
C0deH4cker

12

CJam, 42 41 octets

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

Une première traversée en largeur simple et une condition d'arrêt de niveau suivant vide.

Comment ça marche :

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

Essayez-le en ligne ici


Cela peut probablement être joué à environ 35 octets. J'essaie de comprendre comment ..
Optimiseur

10

Python 3, 112 octets

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 octets enregistrés grâce à @FryAmTheEggman.

Explication à venir plus tard ...

En prime: chaque puissance de 2 a un nombre de fission de 1. Cela est dû au fait que la somme d'une séquence de longueur égale est toujours la somme des deux nombres du milieu, ce qui est impair, multiplié par la moitié de la longueur de la séquence, qui est un entier. . La somme d'une séquence de longueur impaire est le nombre du milieu multiplié par la longueur de la séquence, ce qui est impair. Ainsi, une puissance de 2 n'ayant pas de diviseur impair, elle ne peut être exprimée que par la somme de celle-ci.


2 + 3 + 4 + 5 = 14 ce qui n'est pas impair. Votre argument pour les séquences de longueur égale devrait être remplacé par "la somme d'une séquence de longueur égale est la somme des deux nombres du milieu, ce qui est impair, multiplié par la moitié de la longueur". Le reste de votre déclaration reste inchangé.
Bruno Le Floch

@BrunoLeFloch Merci! Corrigée.
randomra

Votre titre ne devrait-il pas refléter les améliorations? c'est-à-dire <strike> 117 </ strike> <strike> 113 </ strike> 112
corsiKa

@corsiKa Je ne le fais généralement que pour des améliorations majeures. Sinon, il y aurait trop de numéros barrés.
randomra

8

Python 2, 111 102 97 octets

Un peu lisible:

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)

Pas si lisible:

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)))))

Les deux 97 octets.

best le nmoins le (a-1)thnombre triangulaire. Si b % a == 0, alors nest la somme des anombres consécutifs à partir de b.


8
J'avais l'habitude de considérer Python comme un langage généralement lisible jusqu'à ce que je rejoigne PPCG.
Alex A.

Je pense que vous devez améliorer votre définition de lisible ..: P
Optimizer

Python 2 ne permet pas 1else. Seule la 2ème solution fonctionne. Ce n'est pas avant Python 3 que l' elseon peut immédiatement suivre un numéro.
mbomb007

@ mbomb007 À ma connaissance, cela fonctionne très bien à partir de la 2.7.8
Sp3000

Ok, j'utilisais 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}))

Moins joué au 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'idée est de tester des suites potentielles d'entiers consécutifs qui totalisent n. L'exécution est stockée directement directement en tant qu'ensemble Rplutôt que via ses points d'extrémité.

Nous vérifions comment la somme de la série en cours se compare à la somme souhaitée nvia leur différence.

  • Si la somme est trop importante, nous coupons le plus petit élément de la série.
  • Si la somme est trop petite, nous étendons la course en augmentant son maximum de 1.
  • Si la somme est correcte, nous recurse, mappons fsur l'exécution, la somme et ajoutons 1 pour le nœud actuel. Si la course est {n}, nous avons essayé toutes les sommes possibles non triviales, arrêtez la récursion en supprimant d' nabord.

Merci à Sp3000 pour la sauvegarde de 3 caractères.


7

Python 2, 85

Je suis très fier de cette réponse car cela prend déjà des dizaines de secondes pour n = 9 et 5-10 minutes pour n = 10. En code golf, ceci est considéré comme un attribut souhaitable d'un programme.

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)]

Il existe également une version en court-circuit qui prend moins de temps et utilise le même nombre d'octets:

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 

Cela peut être plus rapide, mais au moins il dépasse la limite de récursion par défaut une fois que n dépasse légèrement 40.

L'idée est de faire une force brute de recherche de nombres aet dtelle que a + a+1 + ... + a+d == n, sur des valeurs entre 1 et n. La f(n,a+d/n,d%n+1)branche de la récursion passe en boucle par les (a, d)paires. Dans le cas où l'égalité est satisfaite, je parviens à éviter un map(range(...))appel coûteux en scindant en deux branches, quelle que soit la durée de la séquence. Les nombres à a+1travers dsont regroupées en un seul appel fen réglant le aparamètre de sorte que ne peut pas être utilisé d' une manière différente de diviser la séquence.


Comment cela marche-t-il?
xnor

"Je suis très fier de cette réponse car cela prend déjà des dizaines de secondes pour n = 9 et 5-10 minutes pour n = 10. En code golf, cela est considéré comme un attribut souhaitable d'un programme." +1 seulement pour ça.
Soham Chowdhury

6

Haskell, 76 69 octets

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

Usage:

*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]

Comment ça fonctionne:

[  [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 octets

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

.
1

Prend l’entrée et imprime la sortie unaire.

Vous pouvez mettre chaque ligne dans un seul fichier ou exécuter le code tel quel avec l' -sindicateur. Par exemple:

> echo -n 1111111|retina -s fission
11111

Explication:

  • Nous conservons une liste délimitée par des virgules des nombres à scinder.
  • Pour chaque nombre, nous prenons la plus petite valeur de départ qui peut créer un fractionnement valide et le délimiter du reste avec un point-virgule.
  • S'il y a un point-virgule dans un nombre, nous le changeons en virgule et délimitons la partie du nombre correctement dimensionnée (longueur de l'élément précédent + 1).
  • Nous répétons les étapes 2 et 3 jusqu'à ce que des changements se produisent.
  • Nous obtenons une virgule pour chaque feuille et un point-virgule pour chaque nœud interne, plus une virgule supplémentaire car nous avons commencé par deux virgules. Nous supprimons donc une virgule et les parties des nombres 1et convertissons le reste en 1.

Les états de la chaîne tout au long du processus avec entrée 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

Merci beaucoup pour @MartinButtner pour l'aide dans le chat!


3

CJam (43 bytes)

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

Démo en ligne

Je suis sûr que je manque quelques astuces avec les boucles avancées, mais cela exploite parfaitement la propriété CJam (qui m’avait déjà ennuyée), %les résultats restent dans la pile sur la pile et peuvent donc être consultés $avec décalage négatif.


Je regarderai de plus près demain, mais pour commencer, vous n'en avez pas besoin ,au début. /et %quelques autres transforment implicitement les nombres en plages.
Martin Ender

,_m*peut être remplacé par 2m*. La formule de progression arithmétique peut être remplacée par ~,>:Y_,+:+et ~\,>0\ devient !Y. Enfin, si vous utilisez {}#au lieu de {}=, vous n’avez pas besoin de l’ )après X. Rassembler tout cela:ri{):X2m*{~,>:Y_,+:+X=}#!Y{~$+}/)}%W=
Dennis

2

Go, 133 octets

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 Σ}

C'est mon premier code de golf, désolé si j'ai commis des erreurs.

Ceci utilise l'idée que la "composition" fissile peut également être vue comme une différence entre deux séquences d'entiers ordonnés. Par exemple, prenons la "composition" fissile pour le nombre 13. Il est 6,7. Mais cela peut être vu comme la somme des nombres entiers 1 ... 7 moins la somme des nombres entiers 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

Rappelez-vous la formule des jours d'école de Gauss, somme 1 ... n = (n ^ 2 + n) / 2. Donc, pour trouver une composition de nombres entiers séquentiels pour un n donné, nous pourrions aussi dire que nous cherchons des «points finaux» p et q le long de la plage 1 ... n, de sorte que (p ^ 2 + p) / 2 - ( q ^ 2 + q) / 2 = n. Dans l'exemple ci-dessus, nous aurions recherché les «points finaux» 5 et 7 car 7 ^ 2 + 7 = 56/2, 5 ^ 2 + 5 = 30/2, 56 / 2-30 / 2 = 28-15 = 13.

Maintenant, il existe de nombreuses manières possibles de composer un nombre, comme l'a noté Martin 9 = 2 + 3 + 4 mais aussi 4 + 5. Mais il semble évident que la séquence de départ "la plus basse" sera aussi la plus longue, car elle prend plus de temps. petits nombres pour résumer à un grand nombre que les nombres de taille moyenne. (Je n'ai malheureusement aucune preuve)

Donc, pour trouver la composition de 9, testez chaque 'paire de points finaux', p et q, en effectuant une itération distincte de 0 à 9, et testez si p ^ p + p / 2 - q ^ 2 + q / 2 = 9. Ou, plus simplement, multipliez l'équation par 2 pour éviter les problèmes de division liés à l'arrondissement et conservez tous les calculs en nombres entiers. On cherche alors p et q tels que (p ^ p + p) - (q ^ q + q) = 9 * 2. La première correspondance que nous trouverons sera les critères de composition Fissile car, comme indiqué, le groupe de nombres le plus bas sera également le plus long, et nous recherchons des valeurs faibles à élevées (0 à 9). Nous sortons de la boucle dès que nous trouvons un match.

Maintenant, la fonction récursive trouve ces «points finaux fissiles» p et q pour le n donné, puis se rappelle elle-même pour chacun des «enfants» de l'arbre de p à q. Pour 9, il trouverait 1 et 4, (20-2 = 18), puis il se rappellera le 2, le 3 et le 4 en faisant la somme des résultats. Pour des nombres tels que 4, il ne trouve simplement jamais de correspondance et renvoie donc «1». C’est peut-être une solution, mais c’est comme mon troisième programme Go, et je ne suis pas un expert en récursivité.

Merci d'avoir lu.


Bon travail! Mais pourquoi les noms de fonction / variable Unicode. Cela coûte des octets inutiles et vous auriez sûrement pu utiliser une lettre normale?
Martin Ender

Merci pour votre aimable commentaire. Mais je me suis demandé pourquoi ne pas compter les caractères au lieu des octets :)
don bright

Parce que ce sont les règles par défaut ici. :) La raison pour laquelle nous comptons habituellement des octets et non des caractères est parce que sinon, cela se produit , ce qui est peu amusant pour toutes les parties concernées. ;) (Cela dit, tout auteur de défi est libre de spécifier le décompte en caractères au lieu d'octets, mais ce n'est pas spécifiquement le cas.)
Martin Ender

1

CJam, 40 35 33 octets

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

Merci à @Optimizer pour avoir suggéré few, qui a sauvé 2 octets.

Essayez-le en ligne dans l' interprète CJam .

Comment ça fonctionne

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.

Si vous combinez ma première moitié avec votre seconde moitié, vous obtenez 34:ri{_,:)_few:+W%{1b1$=}=|{F}*}:F~],
Optimiseur,

@Optimizer: Nice. Cela permet une amélioration supplémentaire. Merci!
Dennis
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.