Побудуйте естетично приємне дерево дільника


43

Естетично дільник дерево є деревом подільників введення , nщо для будь-якого складеного числа m, має дві дочірніх вузли , які є парою подільників , які знаходяться ближче до кореню квадратному з m. Лівий вузол повинен бути меншим дільником, mа правий вузол - більшим дільником m. Просте число в дереві не повинно мати дітових вузлів. Ваше дерево може бути у формі тексту тексту чи зображення. Правила виведення тексту тексту наступні.

Правила інтервалу

Для розміщення вузлів на дереві ми маємо такі правила:

  • Вузли на заданій глибині від кореня повинні бути в одному рядку тексту у висновку.
  / \ НЕ / \  
 / \ / 3
2 3 2
  • Для лівих вузлів вхідна гілка повинна знаходитися у верхньому правому куті, якщо вузол - одноцифрове число, в іншому випадку - трохи вище останньої цифри. Приклад:
 / І /
3 720
  • Для правильних вузлів вхідна гілка повинна знаходитися в лівій верхній частині, якщо вузол - одноцифрове число, в іншому випадку - трохи вище першої цифри. Приклад:
\ І \
 7 243
  • Для відхідних лівих гілок гілка повинна починатися на один пробіл зліва від числа. Приклад:
  275
 /
11
  • Для правої гілки, що виходить, гілка повинна починати один пробіл праворуч від числа. Приклад:
275
   \
   25
  • Будь-які два вузли на одному рівні дерева повинні мати як мінімум два проміжки між ними. У той же час, будь-які два піддерева на тому ж рівні дерева повинні мати кілька пробілів між ними , як це можливо.
Це дерево не працює, оскільки ** підтрубки ** занадто близько.

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

Хоча цьому дереву достатньо місця між його гілками.

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • Якщо будь-які дві підкреслини занадто близько між собою на дереві, їх можна відокремити, додавши ще один ряд гілок /\до дерева над батьками.
   441                              
  / \ Останній рядок ще не заповнений, і у нас вже не вистачає місця.
 21 21
/ \ / \

Додати ще один ряд гілок

     441                              
    / \ Майже, але 7 і 3 занадто близько один до одного.
   / \ Ще один рядок повинен зробити це.
  21 21
 / \ / \
3 7 3 7

Додати ще один ряд гілок

      441
     / \ І ми закінчили.
    / \
   / \
  21 21
 / \ / \
3 7 3 7

Приклади

Як повний приклад, дерево-дільник 24 буде виглядати так:

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

4 і 6 - пара дільників, найближчих до квадратного кореня 24. 4 знаходиться зліва, тому що вона менша. У наступному рядку цифра 2 зліва від 3, тому що вона менша.

Дерево дільника на 63 має виглядати так:

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

У неправильному дереві 3 та 21 не є пара дільників, найближчих до квадратного кореня 63, а 3 та 7 не відсортовані належним чином. Хоча розміщення гілок на 21 є правильним.

Для 42 років у вас повинно бути:

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

Давайте подивимось на 720. Зауважимо, що нам потрібно п’ять рівнів гілок з 720таким чином, щоб 24і 30підтрубки були правильно розташовані. Також зауважте, що 24і 30мають два рівні гілок, тому що 4і 6є дочірні вузли, які потребують правильного проміжку, і дитячі вузли 30повинні бути на тому ж рівні , що і дочірні вузли 24.

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

Змагання

  • Ваше завдання полягає в тому, щоб створити правильно впорядковане естетично приємне дерево дільника для введення n, де nдодатне ціле число більше 1.
  • Ваш вихід може містити провідні та кінцеві пробіли, а також провідні та кінцеві нові рядки, але в іншому випадку повинен відповідати правилам інтервалу, наведеним вище.
  • Вашим результатом може бути: текст тексту, зображення (інші необхідні формати, якщо потрібно).
  • Для зображень переконайтесь, що вузли вашого дерева добре розташовані, а також, щоб вузли на одній висоті в дереві були на одній висоті на зображенні.
  • Це код гольфу. Найменша кількість байтів (або еквівалент) виграє.

