Bauen Sie einen ästhetisch ansprechenden Teilerbaum

43

Ein ästhetisch ansprechender Teilerbaum ist ein Baum von Eingabeteilern n, der für jede zusammengesetzte Zahl mzwei untergeordnete Knoten hat, die das Teilerpaar darstellen , das der Quadratwurzel von am nächsten liegt m. Der linke Knoten sollte der kleinere Teiler sein mund der rechte Knoten sollte der größere Teiler sein m. Eine Primzahl im Baum sollte keine untergeordneten Knoten haben. Ihr Baum kann in Form von Textkunst oder einem Bild vorliegen. Die Regeln für die Ausgabe von Textkunst lauten wie folgt.

Abstandsregeln

Um die Knoten in der Baumstruktur auszuräumen, gelten die folgenden Regeln:

  • Die Knoten in einer bestimmten Tiefe von der Wurzel sollten sich in der Ausgabe alle in derselben Textzeile befinden.
  / \ NOT / \  
 / \ / 3
2 3 2
  • Bei linken Knoten sollte sich der eingehende Zweig oben rechts befinden, wenn der Knoten eine einstellige Zahl ist, ansonsten direkt über der letzten Ziffer. Beispiel:
 / UND /
3 720
  • Bei rechten Knoten sollte sich der eingehende Zweig oben links befinden, wenn der Knoten eine einstellige Zahl ist, ansonsten direkt über der ersten Ziffer. Beispiel:
\ UND \
 7 243
  • Bei ausgehenden linken Zweigen sollte der Zweig ein Leerzeichen links von der Nummer beginnen. Beispiel:
  275
 /
11
  • Für ausgehende rechte Zweige sollte der Zweig ein Leerzeichen rechts von der Nummer beginnen. Beispiel:
275
   \
   25
  • Zwei beliebige Knoten auf derselben Ebene des Baums müssen mindestens zwei Leerzeichen voneinander entfernt sein. Gleichzeitig sollten zwei Teilbäume auf derselben Ebene des Baums so wenig Leerzeichen wie möglich voneinander entfernt sein.
Dieser Baum funktioniert nicht, weil die ** Teilbäume ** zu eng sind.

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

Während dieser Baum genug Platz zwischen seinen Zweigen hat.

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • Wenn zwei Teilbäume in einem Baum zu nahe beieinander liegen, können Sie sie trennen, indem Sie /\dem Baum über den Eltern eine weitere Reihe von Zweigen hinzufügen .
   441                              
  / \ Letzte Zeile ist noch nicht ausgefüllt und wir haben bereits keinen Platz mehr.
 21 21
/ \ / \

Fügen Sie eine weitere Reihe von Zweigen hinzu

     441                              
    / \ Fast, aber die 7 und die 3 liegen zu nahe beieinander.
   / \ Eine weitere Zeile sollte es tun.
  21 21
 / \ / \
3 7 3 7

Fügen Sie eine weitere Reihe von Zweigen hinzu

      441
     / \ Und wir sind fertig.
    / \
   / \
  21 21
 / \ / \
3 7 3 7

Beispiele

Als vollständiges Beispiel sieht der Teilerbaum von 24 folgendermaßen aus:

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

4 und 6 sind das Teilerpaar, das der Quadratwurzel von 24 am nächsten liegt. 4 befindet sich links, da es kleiner ist. In der nächsten Zeile die Zahl 2 links von 3, weil sie kleiner ist.

Der Teilerbaum für 63 sollte wie folgt aussehen:

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

In der falschen Baumstruktur sind 3 und 21 nicht das der Quadratwurzel von 63 am nächsten liegende Teilerpaar, und 3 und 7 sind nicht richtig sortiert. Die Abzweigplatzierung auf der 21 ist jedoch korrekt.

Für 42 sollten Sie haben:

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

Werfen wir einen Blick auf 720. Beachten Sie, dass wir fünf Verzweigungsebenen benötigen 720, damit die Teilbäume 24und den 30richtigen Abstand zueinander haben. Beachten Sie auch, dass 24und 30zwei Ebenen von Zweigen haben , weil 4und 6Kinder - Knoten haben , die richtigen Abstand und die Kinder - Knoten müssen 30Notwendigkeit , sich auf der gleichen Ebene wie die Kinder - Knoten zu sein 24.

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

Die Herausforderung

  • Ihre Aufgabe ist es, einen korrekt beabstandeten, ästhetisch ansprechenden Teilerbaum für die Eingabe zu erstellen n, wobei neine positive ganze Zahl größer als 1 ist.
  • Ihre Ausgabe kann führende und nachfolgende Leerzeichen sowie führende und nachfolgende Zeilenumbrüche enthalten, muss jedoch ansonsten den oben angegebenen Abstandsregeln entsprechen.
  • Ihre Ausgabe darf sein: Textkunst, ein Bild (andere Formate müssen hinzugefügt werden, falls erforderlich).
  • Stellen Sie bei Bildern sicher, dass die Knoten Ihres Baums einen ausreichenden Abstand haben und dass sich die Knoten auf derselben Höhe im Baum auf derselben Höhe im Bild befinden.
  • Das ist Code Golf. Die geringste Anzahl von Bytes (oder eine entsprechende Anzahl) gewinnt.

