Costruisci un albero divisore esteticamente gradevole


43

Un albero divisore esteticamente gradevole è un albero di divisori di input nche, per qualsiasi numero composto m, ha due nodi figlio che sono la coppia di divisori più vicini alla radice quadrata di m. Il nodo sinistro dovrebbe essere il divisore più piccolo di me il nodo destro dovrebbe essere il divisore più grande di m. Un numero primo nell'albero non dovrebbe avere nodi figli. Il tuo albero può essere sotto forma di arte del testo o di un'immagine. Le regole per l'output della grafica sono le seguenti.

Regole di spaziatura

Per distanziare i nodi sull'albero, abbiamo le seguenti regole:

  • I nodi a una determinata profondità dalla radice dovrebbero essere tutti sulla stessa riga di testo nell'output.
  / \ NOT / \  
 / \ / 3
2 3 2
  • Per i nodi a sinistra, il ramo in arrivo dovrebbe essere in alto a destra se il nodo è un numero a una cifra, altrimenti appena sopra l'ultima cifra. Esempio:
 / E /
3 720
  • Per i nodi a destra, il ramo in arrivo dovrebbe essere in alto a sinistra se il nodo è un numero a una cifra, altrimenti appena sopra la prima cifra. Esempio:
\ E \
 7 243
  • Per i rami di sinistra in uscita, il ramo dovrebbe iniziare uno spazio a sinistra del numero. Esempio:
  275
 /
11
  • Per i rami a destra in uscita, il ramo dovrebbe iniziare uno spazio a destra del numero. Esempio:
275
   \
   25
  • Ogni due nodi sullo stesso livello dell'albero dovrebbero avere almeno due spazi tra loro. Allo stesso tempo, due sottotipi sullo stesso livello dell'albero dovrebbero avere il minor numero di spazi tra loro possibile.
Questo albero non funziona perché i ** sottotitoli ** sono troppo vicini.

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

Mentre questo albero ha abbastanza spazio tra i suoi rami.

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • Se due sottostrutture sono troppo vicine tra loro su un albero, possono essere separate aggiungendo un'altra fila di rami /\all'albero sopra i genitori.
   441                              
  / \ L'ultima riga non è ancora stata compilata e abbiamo già esaurito lo spazio.
 21 21
/ \ / \

Aggiungi un'altra fila di rami

     441                              
    / \ Quasi, ma il 7 e il 3 sono troppo vicini.
   / \ Un'altra riga dovrebbe farlo.
  21 21
 / \ / \
3 7 3 7

Aggiungi un'altra fila di rami

      441
     / \ E abbiamo finito.
    / \
   / \
  21 21
 / \ / \
3 7 3 7

Esempi

Ad esempio, l'albero divisore di 24 sarà simile al seguente:

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

4 e 6 sono la coppia di divisori più vicini alla radice quadrata di 24. 4 è a sinistra, perché è più piccolo. Nella riga successiva, il numero 2 a sinistra di 3, perché è più piccolo.

L'albero del divisore per 63 dovrebbe apparire come:

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

Nell'albero errato, 3 e 21 non sono la coppia di divisori più vicini alla radice quadrata di 63 e 3 e 7 non sono ordinati correttamente. Tuttavia, la posizione del ramo sul 21 è corretta.

Per 42, dovresti avere:

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

Diamo un'occhiata a 720. Si noti che abbiamo bisogno di cinque livelli di rami in 720modo che gli spazi secondari 24e 30siano spaziati correttamente. Inoltre, si noti che 24e 30hanno due livelli di rami perché 4e 6avere nodi figli che necessitano corretta spaziatura ei nodi figli di 30necessità di essere sullo stesso livello dei bambini nodi 24.

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

La sfida

  • Il tuo compito è costruire un albero divisore esteticamente piacevole e spaziato correttamente per l'input n, dove nun numero intero positivo maggiore di 1.
  • L'output può contenere spazi iniziali e finali e nuove righe iniziali e finali, ma deve in altro modo essere conforme alle regole di spaziatura indicate sopra.
  • Il tuo output può essere: text art, un'immagine (altri formati da aggiungere, se necessario).
  • Per le immagini, assicurati che i nodi dell'albero siano ben distanziati e che i nodi alla stessa altezza dell'albero siano alla stessa altezza nell'immagine.
  • Questo è il codice golf. Vince il numero minimo di byte (o equivalente).

