審美的に楽しい除数ツリーを構築する


43

美的に満足できる除数ツリーは、n任意の合成数に対して、の平方根に最も近い除数mペアである2つの子ノードを持つ入力の除数のツリーです。左ノードはのより小さな除数で、右ノードはのより大きな除数でなければなりません。ツリーの素数には、子ノードがないはずです。あなたのツリーは、テキストアートまたは画像の形式である場合があります。テキストアート出力のルールは次のとおりです。mmm

間隔ルール

ツリー上のノードの間隔を空けるために、次のルールがあります。

  • ルートから所定の深さにあるノードはすべて、出力の同じテキスト行にある必要があります。
  / \ NOT / \  
 / \ / 3
2 3 2
  • 左側のノードでは、ノードが1桁の数字の場合、着信ブランチは右上になければなりません。そうでなければ、最後の数字のすぐ上になります。例:
 /および/
3 720
  • 正しいノードの場合、ノードが1桁の数字である場合、着信ブランチは左上になければなりません。それ以外の場合、最初の数字のすぐ上にあります。例:
\および\
 7 243
  • 出ていく左の枝の場合、枝は番号の左側に1スペースを開始する必要があります。例:
  275
 /
11
  • 出て行く右の枝の場合、枝は数字の右に1スペースを開始する必要があります。例:
275
   \
   25
  • ツリーの同じレベルにある2つのノードの間には、少なくとも2つのスペースが必要です。同時に、ツリーの同じレベルにある2つのサブツリーは、それらの間のスペースをできるだけ少なくする必要があります。
**サブツリー**が近すぎるため、このツリーは機能しません。

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

このツリーには、ブランチ間に十分なスペースがありますが。

         504           
        / \          
       / \         
      / \        
     / \       
    / \      
   21 ... 24     
  / \ ... / \    
 / \ ... / \   
3 7 ... 4 6  
        ... / \ / \ 
        ... 2 2 2 3
  • 2つのサブツリーがツリー上で近すぎる場合/\、親の上のツリーにブランチの別の行を追加することにより、サブツリーを分離できます。
   441                              
  / \最後の行はまだ入力されておらず、すでにスペースが不足しています。
 21 21
/ \ / \

別のブランチの行を追加する

     441                              
    / \ほぼ、しかし、7と3は近すぎます。
   / \もう1行はそれを行う必要があります。
  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は小さいため、左側にあります。次の行の3の左にある数字2は、小さいためです。

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なるように、5つのレベルのブランチが必要であることに注意してください。また、とには2つのレベルのブランチがあることに注意してください。なぜなら、とは正しい間隔を必要とする子ノードを持っているためです。24302430463024

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

挑戦

  • あなたの仕事は、入力用に正しく間隔をあけた審美的に心地よい除数ツリーを構築することです。nここnで、1は正の整数です。
  • 出力には、先頭と末尾のスペース、先頭と末尾の改行が含まれる場合がありますが、それ以外の場合は上記の間隔規則に準拠する必要があります。
  • 出力は、テキストアート、画像(必要に応じて追加する他の形式)です。
  • 画像の場合、ツリーのノードが十分に間隔を空けており、ツリーの同じ高さのノードが画像の同じ高さにあることを確認してください。
  • これはコードゴルフです。最小バイト数(または同等)が勝ちます。

このアイデアを考えてくれたStewie Griffinに感謝します。また、仕様の書き直しに協力してくれたPeter Taylor、Martin Ender、Mego、EᴀsᴛᴇʀʟʏIʀᴋに感謝します。いつものように、提案や修正は大歓迎です。幸運と良いゴルフ!

その他のテストケース:

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:私は今、それらを毎回描画せずにこれらの事を視覚化することができます
コナー・オブライエン

ツリーは例のように見える必要がありますか、または組み込みのMathematica関数を使用できますか?このように見えますが、分解があります。
ジョンファンミン

@JHM グラフィカル出力タグを保持する必要があることはわかっていました。はい、そのビルトインを使用できます。チャレンジを編集します。
シャーロック

回答:


29

パイソン2711 651 575 559 554 547の 539 540 530 522バイト