Подяка Стюі Гріффін за думку про цю ідею, і велике спасибі Пітеру Тейлору, Мартіну Ендеру, Мего та Еᴀсᴛᴇʀʟʏ Іʀᴋ за допомогу в переписанні специфікації. Як завжди, будь-які пропозиції чи виправлення високо оцінюються. Успіхів і хорошого гольфу!

Більше тестових випадків:

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

Дякую за цей виклик. Зараз я можу візуалізувати ці речі, не малюючи їх кожен раз: D
Conor O'Brien

Чи потрібно дерево виглядати як приклади, чи я можу використовувати вбудовану функцію Mathematica? Схоже , що це , але з розкладанням.
JungHwan Min

@JHM Я знав, що мав би зберегти тег графічного виводу . Так, ви можете використовувати цей вбудований. Я відредагую виклик.
Шерлок9

Відповіді:


29

Python 2 , 711 651 575 559 554 547 539 540 530 522 байт

Після чотирьох місяців спроб написати цю відповідь, наткнувшись на стіну, залишивши її на тиждень, промити, повторити, я нарешті закінчив належну відповідь на мистецтво ASCII для цього завдання. Все, що залишилося, - це гольф, і, отже, пропозиції щодо гольфу дуже вітаються. Спробуйте в Інтернеті!

Гольфи: -60 байт від перейменування деяких часто використовуваних функцій та зміни способу повернення результату. -73 байти від зміни способу перевірки висоти підрядів, як обчислюються змінні міжряддя та як повертається результат. -3 байти від isdigit()заміни FlipTack . -16 байт гольфу, що isdigit()заміни ще більше і заміни "" на E. -5 байт від незначних удосконалень та зміни від Python 3 до Python 2. -7 байт від зміни способу повернення результату. -8 байт від невеликої зміни до того, як Aвизначено, зміни способу Tвизначення та додавання W, використовуючи гіпотезу про те, що будь-яке піддерево з принаймні однією довшою гілкою, ніж його аналог, обов'язково довше, ніж його аналог , видаляючиQвзагалі та редагування того, як результат повертається. -10 байт від використання A<10замість L(S(A))<2для Aі B. -8 байт від зміни за замовчуванням Hдо, [0]оскільки код уникає проблеми змінних аргументів за замовчуванням, ніколи не мутуючи H, змінюючи спосіб qвизначення, використовуючи (B>9)замість 1-(B<10), видаляючи pвзагалі та створюючи Fяк заміну p+q-M.

Виправлення помилок: Гіпотеза помилкова, контрприклад в 11**9 = 2357947691. +1 байт

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

Пояснення

Цілу функцію можна звести приблизно до чотирьох кроків:

  1. Визначити найбільший дільник пари n, Aі B.
  2. Складіть підкреслення Aта B, перемальовуючи за потребою.
  3. Визначте кількість пробілів, які повинні пройти між підрядками.
  4. Намалюйте та поверніть нове дерево дільника.

Я буду проходити кожен крок по порядку.

Крок 1. Це найпростіший крок, відверто кажучи. Перевірте кожне число zміж 1 і квадратним коренем на предмет поділу на nі схопіть найбільше zі таке, n//zщо відповідає. Повертайтеся лише у str(n)випадку, якщо nце просто (або A==1або B==n)

Крок 2. Накресліть підряди Aта Bі отримайте кількість /\гілок між вузлами в підтрубках. Для цього ми отримуємо індекси кожного кроку, який має в них цифри, отримуємо перші відмінності індексів і віднімаємо 1 знову. Як тільки ми отримаємо висоти, ми порівнюємо їх, щоб отримати найбільші, і перемальовуємо підкреслення з новими висотами.

У мене криється підозра на те, що у підтонного дерева, яке є загальним, завжди є гілки до тих пір, як і дорівнює гілкам на коротшому піддереві, і я можу використовувати це для гольфу коду, але до цього часу я не маю доказів. Контрприклад в 11**9 = 2357947691.

