Construire un arbre diviseur esthétique

43

Un arbre diviseur esthétiquement plaisant est une arborescence de diviseurs d’entrée nqui, pour tout nombre composé m, a deux nœuds enfants qui sont la paire de diviseurs la plus proche de la racine carrée de m. Le noeud de gauche doit être le plus petit diviseur de met le noeud de droite doit être le plus grand diviseur de m. Un nombre premier dans l'arborescence ne doit pas avoir de nœud enfant. Votre arbre peut être sous la forme d'un texte ou d'une image. Les règles pour la sortie de texte d'art sont les suivantes.

Règles d'espacement

Pour espacer les nœuds de l’arbre, nous appliquons les règles suivantes:

  • Les nœuds situés à une profondeur donnée à partir de la racine doivent tous se trouver sur la même ligne de texte dans la sortie.
  / \ NE PAS / \  
 / \ / 3
2 3 2
  • Pour les nœuds de gauche, la branche entrante doit se trouver en haut à droite si le nœud est un nombre à un chiffre, sinon juste au-dessus du dernier chiffre. Exemple:
 / ET /
3 720
  • Pour les nœuds de droite, la branche entrante doit être en haut à gauche si le nœud est un nombre à un chiffre, sinon juste au-dessus du premier chiffre. Exemple:
\ ET \
 7 243
  • Pour les branches gauche sortantes, la branche doit commencer par un espace à gauche du nombre. Exemple:
  275
 /
11
  • Pour les branches droites sortantes, la branche doit commencer par un espace à droite du nombre. Exemple:
275
   \
   25
  • Deux nœuds situés au même niveau de l’arbre doivent être séparés par au moins deux espaces. Dans le même temps, deux sous-arbres situés au même niveau de l’arborescence doivent être espacés le moins possible.
Cet arbre ne fonctionne pas car les ** sous-arbres ** sont trop proches.

        504           
       / \          
      / \         
     / \        
    / \       
   21 24     
  / \. / \    
 / \. / \   
3 7. 4 6  
        . / \ / \
        .2 2 2 3

Bien que cet arbre ait suffisamment d’espace entre ses branches.

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • Si deux sous-arbres sont trop proches l'un de l'autre dans un arbre, vous pouvez les séparer en ajoutant une autre rangée de branches /\à l'arbre au-dessus des parents.
   441                              
  / \ La dernière ligne n’est pas encore renseignée et nous sommes déjà à court d’espace.
 21 21
/ \ / \

Ajouter une autre rangée de branches

     441                              
    / \ Presque, mais le 7 et le 3 sont trop rapprochés.
   / \ Une autre ligne devrait le faire.
  21 21
 / \ / \
3 7 3 7

Ajouter une autre rangée de branches

      441
     / \ Et nous avons fini.
    / \
   / \
  21 21
 / \ / \
3 7 3 7

Exemples

Par exemple, l’arbre diviseur de 24 ressemblera à ceci:

     24
    /  \
   /    \
  4      6
 / \    / \
2   2  2   3

4 et 6 sont la paire de diviseurs la plus proche de la racine carrée de 24. 4 est à gauche, car elle est plus petite. Sur la ligne suivante, le nombre 2 à gauche de 3, car il est plus petit.

L'arbre diviseur pour 63 devrait ressembler à:

  63        and NOT like this        63
 /  \                               /  \
7    9                             3   21
    / \                               /  \
   3   3                             7    3

Dans l'arborescence incorrecte, 3 et 21 ne sont pas la paire de diviseurs la plus proche de la racine carrée de 63, et 3 et 7 ne sont pas triés correctement. Le placement de la branche sur le 21 est correct, cependant.

Pour 42, vous devriez avoir:

    42      and NOT        42
   /  \                   /  \
  6    7                 21   2
 / \                    /  \
2   3                  3    7

Jetons un coup d'œil à 720. Notez qu'il nous faut cinq niveaux de branches 720pour que les sous-arbres 24et 30soient correctement espacés. En outre, notez que 24et 30ont deux niveaux de branches parce que 4et 6ont des nœuds enfants qui nécessitent un espacement correct et que les nœuds enfants de 30doivent être au même niveau que les nœuds enfants de 24.

           720
          /   \
         /     \
        /       \
       /         \
      /           \ 
     24           30
    /  \         /  \
   /    \       /    \
  4      6     5      6
 / \    / \          / \