この答えを書き、壁にぶつかり、数週間放置して、すすぎ、繰り返して、この答えを書き込もうとして4か月後、私はついにこの挑戦に対する適切なASCIIアートの答えを完成させました。残っているのはゴルフだけですので、ゴルフの提案は大歓迎です。オンラインでお試しください!

ゴルフ:よく使用される関数の名前を変更し、結果が返される方法を変更してから-60バイト。-73バイトは、サブツリーの高さのチェック方法、間隔変数の計算方法、および結果が返される方法の変更から。FlipTackのisdigit()代替品から-3バイト。-16バイトでそのisdigit()交換をさらにゴルフし、""で交換しEます。マイナーな改善とPython 3からPython 2への変更から-5バイト。結果の返り方の変更から-7バイト。A定義された方法の小さな変更から定義された方法の変更T、および対応するWものよりも少なくとも1つ長い分岐を持つサブツリーは必然的に対応するものよりも全体的に長いという仮説を使用してを追加して -8バイトQ全体、および結果が返される方法を編集します。for およびのA<10代わりに-10バイトを使用。-8バイトはデフォルトを変更しないため、コードは変更しないことによって可変デフォルト引数の問題を回避し、代わりにを使用して定義方法を変更し、を完全に削除して、の代わりとして作成します。L(S(A))<2ABH[0]Hq(B>9)1-(B<10)pFp+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)))]

説明

機能全体を約4つのステップに要約できます。

  1. 最大の除数のペアを決定しnAそしてB
  2. Aおよびのサブツリーを作成しB、必要に応じて再描画します。
  3. サブツリー間を移動するスペースの数を決定します。
  4. 新しい除数ツリーを描画して返します。

各ステップを順番に実行します。

ステップ1.これは最も率直な最も簡単なステップです。すべての番号をチェックz1の間とに割り切れるための平方根をnし、最大つかむzn//zそのマッチを。戻り値は、ちょうどstr(n)場合には、n(いずれかの素数ですA==1B==n

手順2.Aおよびのサブツリーを描画し、サブツリー内のノード間B/\ブランチの数を取得します。これを行うには、数字が含まれるすべてのステップのインデックスを取得し、インデックスの最初の差を取得して、再び1を引きます。高さを取得したら、それらを比較して最大値を取得し、新しい高さでサブツリーを再描画します。

全体的に背の高いサブツリーには常に短いサブツリーのブランチと同じかそれ以上のブランチがあるというこっそりした疑いがあり、それを使ってコードをゴルフすることはできますが、これの証拠はまだありません。で反例11**9 = 2357947691

ステップ3.このステップは、書くのに何ヶ月もかかったものです。ステップ2の作成とデバッグには数日かかりましたが、間隔に適した式を見つけるのには時間がかかりました。私が理解したことをいくつかのパラグラフに凝縮できるかどうかを確認します。この説明のコードの一部は、実際のコードからはみ出したことに注意してください。

まず、pqhPQsMpは、左ブランチの/終わりから左サブツリーの右端までの文字数です。qは、右サブツリーの左端から右ブランチの終わりまでの文字数/です。hルートとサブツリーの間のブランチの数です。Pそして、Qのちょうど逆でpqして前後にスペースを配置するのに便利です/\ルートまでの枝ns2つのサブツリーの間に追加するスペースの数です。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の最後の文字のゼロインデックス、1桁Aのsの修正を引いたもの。

決定の式qは次のとおりです。q = y[0].index(str(B)[0]) + (B>9)、Bの最初の文字のインデックス、ゼロインデックスの修正、1桁Bのsの修正(複数桁Bのsの1つの修正に結合)。

決定の式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とを減算します。q2*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)))]

ほら!審美的に楽しい除数ツリーがあります!

アンゴルフ:

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

Mathematicaの、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 とはほど遠い
ベータ崩壊

2
美しさは見る人の目にあります:)
ネルソン

1
これが有効かどうかわかりません。例とは異なり、各行のノードは等間隔ではありません。さらに、線は正しい数字に接続しません。
メゴ

1
@Megoまあ、OP それが有効だと言った。
R.ガプス

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.