Ringraziamo Stewie Griffin per aver pensato a questa idea e molte grazie a Peter Taylor, Martin Ender, Mego ed Eᴀsᴛᴇʀʟʏ Iᴛᴇʀʟʏ per il loro aiuto nel riscrivere le specifiche. Come al solito, eventuali suggerimenti o correzioni sono molto apprezzati. Buona fortuna e buon golf!

Altri casi di 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

Grazie per questa sfida. Ora posso visualizzare queste cose senza disegnarle ogni volta: D
Conor O'Brien,

L'albero deve apparire come negli esempi o posso usare la funzione Mathematica integrata? Sembra che questo , ma con fattorizzazione.
JungHwan Min

@JHM Sapevo che avrei dovuto conservare il tag di output grafico . Sì, è possibile utilizzare quello integrato. Modificherò la sfida.
Sherlock9

Risposte:


29

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

Dopo quattro mesi di tentativi di scrivere questa risposta, incappando in un muro, lasciandolo per una settimana, sciacquandolo, ripetendolo, ho finalmente finito una corretta risposta ASCII per questa sfida. Tutto ciò che rimane è il golf e quindi i suggerimenti per il golf sono i benvenuti. Provalo online!

Golf: -60 byte dalla ridenominazione di alcune funzioni spesso utilizzate e dalla modifica della modalità di restituzione del risultato. -73 byte dalla modifica del modo in cui vengono controllate le altezze dei sottotitoli, come vengono calcolate le variabili di spaziatura e come viene restituito il risultato. -3 byte dalla isdigit()sostituzione di FlipTack . -16 byte giocando a golf quella isdigit()sostituzione ancora di più e sostituendo "" con E. -5 byte da piccoli miglioramenti e passaggio da Python 3 a Python 2. -7 byte dalla modifica della modalità di restituzione del risultato. -8 byte da una piccola modifica al modo in cui Aè definito, cambiando il modo in cui Tè definito e aggiungendo W, usando l'ipotesi che qualsiasi sottostruttura con almeno un ramo più lungo della sua controparte, sia necessariamente più lunga complessiva della sua controparte , rimuovendoQdel tutto, e modificando come viene restituito il risultato. -10 byte dall'uso A<10anziché L(S(A))<2per Ae B. -8 byte dalla modifica del valore predefinito Ha [0]poiché il codice evita il problema degli argomenti predefiniti mutabili non mutando mai H, cambiando il modo in cui qviene definito usando (B>9)invece di 1-(B<10), rimuovendo del ptutto e creando Fcome sostituto di p+q-M.

Correzioni di errori : l' ipotesi era errata, controesempio 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)))]

Spiegazione

L'intera funzione può essere ridotta a circa quattro passaggi:

  1. Determinare la più grande coppia divisore di n, Ae B.
  2. Rendi i sottotitoli di Ae B, ridisegnando secondo necessità.
  3. Determina il numero di spazi che dovrebbero andare tra i sottotitoli.
  4. Disegna e restituisci il nuovo albero divisore.

Eseguirò ogni passaggio in ordine.

Passaggio 1. Questo è il passaggio più semplice, francamente. Controlla ogni numero ztra 1 e la radice quadrata per la divisibilità ne prendi il più grande ze n//zquello corrispondente. Ritorna solo str(n)se nè primo (o A==1o B==n)

Passaggio 2. Disegnare i sottotitoli di Ae Bottenere il numero di /\rami tra i nodi nei sottotitoli. Per fare ciò, otteniamo gli indici di ogni passaggio che contiene cifre, otteniamo le prime differenze degli indici e sottraggiamo di nuovo 1. Una volta che abbiamo le altezze, le confrontiamo per ottenere il più grande e ridisegniamo i sottotitoli con le nuove altezze.

Ho il sospetto furtivo che la sottostruttura più alta in generale abbia sempre rami lunghi o uguali a quelli della sottostruttura più corta, e posso usarlo per giocare a golf il codice, ma non ne ho ancora la prova. Controesempio in 11**9 = 2357947691.