Wir danken Stewie Griffin für das Nachdenken über diese Idee und Peter Taylor, Martin Ender, Mego und Eᴀsᴀ Iᴛᴇʀʟʏ für ihre Hilfe bei der Neufassung der Spezifikation. Wie immer sind Vorschläge oder Korrekturen sehr willkommen. Viel Glück und gutes Golfen!

Weitere Testfälle:

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
quelle
Vielen Dank für diese Herausforderung. Ich kann diese Dinge jetzt visualisieren, ohne sie jedes Mal zu zeichnen: D
Conor O'Brien
Muss der Baum wie in den Beispielen aussehen, oder kann ich die integrierte Mathematica-Funktion verwenden? Es sieht so aus , aber mit Faktorisierung.
JungHwan Min
@JHM Ich wusste, dass ich das Tag für die grafische Ausgabe hätte behalten sollen . Ja, Sie können das eingebaute verwenden. Ich werde die Herausforderung bearbeiten.
Sherlock9

Antworten:

29

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

Nachdem ich vier Monate lang versucht habe, diese Antwort zu schreiben, gegen eine Wand gelaufen bin und sie für Wochen stehen gelassen habe, spüle und wiederhole, habe ich endlich eine richtige ASCII-Kunstantwort für diese Herausforderung erstellt. Alles, was übrig bleibt, ist das Golfen. Daher sind Vorschläge zum Golfen sehr willkommen. Probieren Sie es online!

Golfs: -60 Bytes durch Umbenennen häufig verwendeter Funktionen und Ändern der Ergebnisausgabe. -73 Bytes, wenn geändert wird, wie die Höhen der Teilbäume überprüft werden, wie die Abstandsvariablen berechnet werden und wie das Ergebnis zurückgegeben wird. -3 Bytes von FlipTacks isdigit()Ersatz. -16 Bytes, die diesen isdigit()Ersatz noch weiter ausbauen und "" durch "ersetzen E. -5 Bytes nach geringfügigen Verbesserungen und nach dem Wechsel von Python 3 zu Python 2. -7 Bytes nach dem Ändern der Rückgabe des Ergebnisses. -8 Bytes aus einer kleinen Änderung, wie Adefiniert ist, zu verändern , wie Tdefiniert ist, und das Hinzufügen W, die Hypothese unter Verwendung dass jede Teilstruktur mit mindestens einem Zweig länger als sein Gegenstück, insgesamt notwendigerweise länger als sein Gegenstück ist , zu entfernenQinsgesamt und Bearbeiten, wie das Ergebnis zurückgegeben wird. -10 Bytes von using A<10anstelle von L(S(A))<2for Aund B. -8 Bytes vom Ändern der Standardeinstellung Hauf, [0]da der Code das Problem von veränderlichen Standardargumenten vermeidet, indem er niemals Hverändert, die qDefinition ändert , indem er (B>9)stattdessen verwendet 1-(B<10), alles entfernt pund Fals Ersatz für erstellt p+q-M.

Fehlerbehebungen: Hypothese war falsch, Gegenbeispiel in 11**9 = 2357947691. +1 Byte

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

Erläuterung

Die gesamte Funktion lässt sich in vier Schritten zusammenfassen:

  1. Bestimmen Sie das größte Divisorpaar von n, Aund B.
  2. Erstellen Sie die Teilbäume von Aund Bund zeichnen Sie sie nach Bedarf neu.
  3. Bestimmen Sie die Anzahl der Leerzeichen, die zwischen den Teilbäumen stehen sollen.
  4. Zeichnen Sie den neuen Teilerbaum und geben Sie ihn zurück.

Ich werde jeden Schritt der Reihe nach durchgehen.

Schritt 1. Dies ist, ganz offen gesagt, der einfachste Schritt. Überprüfen Sie jede Zahl zzwischen 1 und der Quadratwurzel auf Teilbarkeit in nund greifen Sie zu der größten zund n//zder übereinstimmenden Zahl . Rückgabe nur str(n)wenn nist prime (entweder A==1oder B==n)

Schritt 2. Zeichnen Sie die Teilbäume von Aund Bund ermitteln Sie die Anzahl der /\Zweige zwischen den Knoten in den Teilbäumen. Dazu erhalten wir die Indizes jedes Schritts, der Ziffern enthält, die ersten Differenzen der Indizes und subtrahieren erneut 1. Sobald wir die Höhen haben, vergleichen wir sie, um die größten zu erhalten, und zeichnen die Teilbäume mit den neuen Höhen neu.

Ich habe den schleichenden Verdacht, dass der Teilbaum, der insgesamt höher ist, immer Zweige hat, die mindestens so lang sind wie die Zweige des kürzeren Teilbaums, und ich kann das zum Golfen des Codes verwenden, aber ich habe noch keinen Beweis dafür. Gegenbeispiel in 11**9 = 2357947691.

