Construa uma árvore divisora ​​esteticamente agradável

43

Uma árvore divisora ​​esteticamente agradável é uma árvore de divisores de entrada nque, para qualquer número composto m, possui dois nós filhos que são o par de divisores mais próximos da raiz quadrada de m. O nó esquerdo deve ser o divisor menor de me o nó direito deve ser o divisor maior de m. Um número primo na árvore não deve ter nós filhos. Sua árvore pode estar na forma de arte de texto ou imagem. As regras para saída de arte de texto são as seguintes.

Regras de espaçamento

Para espaçar os nós na árvore, temos as seguintes regras:

  • Os nós em uma determinada profundidade da raiz devem estar todos na mesma linha de texto na saída.
  / \ NÃO / \  
 / \ / 3
2 3 2
  • Para nós esquerdos, a ramificação recebida deve estar no canto superior direito se o nó for um número de um dígito; caso contrário, logo acima do último dígito. Exemplo:
 / AND /
3 720
  • Para nós da direita, a ramificação de entrada deve estar no canto superior esquerdo se o nó for um número de um dígito; caso contrário, logo acima do primeiro dígito. Exemplo:
\ AND \
 7 243
  • Para ramificações esquerdas de saída, a ramificação deve começar um espaço à esquerda do número. Exemplo:
  275
 /
11
  • Para ramificações direitas de saída, a ramificação deve começar um espaço à direita do número. Exemplo:
275
   \
   25
  • Quaisquer dois nós no mesmo nível da árvore devem ter no mínimo dois espaços entre eles. Ao mesmo tempo, quaisquer duas subárvores no mesmo nível da árvore devem ter o menor espaço possível entre elas.
Esta árvore não funciona porque as ** subárvores ** estão muito próximas.

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

Embora essa árvore tenha espaço suficiente entre seus galhos.

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • Se quaisquer duas subárvores estão muito próximos em uma árvore, eles podem ser separados por adição de outra linha de ramos /\da árvore acima dos pais.
   441                              
  / \ A última linha ainda não foi preenchida e já estamos sem espaço.
 21 21
/ \ / \

Adicione outra linha de ramificações

     441                              
    Quase \, mas o 7 e o 3 estão muito próximos.
   / \ Mais uma linha deve fazê-lo.
  21 21
 / \ / \
3 7 3 7

Adicione outra linha de ramificações

      441
     / \ E terminamos.
    / \
   / \
  21 21
 / \ / \
3 7 3 7

Exemplos

Como um exemplo completo, a árvore divisora ​​de 24 terá a seguinte aparência:

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

4 e 6 são o par de divisores mais próximo da raiz quadrada de 24. 4 está à esquerda, porque é menor. Na próxima linha, o número 2 à esquerda de 3, porque é menor.

A árvore divisória para 63 deve se parecer com:

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

Na árvore incorreta, 3 e 21 não são o par de divisores mais próximos da raiz quadrada de 63 e 3 e 7 não são classificados corretamente. O posicionamento da filial no 21 está correto, no entanto.

Para 42, você deve ter:

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

Vamos dar uma olhada no 720. Observe que precisamos de cinco níveis de ramificações 720para que as subárvores 24e 30sejam espaçadas corretamente. Além disso, nota que 24e 30têm dois níveis de ramos, porque 4e 6tem nós filhos que precisam espaçamento correto e os nós filhos de 30necessidade de estar no mesmo nível que os nós filhos de 24.

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

O desafio

  • Sua tarefa é criar uma árvore divisora ​​esteticamente agradável, espaçada corretamente para entrada n, em que num número inteiro positivo seja maior que 1.
  • Sua saída pode conter espaços à esquerda e à direita e novas linhas à esquerda e à direita, mas deve estar em conformidade com as regras de espaçamento fornecidas acima.
  • É permitido que sua saída seja: arte do texto, uma imagem (outros formatos a serem adicionados, se necessário).
  • Para imagens, verifique se os nós da árvore estão bem espaçados e se os nós na mesma altura na árvore estão na mesma altura na imagem.
  • Isso é código de golfe. O menor número de bytes (ou equivalente) vence.