2   2  2   3        2   3

Le défi

  • Votre tâche consiste à créer un arbre diviseur esthétiquement plaisant, correctement espacé n, dans lequel nun entier positif est supérieur à 1.
  • Votre sortie peut contenir des espaces de début et de fin et des nouvelles lignes de début et de fin, mais doit autrement être conforme aux règles d'espacement données ci-dessus.
  • Votre sortie est autorisée à être: du texte, une image (autres formats à ajouter, si nécessaire).
  • Pour les images, assurez-vous que les nœuds de votre arborescence sont bien espacés et que les nœuds situés à la même hauteur dans l'arborescence ont la même hauteur dans l'image.
  • C'est du code golf. Le plus petit nombre d'octets (ou équivalent) gagne.

Nous remercions Stewie Griffin pour avoir pensé à cette idée, ainsi que Peter Taylor, Martin Ender, Mego et Eᴀsᴀ Iʀᴋ pour leur aide dans la réécriture de la spécification. Comme d'habitude, toute suggestion ou correction est très appréciée. Bonne chance et bon golf!

Plus de cas de test:

2

  4
 / \
2   2

    20
   /  \
  4    5
 / \
2   2

  323
 /   \
17   19

                        362880
                       /      \
                      /        \
                     /          \
                    /            \
                   /              \
                  /                \
                 /                  \
                /                    \
               /                      \
              /                        \
            576                        630
           /   \                      /   \
          /     \                    /     \
         /       \                  /       \
        /         \                /         \
       /           \              /           \
      /             \            /             \
     24             24          21             30
    /  \           /  \        /  \           /  \
   /    \         /    \      /    \         /    \
  4      6       4      6    3      7       5      6
 / \    / \     / \    / \                        / \
2   2  2   3   2   2  2   3                      2   3

              1286250
             /       \
            /         \
           /           \
          /             \
         /               \
      1050               1225
     /    \             /    \
    /      \           /      \
   /        \         /        \
  30        35       35        35
 /  \      /  \     /  \      /  \
5    6    5    7   5    7    5    7
    / \
   2   3
Sherlock9
la source
Merci pour ce défi. Je peux maintenant visualiser ces choses sans les dessiner à chaque fois: D
Conor O'Brien
L'arbre doit-il ressembler aux exemples ou puis-je utiliser la fonction intégrée Mathematica? Cela ressemble à ceci , mais avec la factorisation.
JungHwan Min
@JHM Je savais que j'aurais dû conserver l' étiquette de sortie graphique . Oui, vous pouvez utiliser cette fonction intégrée. Je vais éditer le défi.
Sherlock9

Réponses:

29

Python 2 , 711 651 575 559 554 547 539 540 530 522 octets

Après quatre mois d’essais pour écrire cette réponse, heurtant un mur, le laissant pendant une semaine, rincez, répétez, j’ai enfin terminé une réponse artistique ASCII appropriée pour ce défi. Il ne reste plus que le golf. Les suggestions de golf sont donc les bienvenues. Essayez-le en ligne!

Golfs: -60 octets pour renommer certaines fonctions fréquemment utilisées et modifier le mode de restitution du résultat. -73 octets pour changer la façon dont les hauteurs des sous-arbres sont vérifiées, comment les variables d'espacement sont calculées et comment le résultat est renvoyé. -3 octets du isdigit()remplacement de FlipTack . -16 octets jouant encore ce isdigit()remplacement et remplaçant "" par " E. -5 octets provenant d'améliorations mineures et passant de Python 3 à Python 2. -7 octets consistant à modifier la façon dont le résultat est renvoyé. -8 octets d'un petit changement dans le mode de Adéfinition, en modifiant ainsi T, et en ajoutant W, en partant de l'hypothèse que tout sous-arbre ayant au moins une branche plus longue que son homologue est nécessairement plus long que son homologue , en supprimantQtout à fait, et éditer comment le résultat est retourné. -10 octets à partir de utiliser A<10au lieu de L(S(A))<2pour Aet B. -8 octets de changer la valeur par défaut Hà [0]car le code évite le problème des arguments par défaut mutables en ne muter H, changer la façon dont qest définie en utilisant au (B>9)lieu de 1-(B<10), en supprimant ptout à fait, et la création Fen remplacement p+q-M.

