Bangun pohon pembagi yang indah secara estetika

43

Pohon pembagi estetis adalah pohon pembagi input nyang, untuk setiap nomor komposit m, memiliki dua anak simpul yang merupakan pasangan pembagi yang paling dekat dengan akar kuadrat dari m. Simpul kiri harus pembagi yang lebih kecil mdan simpul kanan harus pembagi yang lebih besar m. Bilangan prima di pohon seharusnya tidak memiliki simpul anak. Pohon Anda mungkin dalam bentuk seni teks atau gambar. Aturan untuk output seni teks adalah sebagai berikut.

Aturan jarak

Untuk menghapus node di pohon, kami memiliki aturan berikut:

  • Node pada kedalaman tertentu dari root semua harus berada pada baris teks yang sama dalam output.
  / \ TIDAK / \  
 / \ / 3
2 3 2
  • Untuk node kiri, cabang yang masuk harus di kanan atas jika node adalah angka satu digit, yang lain, tepat di atas digit terakhir. Contoh:
 / DAN /
3 720
  • Untuk node kanan, cabang yang masuk harus di kiri atas jika node adalah angka satu digit, atau di atas angka pertama. Contoh:
\ AND \
 7 243
  • Untuk cabang kiri yang keluar, cabang harus mulai satu ruang di sebelah kiri nomor. Contoh:
  275
 /
11
  • Untuk cabang kanan keluar, cabang harus mulai satu ruang di sebelah kanan nomor. Contoh:
275
   \
   25
  • Setiap dua node pada tingkat pohon yang sama harus memiliki minimal dua ruang di antara mereka. Pada saat yang sama, setiap dua sub pohon pada tingkat pohon yang sama harus memiliki ruang sesedikit mungkin di antara mereka.
Pohon ini tidak berfungsi karena sub pohon ** ** terlalu dekat.

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

Sementara pohon ini memang memiliki ruang yang cukup di antara cabang-cabangnya.

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • Jika ada dua sub pohon yang terlalu berdekatan pada pohon, mereka dapat dipisahkan dengan menambahkan satu baris cabang /\ke pohon di atas orang tua.
   441                              
  / \ Baris terakhir belum diisi dan kami sudah kehabisan ruang.
 21 21
/ \ / \

Tambahkan baris cabang lainnya

     441                              
    / \ Hampir, tetapi 7 dan 3 terlalu berdekatan.
   / \ Satu baris lagi harus melakukannya.
  21 21
 / \ / \
3 7 3 7

Tambahkan baris cabang lainnya

      441
     / \ Dan kita sudah selesai.
    / \
   / \
  21 21
 / \ / \
3 7 3 7

Contohnya

Sebagai contoh lengkap, pohon pembagi 24 akan terlihat seperti ini:

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

4 dan 6 adalah pasangan pembagi paling dekat dengan akar kuadrat dari 24. 4 ada di sebelah kiri, karena lebih kecil. Pada baris berikutnya, angka 2 di sebelah kiri 3, karena lebih kecil.

Pohon pembagi untuk 63 akan terlihat seperti:

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

Pada pohon yang salah, 3 dan 21 bukan pasangan pembagi yang paling dekat dengan akar kuadrat dari 63, dan 3 dan 7 tidak diurutkan dengan benar. Namun, penempatan cabang pada 21 benar.

Untuk 42, Anda harus memiliki:

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

Mari kita lihat 720. Perhatikan bahwa kita membutuhkan lima tingkat cabang dari 720sehingga sub pohon 24dan 30spasi benar. Juga, perhatikan bahwa 24dan 30memiliki dua tingkat cabang karena 4dan 6memiliki simpul anak yang perlu spasi yang benar dan simpul anak 30perlu berada pada tingkat yang sama dengan simpul anak-anak 24.

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

Tantangan

  • Tugas Anda adalah untuk membangun pohon pembagi yang ditata secara estetika dan ditempatkan dengan benar untuk input n, di mana nbilangan bulat positif lebih besar dari 1.
  • Output Anda mungkin berisi spasi awal dan akhir dan baris baru awal dan akhir, tetapi sebaliknya harus sesuai dengan aturan spasi yang diberikan di atas.
  • Output Anda diperbolehkan menjadi: seni teks, gambar (format lain yang akan ditambahkan, jika perlu).
  • Untuk gambar, pastikan simpul pohon Anda memiliki jarak yang baik, dan simpul pada ketinggian yang sama di pohon berada pada ketinggian yang sama pada gambar.
  • Ini kode golf. Jumlah byte terkecil (atau setara) menang.