Agradecemos a Stewie Griffin por pensar nessa idéia e muito obrigado a Peter Taylor, Martin Ender, Mego e Eᴀsᴛᴇʀʟʏ Iʀᴋ por sua ajuda na reescrita da especificação. Como sempre, qualquer sugestão ou correção é muito apreciada. Boa sorte e bom golfe!

Mais casos de teste:

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
fonte
Obrigado por este desafio. Agora eu possa visualizar estas coisas sem chamar-los cada vez: D
Conor O'Brien
A árvore precisa se parecer com os exemplos ou posso usar a função interna do Mathematica? Parece que este , mas com fatoração.
JungHwan Min 1/10/16
@JHM Eu sabia que deveria ter mantido a tag de saída gráfica . Sim, você pode usar esse built-in. Vou editar o desafio.
Sherlock9

Respostas:

29

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

Após quatro meses tentando escrever essa resposta, colidindo com uma parede, deixando-a por uma semana, enxágue, repita, finalmente terminei uma resposta artística ASCII adequada para esse desafio. Tudo o que resta é o golfe, e assim, sugestões de golfe são muito bem-vindas. Experimente online!

Golfe: -60 bytes ao renomear algumas funções usadas com frequência e alterar como o resultado é retornado. -73 bytes, alterando como as alturas das subárvores são verificadas, como as variáveis ​​de espaçamento são calculadas e como o resultado é retornado. -3 bytes da isdigit()substituição do FlipTack . -16 bytes jogando essa isdigit()substituição ainda mais e substituindo "" por E. -5 bytes de pequenas melhorias e mudança de Python 3 para Python 2. -7 bytes de modificar como o resultado é retornado. -8 bytes de uma pequena alteração na forma como Aé definido, alterando como Té definido e adicionando W, usando a hipótese de que qualquer subárvore com pelo menos uma ramificação mais longa que sua contraparte, seja necessariamente mais longa do que sua contraparte , removendoQcompletamente e editando como o resultado é retornado. -10 bytes de usar em A<10vez de L(S(A))<2para Ae B. -8 bytes de alterar o padrão Hpara, [0]uma vez que o código evita o problema de argumentos padrão mutáveis ​​nunca mudando H, alterando como qé definido usando em (B>9)vez de 1-(B<10), removendo pcompletamente e criando Fcomo substituto p+q-M.

Correções de bugs: a hipótese estava errada, contra-exemplo 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)))]

Explicação

Toda a função pode ser resumida em quatro etapas:

  1. Determine o maior par divisor de n, Ae B.
  2. Crie as subárvores de Ae B, redesenhando conforme necessário.
  3. Determine o número de espaços que devem ir entre as subárvores.
  4. Desenhe e retorne a nova árvore divisória.

Vou seguir cada passo em ordem.

Etapa 1. Este é o passo mais fácil, francamente. Verifique cada número zentre 1 e a raiz quadrada quanto à divisibilidade ne pegue a maior ze a n//zque corresponder. Retornar apenas str(n)se nfor primo (um A==1ou B==n)

Passo 2. Desenhe as subárvores de Ae Be obter o número de /\ramos entre os nós no sub-árvores. Para fazer isso, obtemos os índices de cada etapa que possui dígitos, obtemos as primeiras diferenças dos índices e subtraímos 1 novamente. Quando tivermos as alturas, as comparamos para obter a maior e redesenhamos as subárvores com as novas alturas.

Eu tenho uma suspeita furtiva de que a subárvore mais alta em geral sempre tem ramificações iguais ou iguais às ramificações na subárvore mais curta, e posso usá-la para jogar golfe no código, mas ainda não tenho prova disso. Contra-exemplo em 11**9 = 2357947691.