Correctifs: L' hypothèse était fausse, le contre-exemple dans 11**9 = 2357947691. +1 octet

G=range;L=len;E=" "
def t(n,H=[0]):
 A=max(z*(n%z<1)for z in G(1,int(n**.5)+1));B=n/A;Z=str(n);M=L(Z)
 if A<2:return[Z]
 T=max([i for i in G(L(w))if"/"not in w[i]]for w in(t(A),t(B)));V=H[1:]or[T[k+1]-T[k]-1for k in G(L(T)-1)];x=t(A,V);y=t(B,V);P=x[0].rindex(str(A)[-1])+(A<10);q=y[0].index(str(B)[0])+(B>9);F=L(x[0])-P+q-M;h=H[0]or(F+M%2+2)/2or 1;return[E*(P+J)+(J<h and"/"+E*(2*h+M-2*J-2)+"\\"or Z)+E*(L(y[0])-q+J)for J in G(h,-1,-1)]+[(E*(2*h-F)).join(I<L(w)and w[I]or E*L(w[0])for w in(x,y))for I in G(max(L(x),L(y)))]

Explication

Toute la fonction peut être réduite à environ quatre étapes:

  1. Déterminez le plus grand diviseur de n, Aet B.
  2. Créez les sous-arbres de Aand B, en redessinant au besoin.
  3. Déterminez le nombre d'espaces devant être placés entre les sous-arbres.
  4. Dessinez et renvoyez le nouvel arbre diviseur.

Je vais passer en revue chaque étape dans l'ordre.

Étape 1. C’est franchement l’étape la plus facile. Vérifiez chaque nombre zentre 1 et la racine carrée pour connaître la divisibilité en net saisissez le plus grand zet n//zcelui qui correspond. Retour juste str(n)si nest premier (ou A==1ou B==n)

Étape 2. Dessinez les sous-arbres de Aet Bet obtenez le nombre de /\branches entre les nœuds des sous-arbres. Pour ce faire, nous obtenons les indices de chaque étape comportant des chiffres, obtenons les premières différences d'indices et soustrayons 1 à nouveau. Une fois que nous avons les hauteurs, nous les comparons pour obtenir les plus grandes, et redessinons les sous-arbres avec les nouvelles hauteurs.

Je soupçonne sournoisement que le sous-arbre qui est le plus grand en général a toujours des branches aussi longues ou égales aux branches du sous-arbre le plus court, et je peux l'utiliser pour jouer au code, mais je n'ai pas encore de preuve de cela. Contre-exemple dans 11**9 = 2357947691.

Étape 3. Cette étape est ce qui a pris des mois pour écrire. L'étape 2 a pris quelques jours pour écrire et déboguer, mais il a fallu une éternité pour trouver les bonnes formules d'espacement. Je verrai si je peux condenser ce que j'ai calculé en quelques paragraphes. Notez que certains des codes de cette explication ont été depuis sortis du code réel.

Tout d' abord, p, q, h, P, Q, set M. pest le nombre de caractères de la fin de la branche gauche /à la droite du sous-arbre gauche. qest le nombre de caractères de l'extrémité gauche de la sous-arborescence droite à la fin de la branche droite /. hest le nombre de branches entre la racine et les sous-arbres. Pet ne Qsont que les inverses de pet qet sont utiles pour placer les espaces autour des /\branches jusqu’à la racine n. sest le nombre d'espaces ajoutés entre les deux sous-arbres. Mest le plus simple; c'est la longueur de n. Mettez graphiquement:

            M
           ---
           720           
 |        /   \          
 |       /     \         
h|      /       \        
 |     /         \       
 |    /           \      
   P    p    s   q   Q   
------______---____------
     24           30     
    /  \         /  \    
   /    \       /    \   
  4      6     5      6  
 / \    / \          / \ 
2   2  2   3        2   3

La formule permettant de déterminer pest p = len(x[0]) - x[0].rindex(str(A)[-1]) - (A<10)la suivante:, la longueur moins l'indice zéro du dernier caractère de A moins la correction pour les chiffres à un chiffre A.