Penghargaan kepada Stewie Griffin karena memikirkan ide ini, dan banyak terima kasih kepada Peter Taylor, Martin Ender, Mego, dan Eyss Iʀᴋ atas bantuan mereka dalam menulis ulang spesifikasi. Seperti biasa, setiap saran atau koreksi sangat dihargai. Semoga berhasil dan bermain golf dengan baik!

Lebih banyak kasus uji:

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
sumber
Terima kasih atas tantangan ini. Saya sekarang dapat memvisualisasikan hal-hal ini tanpa menggambar mereka setiap waktu: D
Conor O'Brien
Apakah pohon perlu terlihat seperti contoh, atau dapatkah saya menggunakan fungsi built-in Mathematica? Sepertinya ini , tetapi dengan faktorisasi.
JungHwan Min
@ JHM aku tahu aku seharusnya menyimpan tag grafis-output . Ya, Anda dapat menggunakan bawaan itu. Saya akan mengedit tantangannya.
Sherlock9

Jawaban:

29

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

Setelah empat bulan mencoba menulis jawaban ini, berlari ke dinding, meninggalkannya selama seminggu, bilas, ulangi, saya akhirnya menyelesaikan jawaban seni ASCII yang tepat untuk tantangan ini. Yang tersisa hanyalah bermain golf, jadi, saran bermain golf sangat disambut. Cobalah online!

Golf: -60 byte dari penggantian nama beberapa fungsi yang sering digunakan dan mengubah bagaimana hasilnya dikembalikan. -73 byte dari mengubah bagaimana ketinggian subtree diperiksa, bagaimana variabel spasi dihitung, dan bagaimana hasilnya dikembalikan. -3 byte dari isdigit()penggantian FlipTack . Golf -16 byte isdigit()penggantian yang lebih jauh dan mengganti "" dengan E. -5 byte dari perbaikan kecil dan berubah dari Python 3 ke Python 2. -7 byte dari memodifikasi bagaimana hasilnya dikembalikan. -8 byte dari perubahan kecil menjadi bagaimana Adidefinisikan, mengubah cara Tdidefinisikan, dan menambahkan W, menggunakan hipotesis bahwa setiap subtree dengan setidaknya satu cabang lebih panjang dari mitranya, secara keseluruhan harus lebih panjang dari mitranya , menghapusQsama sekali, dan mengedit bagaimana hasilnya dikembalikan. -10 byte dari menggunakan A<10bukan L(S(A))<2untuk Adan B. -8 byte dari mengubah default Hmenjadi [0]karena kode menghindari masalah argumen default yang bisa berubah dengan tidak pernah bermutasi H, mengubah cara qdidefinisikan dengan menggunakan (B>9)alih-alih 1-(B<10), menghapus psama sekali, dan membuat Fsebagai pengganti p+q-M.

Perbaikan bug: Hipotesis salah, berlawanan dengan contoh di 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)))]

Penjelasan

Seluruh fungsi dapat diringkas menjadi sekitar empat langkah:

  1. Tentukan pasangan pembagi terbesar n, Adan B.
  2. Buat subtree Adan B, gambar sesuai kebutuhan.
  3. Tentukan jumlah ruang yang harus berada di antara sub pohon.
  4. Gambar dan kembalikan pohon pembagi baru.

Saya akan melewati setiap langkah secara berurutan.

Langkah 1. Ini adalah langkah termudah, terus terang. Periksa setiap angka zantara 1 dan akar kuadrat untuk dapat dibagi ndan ambil yang terbesar zdan n//zyang cocok. Kembalikan hanya str(n)jika nprima (salah satu A==1atau B==n)

Langkah 2. Gambarlah subpohon Adan Bdan dapatkan jumlah /\cabang antar simpul dalam subpohon. Untuk melakukan ini, kita mendapatkan indeks dari setiap langkah yang memiliki angka di dalamnya, dapatkan perbedaan pertama dari indeks, dan kurangi 1 lagi. Setelah kami memiliki ketinggian, kami membandingkannya untuk mendapatkan yang terbesar, dan menggambar ulang subtree dengan ketinggian baru.

Saya memiliki kecurigaan bahwa subtree yang lebih tinggi secara keseluruhan selalu memiliki cabang selama atau sama dengan cabang pada subtree yang lebih pendek, dan saya dapat menggunakannya untuk memasukkan kode, tetapi saya belum memiliki bukti mengenai hal ini. Contoh balasan dalam 11**9 = 2357947691.

