Aller au contenu

Arbre ternaire de Berggrens

Définition

Un arbre ternaire est une structure qui ressemble aux arbres binaires, mais au lieu de deux sous-arbres s'il est non vide, c'est trois sous-arbres : à gauche, au milieu et à droite.

Un arbre ternaire est, ou bien :

  • un arbre vide, qu'on peut noter nil,
  • sinon, il possède un nœud particulier, sa racine, et exactement trois sous-arbres indépendants, un à gauche, un au milieu et un à droite, qui sont des arbres ternaires.

Nouvelle modélisation

Cet exercice n'utilise pas la classe Noeud pour construire un arbre.

Cet exercice utilise un tuple ((a, b, c), gauche, milieu, droite) pour désigner un nœud

  • portant l'étiquette (a, b, c) ; les côtés d'un triangle rectangle,
  • pointant vers trois sous-arbres : à gauche, au milieu, à droite.

None désignera encore directement un arbre ternaire vide, comme toujours tant qu'on ne travaille pas avec une classe Arbre 😉.

Ainsi, si arbre est un arbre ternaire non vide, on pourra réaliser un dépaquetage (unpack) pour accéder aux différentes composantes.

🐍 Script Python
triplet, gauche, milieu, droite = arbre
a, b, c = triplet

L'arbre ternaire de Berggrens

Un triplet pythagoricien primitif est un triplet d'entiers \((a, b, c)\) qui sont les côtés d'un triangle rectangle avec \(a^2 + b^2 = c^2\), et tels que \(a\) et \(b\) sont premiers entre eux (\(1\) est le seul diviseur commun à \(a\) et \(b\)).

Pour un triplet pythagoricien \((a, b, c)\), on définit trois nouveaux triplets de la manière suivante :

  • le triplet \((a_0, b_0, c_0)\) tel que :
    • \(a_0 = +a-2b+2c\),
    • \(b_0 = +2a-b+2c\),
    • \(c_0 = +2a-2b+3c\)
  • le triplet \((a_1, b_1, c_1)\) tel que :
    • \(a_1 = +a+2b+2c\),
    • \(b_1 = +2a+b+2c\),
    • \(c_1 = +2a+2b+3c\)
  • le triplet \((a_2, b_2, c_2)\) tel que :
    • \(a_2 = -a+2b+2c\),
    • \(b_2 = -2a+b+2c\),
    • \(c_2 = -2a+2b+3c\)

On admet qu'on obtient trois nouveaux triplets pythagoriciens primitifs.

En partant de \((3, 4, 5)\), on obtient l'arbre ternaire de Berggrens qui est infini. Il regroupe tous les triplets pythagoriciens primitifs. C'est un excellent outil pour résoudre certains problèmes où on cherche des distances entières dans un réseau à mailles carrées.

En tronquant l'arbre de Berggrens à la hauteur 3, on obtient (sans dessiner les nil):

graph TB
    R("(3, 4, 5)")
    R --> R0("(5, 12, 13)")
    R0 --> R00("(7, 24, 25)")
    R0 --> R01("(55, 48, 73)")
    R0 --> R02("(45, 28, 53)")
    R --> R1("(21, 20, 29)")
    R1 --> R10("(39, 80, 89)")
    R1 --> R11("(119, 120, 169)")
    R1 --> R12("(77, 36, 85)")
    R --> R2("(15, 8, 17)")
    R2 --> R20("(33, 56, 65)")
    R2 --> R21("(65, 72, 97)")
    R2 --> R22("(35, 12, 37)")

On obtient cet arbre avec le code suivant :

🐍 Script Python
def berggrens(a, b, c, h):
    if h == 0:
        return None
    else:
        a_0 =   +a - 2*b + 2*c
        b_0 = +2*a -   b + 2*c
        c_0 = +2*a - 2*b + 3*c

        a_1 =   +a + 2*b + 2*c
        b_1 = +2*a +   b + 2*c
        c_1 = +2*a + 2*b + 3*c

        a_2 =   -a + 2*b + 2*c
        b_2 = -2*a +   b + 2*c
        c_2 = -2*a + 2*b + 3*c

        return ((a, b, c),
            berggrens(a_0, b_0, c_0, h - 1),
            berggrens(a_1, b_1, c_1, h - 1),
            berggrens(a_2, b_2, c_2, h - 1),
        )