Passaggio 3. Questo passaggio è ciò che ha richiesto mesi per scrivere. Il passaggio 2 ha richiesto alcuni giorni per scrivere ed eseguire il debug, ma trovare le formule giuste per la spaziatura ha richiesto secoli. Vedrò se riesco a condensare ciò che ho capito in alcuni paragrafi. Si noti che da allora parte del codice in questa spiegazione è stato sottratto al codice reale.

In primo luogo, p, q, h, P, Q, se M. pè il numero di caratteri dalla fine del ramo /sinistro all'estremità destra della sottostruttura sinistra. qè il numero di caratteri dall'estremità sinistra della sottostruttura destra all'estremità del ramo destro /. hè il numero di rami tra la radice e i sottotitoli. Pe Qsono solo le inversioni di pe qe sono utili per posizionare gli spazi attorno ai /\rami fino alla radice n. sè il numero di spazi che si aggiungono tra i due sottoalberi. Mè il più semplice; è la lunghezza di n. Metti graficamente:

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

La formula per determinare pè questa p = len(x[0]) - x[0].rindex(str(A)[-1]) - (A<10):, la lunghezza, meno l'indice zero dell'ultimo carattere in A, meno una correzione per i numeri a Auna cifra .

La formula per determinare qè questa: q = y[0].index(str(B)[0]) + (B>9)l'indice del primo carattere in B, più una correzione per l'indicizzazione zero, meno una correzione per le Bs di una cifra (combinate in una correzione per le Bs di più cifre ).

La formula per determinare hquesto: h = H[0] or (p+q+M%2+2-M)//2 or 1. O prendiamo da un predefinito Hche significa che stiamo ridisegnando l'albero, usiamo (from_the_left + from_the_right + parity_space + 2 - len(root)) // 2)o usiamo il numero minimo di livelli di ramo, 1.

La formula per determinare squesto: s = 2*h+M-p-q. Sottraiamo pe qdal numero di spazi tra i rami della radice al massimo 2*h + M.

Passaggio 4. E infine abbiamo messo tutto insieme. Prima facciamo la radice, [" "*(P+h)+Z+" "*(Q+h)]e poi abbiamo messo tra i rami fino alle sottostrutture, [" "*(P+J)+"/"+" "*(2*h+M-2*J-2)+"\\"+" "*(Q+J)for J in G(h)][::-1]e, infine, abbiamo messo in sottostrutture adeguatamente distanziati, [(" "*(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à! Abbiamo un albero divisore esteticamente piacevole!

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

Il tuo isdigitassegno potrebbe essere '/'<x[i].strip()[0]<':'?
FlipTack,

14

Mathematica, 96 86 81 79 78 byte

Grazie @MartinEnder per 2 byte.

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

L'output è simile al seguente:

inserisci qui la descrizione dell'immagine

Spiegazione

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

Genera l'elenco dei divisori dell'input. Trova l'elemento più vicino alla radice quadrata dell'input. ( Maxserve per appiattire l'uscita)

#2[#,#2/#]&

Trova l'altro divisore dividendo l'input per il divisore trovato sopra, applica l'input come capo del risultato.

#0/@

Ripeti il ​​processo.

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

Se l'input è primo, non fare nulla.

TreeForm

Formatta l'output.

Modifica: una versione esteticamente più piacevole (258 byte)

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

L'output è simile al seguente:

inserisci qui la descrizione dell'immagine


3
Sqrt@#-> #^.5(ovviamente non puoi usare la notazione infix per Nearestma poi puoi usare Max@).
Martin Ender,

5
Segue le regole, ma quell'albero è tutt'altro che esteticamente piacevole xD
Decadimento beta

2
La bellezza è negli occhi di chi guarda :)
Nelson,

1
Non sono sicuro che questo sia valido. A differenza degli esempi, i nodi su ogni riga non sono distribuiti uniformemente. Inoltre, le linee non si collegano alla cifra corretta.
Mego

1
@Mego Bene, OP ha detto che era valido.
R. Kap,

3

Carbone , 302 byte

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

Provalo online! Il collegamento è alla versione dettagliata del codice. Poiché la versione dettagliata è molto dettagliata, è una traslitterazione JavaScript dell'algoritmo principale:

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