La formule de détermination qest q = y[0].index(str(B)[0]) + (B>9)la suivante:, l’index du premier caractère de B, plus une correction pour index-zéro, moins une correction pour un seul chiffre B(combinés en une correction pour plusieurs chiffres B).

La formule de calcul hest la suivante: h = H[0] or (p+q+M%2+2-M)//2 or 1. Soit nous prenons un élément prédéfini, Hce qui signifie que nous redessinons l’arbre, nous utilisons (from_the_left + from_the_right + parity_space + 2 - len(root)) // 2), soit nous utilisons le nombre minimum de niveaux de branche, 1.

La formule de calcul sest la suivante: s = 2*h+M-p-q. Nous soustrayons pet qdu nombre d'espaces entre les branches de la racine au plus large 2*h + M.

Étape 4. Et finalement, nous mettons tout cela ensemble. D' abord , nous faisons la racine, [" "*(P+h)+Z+" "*(Q+h)]puis nous mettons dans les branches jusqu'aux sous - arbres, [" "*(P+J)+"/"+" "*(2*h+M-2*J-2)+"\\"+" "*(Q+J)for J in G(h)][::-1]et enfin nous mettons dans notre sous - arbres espacés régulièrement, [(" "*(2*h+M-p-q)).join([(I<L(w)and w[I]or" "*L(w[0]))for w in(x,y)])for I in G(max(L(x),L(y)))].

Et voilà! Nous avons nous-mêmes un arbre diviseur esthétique!

Ungolfing:

def tree(n, H=[0]):
    A = max(z for z in range(1, int(n**.5)+1) if n%z<1)
    B = n/A
    Z = str(n)
    M = len(Z)
    if A < 2:
        return [Z]

    # redraw the tree so that all of the numbers are on the same rows
    x = tree(A)
    y = tree(B)
    for W in [x, y]:
        T = [i for i in range(len(W)) if "/" not in W[i]]
    V = H[1:] or [T[k+1]-T[k]-1 for k in range(len(T)-1)]
    x = tree(A, V)
    y = tree(B, V)

    # get the height of the root from the two trees
    P = x[0].rindex(str(A)[-1]) + (A < 10)
    p = len(x[0]) - P
    q = y[0].index(str(B)[0]) + (B > 9)
    Q = len(y[0]) - q
    h = hs[0] or (p+q+M%2+2-M)/2 or 1

    # and now to put the root down
    R = []
    s = 2*h+M-p-q
    for I in range(max(len(x),len(y))):
        c = I<len(x) and x[I] or " "*len(x[0])
        d = I<len(y) and y[I] or " "*len(y[0])
        R += c + " "*s + d,
    for J in range(h, -1, -1):
        if J<h:
            C = "/" + " "*(2*h+M-2*J-2) + "\\"
        else:
            C = Z
        R += [" "*(P+J) + C + " "*(Q+J)]
    return R
Sherlock9
la source
Votre isdigitchèque pourrait être '/'<x[i].strip()[0]<':'?
FlipTack
14

Mathematica, 96 86 81 79 78 octets

Merci @MartinEnder pour 2 octets.