Крок 3. На написання цього кроку пішло кілька місяців. На написання та налагодження кроку 2 пішло кілька днів, але пошук правильних формул для інтервалу зайняв багато років. Я побачу, чи зможу я ущільнити те, що я з'ясував у кількох абзацах. Зауважте, що частина коду в цьому поясненні з тих пір вийшла з реального коду.

По- перше, p, q, h, P, Q, sі M. p- кількість символів від кінця лівої гілки /до правого кінця лівого піддерева. q- кількість символів від лівого кінця правого піддерева до кінця правої гілки /. h- кількість гілок між коренем і підтрубками. Pі Qє лише обертами pта qкорисними для розміщення проміжків навколо /\гілок до кореня n. s- кількість пробілів, які додаються між двома підрядками. Mнайпростіший; це довжина n. Поставте графічно:

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

Формула для визначення pтака:, p = len(x[0]) - x[0].rindex(str(A)[-1]) - (A<10)довжина, мінус нульовий індекс останнього символу в A, мінус поправка на одноцифрове As.

Формула для визначення qтака:, q = y[0].index(str(B)[0]) + (B>9)індекс першого символу в B, плюс поправка на нульову індексацію, мінус поправка на одноцифрове Bs (об'єднане в одну корекцію для багатозначного Bs).

Формула для визначення hполягає в наступному: h = H[0] or (p+q+M%2+2-M)//2 or 1. Або ми беремо з заздалегідь визначеного, Hщо означає, що ми перемальовуємо дерево, ми використовуємо (from_the_left + from_the_right + parity_space + 2 - len(root)) // 2), або ми використовуємо мінімальну кількість рівнів гілок, 1.

Формула для визначення sполягає в наступному: s = 2*h+M-p-q. Віднімаємо pі qвід кількості проміжків між гілками кореня на їх найширшому рівні 2*h + M.

Крок 4. І, нарешті, ми все це склали. Спочатку ми робимо корінь, [" "*(P+h)+Z+" "*(Q+h)]то покладе в гілках вниз поддерева, [" "*(P+J)+"/"+" "*(2*h+M-2*J-2)+"\\"+" "*(Q+J)for J in G(h)][::-1]і , нарешті , ми вкладаємо в наших правильно розташованих Піддерево, [(" "*(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à! Ми маємо собі естетично ділене дерево!

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

Чи може isdigitбути ваш чек '/'<x[i].strip()[0]<':'?
FlipTack

14

Математика, 96 86 81 79 78 байт

Дякую @MartinEnder за 2 байти.

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

Вихід виглядає приблизно так:

введіть тут опис зображення

Пояснення

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

Створіть список дільників вводу. Знайдіть елемент, найближчий до квадратного кореня вводу. ( Maxпризначено для вирівнювання виходу)

#2[#,#2/#]&

Знайдіть іншого дільника, поділивши вхід на дільник, знайдений вище, застосуйте вхід як голову результату.

#0/@

Повторіть процес.

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

Якщо введення є простим, нічого не робіть.

TreeForm

Відформатуйте вихід.

Редагувати: Естетичніша версія (258 байт)

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

Вихід виглядає приблизно так:

введіть тут опис зображення


3
Sqrt@#-> #^.5(звичайно, тоді ви не можете використовувати позначення інфіксації, Nearestале тоді ви можете використовувати Max@).
Мартін Ендер

5
Він дотримується правил, але це дерево далеко не естетично xD
Beta Decay

2
Краса в очах глядача :)
Нельсон

1
Я не впевнений, що це дійсно. На відміну від прикладів, вузли в кожному рядку не розташовані рівномірно. Крім того, рядки не підключаються до правильної цифри.
Mego

1
@Mego Ну, ОП сказав, що це дійсно.
Р. Кап

3

Вугілля деревне , 302 байт

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

Спробуйте в Інтернеті! Посилання на багатослівну версію коду. Оскільки багатослівна версія дуже багатослівна, він є транслітерацією JavaScript основного алгоритму:

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.