Schritt 3. Das Schreiben dieses Schritts dauerte Monate. Das Schreiben und Debuggen von Schritt 2 dauerte einige Tage, aber das Finden der richtigen Formeln für den Abstand dauerte ewig. Ich werde sehen, ob ich das, was ich herausgefunden habe, in ein paar Absätzen zusammenfassen kann. Beachten Sie, dass ein Teil des Codes in dieser Erklärung seitdem nicht mehr mit dem tatsächlichen Code übereinstimmt.

Erstens p, q, h, P, Q, sund M. pist die Anzahl der Zeichen vom Ende des linken Zweigs /bis zum rechten Ende des linken Teilbaums. qist die Anzahl der Zeichen vom linken Ende des rechten Teilbaums bis zum Ende des rechten Zweigs /. hist die Anzahl der Zweige zwischen der Wurzel und den Teilbäumen. Pund Qsind nur die Umkehrungen von pund qund sind nützlich, um die Abstände um /\Zweige bis zur Wurzel zu platzieren n. sist die Anzahl der Leerzeichen, die zwischen den beiden Teilbäumen eingefügt werden. Mist am einfachsten; es ist die Länge von n. Grafisch setzen:

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

Die Formel zur Bestimmung plautet: p = len(x[0]) - x[0].rindex(str(A)[-1]) - (A<10)Die Länge abzüglich des Nullindex des letzten Zeichens in A abzüglich einer Korrektur für einstellige As.

Die Formel zur Bestimmung qlautet wie folgt: q = y[0].index(str(B)[0]) + (B>9)Der Index des ersten Zeichens in B plus eine Korrektur für die Indexierung auf Null minus eine Korrektur für einstellige Bs (kombiniert zu einer Korrektur für mehrstellige Bs).

Die Formel zur Bestimmung hist dies: h = H[0] or (p+q+M%2+2-M)//2 or 1. Entweder greifen wir auf ein vordefiniertes Objekt zurück, Hwas bedeutet, dass wir den Baum neu zeichnen (from_the_left + from_the_right + parity_space + 2 - len(root)) // 2), oder wir verwenden die Mindestanzahl von Zweigebenen, 1.

Die Formel zur Bestimmung sist dies: s = 2*h+M-p-q. Wir subtrahieren pund qvon der Anzahl der Abstände zwischen den Zweigen der Wurzel am breitesten 2*h + M.

Schritt 4. Und zum Schluss setzen wir alles zusammen. Zuerst machen wir die Wurzel, [" "*(P+h)+Z+" "*(Q+h)]dann setzen wir die Zweige bis zu den Teilbäumen ein [" "*(P+J)+"/"+" "*(2*h+M-2*J-2)+"\\"+" "*(Q+J)for J in G(h)][::-1], und schließlich setzen wir unsere Teilbäume mit den richtigen Abständen ein [(" "*(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à! Wir haben uns einen ästhetisch ansprechenden Teilerbaum!

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
quelle
Könnte Ihr isdigitScheck sein '/'<x[i].strip()[0]<':'?
FlipTack
14

Mathematica, 96 86 81 79 78 Bytes

Danke @MartinEnder für 2 Bytes.

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

Die Ausgabe sieht folgendermaßen aus:

Bildbeschreibung hier eingeben

Erläuterung

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

Generieren Sie die Liste der Teiler der Eingabe. Suchen Sie das Element, das der Quadratwurzel der Eingabe am nächsten liegt. ( Maxdient zur Verflachung der Ausgabe)

#2[#,#2/#]&

Suchen Sie den anderen Divisor, indem Sie die Eingabe durch den oben angegebenen Divisor dividieren. Wenden Sie die Eingabe als Kopf des Ergebnisses an.

#0/@

Wiederholen Sie den Vorgang.

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

Wenn der Eingang prim ist, machen Sie nichts.

TreeForm

Formatieren Sie die Ausgabe.

Edit: Eine ästhetisch ansprechendere Version (258 Bytes)

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

Die Ausgabe sieht folgendermaßen aus:

Bildbeschreibung hier eingeben

JungHwan min
quelle
3
Sqrt@#-> #^.5(dann können Sie natürlich keine Infixnotation verwenden, Nearestaber dann können Sie verwenden Max@).
Martin Ender
5
Es folgt den Regeln, aber dieser Baum ist
Beta Decay
2
Schönheit liegt im Auge des Betrachters :)
Nelson
1
Ich bin mir nicht sicher, ob dies gültig ist. Im Gegensatz zu den Beispielen sind die Knoten in jeder Zeile nicht gleichmäßig verteilt. Außerdem verbinden sich die Leitungen nicht mit der richtigen Ziffer.
Mego
1
@ Mego Nun, OP sagte, es sei gültig.
R. Kap
3

Kohle , 302 Bytes

≔⟦⟦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⊕§ηι↓

Probieren Sie es online! Link ist eine ausführliche Version des Codes. Da die ausführliche Version sehr ausführlich ist, ist er eine JavaScript-Transliteration des Hauptalgorithmus:

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
quelle