Ainsi que l'appel >>> berggrens(a=3, b=4, c=5, h=3)

Tronquer au périmètre 130

Au lieu de tronquer à la hauteur 3, on pourrait tronquer au périmètre 130 et obtenir (sans dessiner certains nil):

graph TB
    R("(3, 4, 5)")
R --> R0("(5, 12, 13)")
R0 --> R00("(7, 24, 25)")
R00 --> R000("(9, 40, 41)")
R00 --> R001(" ")
R00 --> R002(" ")
R0 --> R01(" ")
R0 --> R02("(45, 28, 53)")
R --> R1("(21, 20, 29)")
R --> R2("(15, 8, 17)")
R2 --> R20(" ")
R2 --> R21(" ")
R2 --> R22("(35, 12, 37)")

Il y a 8 (vrais) nœuds dans cette troncature. (nil n'est pas un vrai nœud).

Exercice

  1. Modifier la fonction berggrens

    • qui prend désormais en paramètres a, b, c un triplet pythagoricien et p un entier
    • et qui renvoie un arbre de Berggrens tronqué de tous les triplet qui ont un périmètre inférieur ou égal à p.
    • ⚠ Une docstring sera attendue à cette fonction ! Elle devra commencer par Renvoie ⚠
  2. Coder une fonction taille qui prend un arbre ternaire en paramètre et qui renvoie son nombre de nœuds.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : /
.128013,59/fq7B8r;nb N_o=ylaepcwgu)vd*4613kRméhtsP(S0+2è-i:050E0w0P0v0Z0u0Q0o0y0u0v0Q0Q0s010P0Z0x010406050Q0B0M0M0v0k0t040T0r0u0B0^0r0m050e0 1113150}0x04051l1e1o0e1l0}0E0Z0D0-0/0;0?0O0Z0A0O0u1C0O0P0{050(0n0u0w1x0:0=011B1D1F1D0P1L1N1J0P0k1m0P0O1P1z010f0*0w0m0v0M0w010-180Q0x0v0m0?0W1J1`1|1*1R1-1N1:1=0{0a0o0R0k0r0x0r0Q0Z1b0m0o0$1^0k0k0w0y2j1e210m1m0e1(2w1#1%1$1K0E230?1F0m1/2g1J1u1w0.1Q2G0Z2I0m0r2M1J0x2p1m2u2w2!0~1{2k2O1+2T0k120u0{0o0I2t2(0|2%222*1R2,2.2:0W2?1|2^2u2F012}0v2/040o0J312v0}342{0?37390o0G3d332(353j2:0c3n3f3p3h360r2-382:0H3u2_2)1y2|3z2~3a0h3E3g3H3i3J3B3a0j3N3w3P3y3A3k0d3V2`3X3r040I0U3$3G2P3Y3K0I2=1f2@3v3%3/3)0I303@323_3.2+3R390I3c3 3e3F3q440{0I3m483o3`433Z4d3t4g414b4k3*3D4n4a3x3|3M4t3O3{4c3*3U4y3W4A4q0I3#4E4i3I4q0W3,4K424M3K0W3?2!4o4v4B0W3~4W4u3(4Z474$4z4j4T4f4+4F4-3S0W4m4:4L3Q4N4s4_4R4{4T4x4~4p4T4D534Y4N4J2$1r2Y1e2M2z0E1%2E3x0y2U1?1m5f1n5d5b2$5l0$2Z4;1R0K0{0$0f3n4%3/0z2:5D4,2|0f0{0n0w0k0A0A2p0m0Q5I5x0?0`040S5V4`360{0v5#4 015Y0b3n0o5E2+5M5*355-5/5;2|0{0y5@3x5_4g5:5J3i0{0x5 3X5Y0C0!3u0o6e635W010Q1 04010L1/0D0r0Z1O0u00130n2p0o0P5O1:0Z6x2m0i5O5Q5S0,1#1c0g0B0N0o0v0B0o0x0N0k0Z0M0X1#1O0`6d6f5{0?5z040Z5C626(5%045)6.64010r0{0V5`6@0m5?6?6h6_046{705$6~045~755+72020A0P0l6|6h77674g6/5Y6c4n6f7q6g5$6*2p0P0B0k1d7a350K0y0{0p1c0w6$6e6/6*0w0+7H7l6@7n7I7r6/770v0q4P2!7s7b0{0s5/7J6@72742$6}5(7h5$720Y7:5+0M0Z0{4#2@6/720F683{6 7Z7~6`7@357_7{811+7 8b5|787S7q7U5M7X873x727%7A8n867P6h89047|328504808t767/8q3X7=7(7!3q832@8J8r738m3X8v8x2v8z8B7-7i5}8h6%7.788l8F3/8o8Q8*8s8X5$8S8e0?8d8C5+7V8,8c0{7?8)1+8;8^358@8/8_8L328N8G8.8M6/8v4*7}7*0{8W9g8Y8g4n067r8#9l7W4V9c9h048p7Z7)719b8y8$6=849v7,9u8u7`8w8=6^9i9M770n8{1R7+9S0?9195939O924v8Z7p9p9982040n0q9t988z9x9I7;9B8U6@9X9k9@8A9P8E9F9A8P629z8D9,9V9Na3a18:9K8T5w9}9j9C9l794W9)9*5=8%9/2van9T7$a89U9#8Rad9M949|966;av9^3a9dazax8-9~aKao9R8 ataa9?7^9K9faiag9 9m4W9o9)8j6;0qaeas8?aua4a,a98~9Y9$aEaQa-aS9:9`aJa?9aaMa 9+aPab7#a{araI8aaNaRah9_aj8!a5aD9-a+9;aF04a=aC88a~bp8ObdafaD9EaT9Zb73abh8Ka7a_a99Ha|9Jbab28|b1bs3(9%ala%8$0ya*bm9=bI9}boaXaUbKbOaLbua(bxbYb6bHb8a}b%b#bzb*8$b4by8Ob/aHb;04aWbeaYbb65a!3^a$9p7K0{7v7x7zb55^0{0S5!c46:b,c25+61cfa@b`b?600{5.bF77akb(1+6acwbRambCcr6G5R1/5Uck5YcjbL8f7W7YcA1Rcpb{bP9,8(cP5Xcva8cycZcTc#04cDcW9+7kc!5,0{0Cc-40cF9qa65N5PcJ5T9McNaZ9sd1c$cx8kaqbvcgc,c%5}9.d5dcd704c:c*c=04c@bg7Tb_cI6IdgcOdl7VbVcMd6cqcXbjdgc^b:ajdyc;cVb-bDdkct69c?dF0|am7mc?3u4X3X6*5B9M5GaHck0m5L040(0*1Ndud30k6w7OdJc?7ocEa:6j0{6m6o6q6s0v6y0)0u1N2l000B2k6v6x6z7y0)2p017Sca6+6-dB9+ebd?c.8cd$0ZcLem1+7C7E7Gdgd_3^dT6@7u0%cda88vcSc_c{aD1#0_1N0PdEdd040A6Q0y0OepdO3/dKdGa67_1F0w0BeSdi0E2d2ieZe%b6bXe?bDeoeieEcbeG7yeI9Kd9a:awev8fd,e5e=dacu5ZaZeV0BeXfadUdnbmb}a:77f8d.dzfdd(0{e)6re,fr0CfleTfpfi7QchaZe/6q6zdgfz4t0e5u0w2w2XfP5e1v5g2z2C2x0v1MfS0e5f0}f$0%e40Q04.