Langkah 3. Langkah ini adalah waktu yang dibutuhkan berbulan-bulan untuk menulis. Langkah 2 membutuhkan beberapa hari untuk menulis dan men-debug, tetapi menemukan formula yang tepat untuk penspasian membutuhkan waktu lama. Saya akan melihat apakah saya bisa menyingkat apa yang saya temukan menjadi beberapa paragraf. Perhatikan bahwa beberapa kode dalam penjelasan ini sejak itu telah dikeluarkan dari kode asli.

Pertama, p, q, h, P, Q, sdan M. padalah jumlah karakter dari ujung cabang kiri /ke ujung kanan subtree kiri. qadalah jumlah karakter dari ujung kiri subtree kanan ke ujung cabang kanan /. hadalah jumlah cabang antara root dan sub pohon. Pdan Qhanyalah kebalikan dari pdan qdan berguna untuk menempatkan ruang di sekitar /\cabang hingga ke akar n. sadalah jumlah spasi yang ditambahkan antara dua subpohon. Mpaling sederhana; itu panjangnya n. Letakkan secara grafis:

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

Rumus untuk menentukan padalah ini p = len(x[0]) - x[0].rindex(str(A)[-1]) - (A<10):, panjang, minus indeks nol karakter terakhir dalam A, minus koreksi untuk satu digit As.

Rumus untuk menentukan qadalah ini q = y[0].index(str(B)[0]) + (B>9):, indeks karakter pertama dalam B, ditambah koreksi untuk pengindeksan nol, minus koreksi untuk satu digit Bs (digabungkan menjadi satu koreksi untuk beberapa digit Bs).

Rumus untuk menentukan hapakah ini: h = H[0] or (p+q+M%2+2-M)//2 or 1. Entah kita ambil dari standar Hyang berarti kita menggambar ulang pohon, kita gunakan (from_the_left + from_the_right + parity_space + 2 - len(root)) // 2), atau kita menggunakan jumlah minimum tingkat cabang, 1.

Rumus untuk menentukan sapakah ini: s = 2*h+M-p-q. Kami mengurangi pdan qdari jumlah spasi di antara cabang-cabang root yang terlebar 2*h + M.

Langkah 4. Dan akhirnya kami menyatukan semuanya. Pertama kita membuat root,, [" "*(P+h)+Z+" "*(Q+h)]lalu kita masukkan cabang ke bawah ke sub pohon [" "*(P+J)+"/"+" "*(2*h+M-2*J-2)+"\\"+" "*(Q+J)for J in G(h)][::-1],, dan akhirnya kita menempatkan di bawah pohon yang ditempatkan dengan benar [(" "*(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)))],.

Dan lagi! Kami memiliki pohon pembagi yang menyenangkan secara estetika!

Tidak melakukan pelanggaran:

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
sumber
Bisakah isdigitcek Anda '/'<x[i].strip()[0]<':'?
FlipTack
14

Mathematica, 96 86 81 79 78 byte

Terima kasih @MartinEnder untuk 2 byte.

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

Outputnya terlihat seperti ini:

masukkan deskripsi gambar di sini

Penjelasan

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

Buat daftar pembagi input. Temukan elemen yang paling dekat dengan akar kuadrat dari input. ( Maxuntuk meratakan output)

#2[#,#2/#]&

Temukan pembagi lainnya dengan membagi input dengan pembagi yang ditemukan di atas, terapkan input sebagai kepala hasil.

#0/@

Ulangi proses ini.

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

Jika inputnya prima, jangan lakukan apa pun.

TreeForm

Memformat output.

Sunting: Versi yang lebih estetis (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])&

Outputnya terlihat seperti ini:

masukkan deskripsi gambar di sini

JungHwan Min
sumber
3
Sqrt@#-> #^.5(tentu saja Anda tidak dapat menggunakan notasi infiks untuk Nearesttetapi kemudian Anda dapat menggunakan Max@).
Martin Ender
5
Ini mengikuti aturan, tetapi pohon itu jauh dari estetika xD
Beta Decay
2
Kecantikan ada di mata yang melihatnya :)
Nelson
1
Saya tidak yakin ini valid. Berbeda dengan contoh, node pada setiap baris tidak diberi spasi secara merata. Selain itu, garis tidak terhubung ke digit yang benar.
Mego
1
@Mego Baiklah, OP mengatakan itu valid.
R. Kap
3

Arang , 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⊕§ηι↓

Cobalah online! Tautan adalah untuk mengucapkan versi kode. Karena versi verbose sangat verbose, ia adalah transliterasi JavaScript dari algoritma utama:

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
sumber