TreeForm[If[PrimeQ@#,#,#0/@(#2[#,#2/#]&[Max@Nearest[Divisors@#,#^.5],#])]&@#]&

La sortie ressemble à ceci:

entrez la description de l'image ici

Explication

Max@Nearest[Divisors@#,#^.5]

Générez la liste des diviseurs de l'entrée. Trouvez l'élément le plus proche de la racine carrée de l'entrée. ( Maxsert à aplatir la sortie)

#2[#,#2/#]&

Recherchez l’autre diviseur en divisant l’entrée par le diviseur trouvé ci-dessus, appliquez l’entrée comme tête du résultat.

#0/@

Répétez le processus.

If[PrimeQ@#,#, ... ]

Si l'entrée est primordiale, ne faites rien.

TreeForm

Formatez la sortie.

Edit: Une version plus esthétique (258 octets)

TreeForm[#/.{a_,_,_}:>a,VertexRenderingFunction->(#2~Text~#&),VertexCoordinateRules->Cases[#,{_,_},Infinity,Heads->True]]&@(If[PrimeQ@#,{##},{##}@@#0@@@({{#,#3-#4{1,√3}/2,#4/2},{#2/#,#3-#4{-1,√3}/2,#4/2}}&[Max@Nearest[Divisors@#,√#],##])]&[#,{0,0},1])&

La sortie ressemble à ceci:

entrez la description de l'image ici

JungHwan Min
la source
3
Sqrt@#-> #^.5(bien entendu, vous ne pouvez pas utiliser la notation infix pour, Nearestmais vous pouvez utiliser Max@).
Martin Ender
5
Il suit les règles, mais cet arbre est loin d'être esthétique xD
Beta Decay
2
La beauté est dans l'oeil du spectateur :)
Nelson,
1
Je ne suis pas sûr que cela soit valide. Contrairement aux exemples, les nœuds de chaque ligne ne sont pas espacés de manière égale. De plus, les lignes ne se connectent pas au bon chiffre.
Mego
1
@ Mego Eh bien, OP a dit que c'était valide.
R. Kap
3

Charbon de bois , 302 octets

≔⟦⟦N⁰θ⁰¦⁰⟧⟧θFθ«≔§ι⁰ζ≔⌈E…·²Xζ·⁵∧¬﹪ζκκη¿η«F⟦η÷ζη⟧«≔⟦κ⊕§ι¹Iκ⁰¦⁰⟧κ⊞ικ⊞θκ»⊞υι»»≔…⁰⌈Eθ§ι¹ηF⮌竧≔ηι⊕⌈⟦⁰⌈Eυ∧⁼§κ¹ι÷Σ⟦¹§§κ⁵¦⁴‹⁹§§κ⁵¦⁰§§κ⁶¦³‹⁹§§κ⁶¦⁰±L§κ²⟧²⟧FυF²§≔κ⁺³λ⁺⁺§ηι∨⊖L§§κ⁺⁵벦¹§§κ⁺⁵λ⁺³λ»Fυ«§≔§ι⁵¦³⁻⁻§ι³§η§ι¹∨⊖L§§ι⁵¦²¦¹§≔§ι⁶¦³⁻⁺⁺§ι³L§ι²§η§ι¹‹⁹§§ι⁶¦⁰»F⊕Lη«Fθ«F⁼§κ¹ι«←⸿M§κ³→F‹⁵Lκ«↙P↙§ηι↗»§κ²↓F‹⁵LκP↘§ηι»»M⊕§ηι↓

Essayez-le en ligne! Le lien est vers la version verbeuse du code. Comme la version détaillée est très détaillée, il utilise une translittération JavaScript du principal algorithme:

u = []; // predefined variable, used as list of branches
q = [[+s, 0, s, 0, 0]]; // list of nodes starts with the root.
for (i of q) { // iterate nodes, includes new nodes
    z = i[0]; // get node value
    h = Math.max(...[...Array(Math.floor(z ** 0.5) + 1).keys()].slice(2).filter(
        k => z % k < 1)); // find largest factor not above square root
    if (h) {
        for (k of [h, z / h]) {
            k = [k, i[1] + 1, `${k}`, 0, 0]; // create child node
            i.push(k); // add each child to parent (indices 5 and 6)
            q.push(k); // and to master nodelist
        }
        u.push(i);
    }
}
h = new Array(Math.max(...q.map(i => i[1]))); // list of branch heights
for (i = h.length; i --> 0; ) {
    // find branch height needed to space immediate children apart at this depth
    h[i] = 1 + Math.max(...u.map(k => k[1] == j && // filter on depth
        1 + k[5][3] + (k[5][0] > 9) + k[6][2] + (k[6][0] > 9) - k[2].length
        >> 1)); // current overlap, halved, rounded up
    // calculate the new margins on all the nodes
    for (k of u) {
        k[3] = h[i] + (k[5][2].length - 1 || 1) + k[5][3]; // left
        k[4] = h[i] + (k[6][2].length - 1 || 1) + k[6][4]; // right
    }
}
// calculate the absolute left margin of all the nodes under the root
for (i of u) {
    i[5][3] = i[3] - h[i[1]] - (i[5][2].length - 1 || 1);
    i[6][3] = i[3] + i[2].length + h[i[1]] - (i[6][0] > 9);
}
// print the nodes (sorry, no transliteration available)
Neil
la source