Etapa 3. Essa etapa foi o que levou meses para ser escrita. A Etapa 2 levou alguns dias para escrever e depurar, mas encontrar as fórmulas corretas para o espaçamento levou séculos. Vou ver se consigo condensar o que descobri em alguns parágrafos. Observe que alguns dos códigos nesta explicação foram extraídos do código real.

Primeiro, p, q, h, P, Q, se M. pé o número de caracteres do final da ramificação esquerda /até a extremidade direita da subárvore esquerda. qé o número de caracteres da extremidade esquerda da subárvore direita até o final da ramificação direita /. hé o número de ramificações entre a raiz e as subárvores. Pe Qsão apenas as inversas de pe qe são úteis para colocar os espaços em torno /\ramos até a raiz n. sé o número de espaços adicionados entre as duas subárvores. Mé mais simples; é o comprimento de n. Coloque graficamente:

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

A fórmula para determinar pé a seguinte: p = len(x[0]) - x[0].rindex(str(A)[-1]) - (A<10)comprimento, menos o índice zero do último caractere em A, menos uma correção para um dígito As.

A fórmula para determinar qé: q = y[0].index(str(B)[0]) + (B>9)o índice do primeiro caractere em B, mais uma correção para indexação zero, menos uma correção para um dígito Bs (combinada em uma correção para vários dígitos Bs).

A fórmula para a determinação hé a seguinte: h = H[0] or (p+q+M%2+2-M)//2 or 1. Ou pegamos um predefinido, o Hque significa que estamos redesenhando a árvore, usamos (from_the_left + from_the_right + parity_space + 2 - len(root)) // 2)ou usamos o número mínimo de níveis de ramificação, 1.

A fórmula para a determinação sé a seguinte: s = 2*h+M-p-q. Subtraímos pe qdo número de espaços entre os ramos da raiz em sua maior extensão 2*h + M.

Etapa 4. E, finalmente, reunimos tudo. Primeiro criamos a raiz, [" "*(P+h)+Z+" "*(Q+h)]depois colocamos os galhos nas subárvores e [" "*(P+J)+"/"+" "*(2*h+M-2*J-2)+"\\"+" "*(Q+J)for J in G(h)][::-1], finalmente, colocamos nossas subárvores espaçadas corretamente [(" "*(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à! Temos uma árvore divisória esteticamente agradável!

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
fonte
Poderia isdigitser o seu cheque '/'<x[i].strip()[0]<':'?
FlipTack
14

Mathematica, 96 86 81 79 78 bytes

Obrigado @MartinEnder por 2 bytes.

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

A saída é assim:

insira a descrição da imagem aqui

Explicação

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

Gere a lista de divisores da entrada. Encontre o elemento mais próximo da raiz quadrada da entrada. ( Maxé para aplainar a saída)

#2[#,#2/#]&

Encontre o outro divisor dividindo a entrada pelo divisor encontrado acima, aplique a entrada como a cabeça do resultado.

#0/@

Repita o processo.

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

Se a entrada for primária, não faça nada.

TreeForm

Formate a saída.

Edit: Uma versão mais esteticamente agradável (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])&

A saída é assim:

insira a descrição da imagem aqui

JungHwan Min
fonte
3
Sqrt@#-> #^.5(é claro que você não pode usar a notação infix, Nearestmas pode usar Max@).
Martin Ender
5
Ele segue as regras, mas essa árvore está longe de ser esteticamente agradável xD
Beta Decay
2
A beleza está nos olhos de quem vê :)
Nelson
1
Não tenho certeza se isso é válido. Diferentemente dos exemplos, os nós em cada linha não são espaçados igualmente. Além disso, as linhas não se conectam ao dígito correto.
Mego
1
@ Mega Bem, OP disse que era válido.
R. Kap
3

Carvão , 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⊕§ηι↓

Experimente online! Link é a versão detalhada do código. Como a versão detalhada é muito detalhada, ele é uma transliteração JavaScript do algoritmo principal:

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
fonte