Aller au contenu

Algorithmes de tri⚓︎

On souhaite trier une liste L, c'est-à-dire réarranger ses éléments pour qu'ils soient rangés dans l'ordre croissant. Par exemple, si L = [5, 1, -4, 2, -8, 7] alors un algorithme de tri doit permettre d'obtenir la liste [-8, -4, 1, 2, 5, 7]. Dans ce TP, nous étudions plusieurs algorithmes de tri. Pensez à bien tester toutes vos fonctions.

Tri par sélection⚓︎

Le tri par sélection consiste à chercher le minimum que l'on met en 1ère position de la liste, puis le 2ème plus petit élément en 2ème position...

Exercice⚓︎

Écrire une fonction minimum(L, i) qui renvoie l'indice du minimum de la liste L à partir de la position i. Par exemple, si L = [5, 1, -8, 2, -4, 7], minimum(L, 0) doit renvoyer 2 (correspondant à L[2] = -8) et minimum([5, 1, -8, 2, -4, 7], 3) doit renvoyer 4 (correspondant à L[4] = -4).

Exercice⚓︎

Écrire une fonction tri_selection(L) qui trie une liste L en utilisant le tri par sélection. On pourra compléter le code suivant :

Python
1
2
3
4
def tri_selection(L):
    for i in range(len(L)):
        m = minimum(L, i)
        # échanger L[i] et L[m]

Tri par insertion⚓︎

Le tri par insertion consiste à trier progressivement la liste L, de gauche à droite. Plus précisément, à l'étape i, les i premiers de L sont triés et on fait en sorte d'insérer le i+1ème élément L[i] au bon endroit pour que les i+1 premiers éléments soient triés.

Voici les étapes du tri par insertion pour la liste L = [5, 1, -4, 2, -8, 7] : - i = 0 : on insère L[0] = 5 de façon à ce que le 1er élément de L soit trié (il n'y a rien à faire : un élément seul est toujours trié). L vaut [5, 1, -4, 2, -8, 7] (on met en gras la partie de L déjà triée). - i = 1 : on insère L[1] = 1 au bon endroit, ce qui donne [1, 5, -4, 2, -8, 7] - i = 2 : on insère L[2] = -4 au bon endroit, ce qui donne [-4, 1, 5, 2, -8, 7] - i = 3 : on insère L[3] = 2 au bon endroit, ce qui donne [-4, 1, 2, 5, -8, 7] - i = 4 : on insère L[4] = -8 au bon endroit, ce qui donne [-8, -4, 1, 2, 5, 7] - i = 5 : on insère L[5] = 7 au bon endroit, ce qui donne [-8, -4, 1, 2, 5, 7]

On a terminé, et la liste est bien triée.

Exercice tri insertion⚓︎

Complète le code de la fonction tri_selection

###(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 : 5/5
.128013it3a;dv,n2S5éwbçcy14q: f-up08)_j9eklohrP=[sà6(]/mg7050g0I0c0e0b0K0R0x0r0K0e0R0R0P010c0b0B010406050R0A0X0X0e0N0s040l0L0K0A0@0L0j050W0~1012140|0B04051k1d1n0W1k0|0g0b0h0,0.0:0=0.0j0Y0A0e0Y0I0z0B0s0c0M1b0x0M0b0Y0M0K1P0M0c0`050%0p0K0I1w0/0;011O1Q1S1Q0c1Y1!1W0c0N1l1K0,170R0B0e0j0=0k011$1y010y0)0I0j0e0X0I1W1{1}221(251!282a0`0a0x0O0N0L0B0L0R0b1a0j0x0#1_0N0N0I0r2v1d2d0j1l0W1K2I1=1@1?1X0g2f1z0b0j272s1W1t1v0-1%2S2U0j0L2Y1W0B2B1l2G2I2/0}1|2w2!232(0N110K1W0e1N2B0y0=030F0F0r2)0I1S2%0L0z0t3d0`0x0t1d0e2:2?0{2=2e2^1(2`2|2~300I32013436383a2V3d0z20040x0k3j3l1}3n2G2R013s0e2}1l2 0M313335370#3C2(3E0d3g0d3K2F3m0|3O3q0=3R3T053V3X3y3Z3B2T3D3e0u3g0u3,1e3.3o2@1x3r0L2{3S3u3W3w3Y3A3#3~3%3e0m3g0m442/3/2?3P3?4e3`3z3!394k3c3e0T3g0T4q463:493=4b3t3U3v3x4y3}3b3E0Z3g0Z4H3M4s3p4K3Q4M4d4O4f4Q3|4j4T3e0D3g0D4Y2H4!482#4%4c3@3_4g3{4i4A4/0z0H3g0H4@3N4t3;4|4N3^4P4h4z3$4C3d0C0`0t0C594_4u4(4~5g515i4B3E0t0t5n3i0W3k3-4Z475s4}4w504R4.3 3d3G0t3J5E3L4^5I5c4v4*4x4-535P0t3)045)5q5X4$5Z5f4+5h4S5(415+435U5G5W4J4{5:4 4,525j5z4n5+4p5|455H5 2_5t5L635x540t4E5+4G6a4r5.606f5!5M5$653e0t4V5+4X6o3m1o2-1d2Y2L0g1@2Q5c4z2X1u1l2,0I2.6D6b2H054z6T2e0b0g0=352G5z3u6#6%645y6x212j0I6-6i5(1W5|6d1(0J0`0#0y5}6Z4`230o3g726q2_0y0`1=0b0F2T0R0I0N2E6V733P0_040U786|3=0`0K7r5b4$7o0w720x793r0p7u0b0R0c7w4#4{7o0E7A7l0|7l5I6,016(2?3E3G5f7U6v6/3F6;296?7V6.547Z6{7x4{763H0x7^7K741(0R0g0`020v0A0L0c0f80828486830f727R2;3O7#0F6)3e5*7!6$7,6^4l0z3)0x6=6@5@8p8k7:7L237}3g7^0x0X2)0b251#0K02030d0H0f4b0g2B2x0I0+0n0K0n2a0j0c0+2y0K0x2r0A0N0x818J2 1S7I1#0R0L2u0x7d0n0I8c7`0x8g8i0z5_8l8u5O8p418s7*965%986`5F7s018B7@7^8988818a9m8b7Q8 917X4m6+8m7$544n9a2a9c6w0z673K8d6D8f9y8h9v0z6l958n8v5k4E9C7+9z5P9Q5U8D7C9h0j7c0(0K1!7B7D0=0L0`0P9.9(7F042i8 5c7o7q7S9(7u9|7y0`0E8~a04t9u1}4U9x9E7%4V9Wae546z3K9$9/010r5B04030x0p0L0A0-1#810b0x0Y2p0:8_0.8)120@1#8{1#0S2x2p2u0I0i0x0e0A2x0n0p190x0A2w7h0A8(8W8Y277J9sa86!9M924;4O8g8o5k4;ah9S97a`9f9k9%7;236~040y4b9@b33r0`0bb98z1(0L7?2Tbe7{3=9_0N1}1Fa37M0`9 8eba0=0X0b5nbr237o0ibBbb040%0)9-a/bl017N7P6pbL90a;9O56a@9Ma_3E56a|9Y8pbV9I9tbTab6x5mbWai5(5mb#7-b;b09$ama1040Gbk3P9;049?7lb2bf7t04bdc4anap0`as0GaW0r0Na+8!1#271_10270q1}0caH8,aG0h3S0I8+8T0x8K8M8O2T1t0r1#8%8L8N0fa*8ZcsaN7g0n2B0Na7bva:6-925Aada}9d5kc!b?bY6xaqb)bRaa0j5z7Z2 a^9Tc=7)9Dc$9F5Sb_b`7_9hccar0g0n0r3S1EcI8J0Q0b0V0x0h7i0+aG0YaU0r0M1#0G0A0R81cK8OaN0R2 0B0.cHaS1|8,0B7i0X190e2v0x0~0rcHaEdh0+0edh0rdL1McNa,cAdlawdocV9Ka9b+c;6x8kc@bXc_d,c{9Xb@8p5)d0d1anb50o1ObK2/c5bM9)b}b 5cc1020Y84e64$by0`5pca9hbh0`1}0gec60a2bR9}0`0QbFc7b~ehbw01c10zen23ee5+eubN0`0VeCbg7 ea9re1ane47veqa404eteTeoe5eXbCeIbP46c/d*5z94d.b:d^998tc}7%0t949#d1b`eQepcWbM7oeWe~4u0`ewePei0`eBexc601eE5Df2er040VbEfae3e}d(fbf0eGe4f5fne eIeK9:9=fw3Qfm3Manfpe!bGfsfC9h7ofifzeReGfEff5/f4fzeAfzfdfOfva.f2c:66c#b$c(9Be;f(f$2I3ke`e{b|fH2He2c0f8c3f6eyfWfZftbScY9O6kf%d@c(9Vf+g65z9!3k9JfId)g2b,3dake-e=6jagg9c+gjd`f^6Maqas1|8$1#2B0c8+0jaSaG8;0ccl7I8E8G258|ctcB2 b70j2D0b1bd%fC0W6Y6E6S6G6P1d0c6Jg(2O2J0e1Zg#0W6H7R0#bI0*04.

Exercice tri de 3 valeurs source codex⚓︎

Le tri du drapeau hollandais en bref

On l'utilise dans le cadre des tableaux ne contenant que trois valeurs distinctes (petite, moyenne et grande).

Il a pour avantage d'être de coût linéaire et de trier le tableau en un seul passage.

On partage le tableau en quatre zones :

  • les petites valeurs,
  • puis les valeurs moyennes,
  • puis les valeurs pas encore triĂ©es,
  • enfin les grandes valeurs.

Schéma

Schéma

L'algorithme s'arrĂŞte lorsque la zone des valeurs Ă  trier est vide.

On souhaite trier un tableau ne contenant que trois valeurs a, b et c répétées un nombre quelconque de fois. On souhaite trier ce tableau afin de placer au début les a suivis des b et enfin des c.

Par exemple, avec nombres = [2, 0, 2, 1, 2, 1] on a a = 0, b = 1 et c = 2. Le tableau trié est [0, 1, 1, 2, 2, 2].

Dans ce cas de figure, on peut utiliser le tri du drapeau hollandais.

Remarque

Ce tri a été inventé par Edsger Dijkstra qui était de nationalité néelandaise.

Il a donné son nom à l'algorithme en référence aux trois couleurs du drapeau néerlandais : Rouge, Blanc, Bleu.

Drapeau hollandais

L'idée est de maintenir quatre zones dans le tableau :

  • la première ne contient que des a,
  • la deuxième que des b,
  • la troisième des valeurs pas encore triĂ©es,
  • enfin la dernière zone ne contient que des c.

Ces zones sont délimitées par les bornes suivantes (toutes incluses):

  1. de l'indice 0 Ă  prochain_a - 1,
  2. de prochain_a Ă  actuel,
  3. de actuel + 1 Ă  prochain_c,
  4. de prochain_c + 1 Ă  len(tableau) - 1

Les quatre zones Les quatre zones

L'algorithme fonctionne ainsi :

  • Les variables prochain_a et actuel sont initialisĂ©es Ă  0,

  • La variable prochain_c est initialisĂ©e Ă  len(tableau) - 1,

  • On rĂ©pète les actions suivantes tant que la zone des valeurs restant Ă  trier est non vide :

    • On lit l'Ă©lĂ©ment Ă  l'indice actuel :

      • Si c'est un a, on le rajoute Ă  la fin de la zone des a en Ă©changeant les Ă©lĂ©ments d'indices prochain_a et actuel (Ă©tape 3 sur les figures),
      • Si c'est un b, on la laisse Ă  sa position (Ă©tape 2 et 6),
      • Si c'est un c, on la place Ă  la position prĂ©cĂ©dant le dĂ©but de la zone des c en Ă©changeant les Ă©lĂ©ments d'indices prochain_c et actuel (Ă©tape 1, 4 et 5),
      • Dans tous les cas, on prend soin de mettre Ă  jour les diffĂ©rents indices afin de reflĂ©ter les nouvelles Ă©tendues des zones.

Les figures ci-dessous illustrent le tri du tableau [2, 0, 2, 1, 2, 1] :

  • Étape 1

    Étape 1 Étape 1

    On lit un 2 : on le place au début de la zone des c.

    prochain_c est décrémenté.⚓︎

    • Étape 2

    Étape 2 Étape 2

    On lit un 1 : on le laisse Ă  cette position.

    actuel est incrémenté.


  • Étape 3

    Étape 3 Étape 3

    On lit un 0 : on le place Ă  la fin de la zone des a.

    prochain_a et actuel sont incrémentés.


  • Étape 4

    Étape 4 Étape 4

    On lit un 2 : on le place au début de la zone des c.

    prochain_c est décrémenté.


  • Étape 5

    Étape 5 Étape 5

    On lit un 2 : on le place au début de la zone des c.

    prochain_c est décrémenté.


  • Étape 6

    Étape 6 Étape 6

    On lit un 1 : on le laisse Ă  cette position.


  • Étape 7

    Étape 7 Étape 7

    actuel est strictement supérieur à prochain_c.

    L'algorithme se termine.

###(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 : 5/5
.128013ita;,EnD2Rê7+14 fIj08eloHr=[ûà]/mC3dv.S5éwbcyq:-up)_9hxPès6(gk050K0w0c0d0b0x0*0q0S0x0d0*0*0B010c0b0Y010406050*0X0H0H0d0A0T040N0y0x0X120y0h0q020d0H0Y0e0q0k0w1c0A0U0X0w0*050G191b1d1f170Y04051K1D1N0G1K170K0b0L0`0|0~100|0h0-0X0d0-0w0W0Y0T0c0$1m0q0$0b0-0$0x1?0$0c15050=0R0x0w1W0}0 011=1@1_1@0c1 211}0c0A1L1.0`1i0*0Y0d0h100j01231Y010r0@0w0h1q0w1}2l2n2s252v212y0H2A040a0q0(0A0y0Y0y0*0b1l1n0:2j0A0A0w0S2V1D2C0h1L0G1.2+2f2h2g1~0K2E1Z0b0h2x2S1}1T1V0{242^2`0h0y2~1}0Y2!1L2)2+3b182m1n302t340A1c0x1}0d1;2!0r10030!0!0S350w1_330y0W0j0W0o150q0o1D0d3c3f163e2D3h253j3l3n3p0w3r013t3v3x3z2{3C3C3G0j3J3L2n3N2)2@013S0d3m1L3o0$3q3s3u3w0:3$343(0J3G0J3,2(3M173:3Q103?3^053`3|3Y3~3#2_3%3D0p3G0p471E493O3g1X3R0y3k3@3U3{3W3}3!404m423D0O3G0O4s3b4a3f3;4e4C4i3Z3 3y4I3B3D0+3G0+4O4u4b4x4d4z3T3_3V3X4W4l3A3(0m3G0m4)3.4Q3P4,3=4.4B4:4D4=4k4H4^3D0v3G0v4}2*4 4w31524A4f4h4E4j4G4Y5a0W0#3G0#5f3/4R4c5k4/4g4;4F4X414!3E0u150o0u5x5h4S535m5E5p5G4Z3(0o3F045Y5O4v5Q5l4U5o4?594n3E2q5!3+0G3K484~5%5A4T554V585r5.0o445!465?3-5g5`515|5D565F4@614p5!4r665^684+5j6b5n575q5H5X4L5!4N6k4t3.1O391D2~2.0K2h2?5A4X2}1U1L380w3a3M6l1L4X6Q2D0b0K103u2)5X3U6X6Z6s5W3D3F0q2I0w6)5V5s5Z476n2t0.150:0r6S695j0Q3G6 6_3R0r152f0b0!0J0!0L3@0w0X0A1C6z2*702t14040,745z6a15340H0R2!7j3d75107o0Z0V6S177k6V1n6(016!3f3(5:5D7L5 6t3D2q6.2z6;6f4J3)2+3K0q7)0q7m3R15380y0S0$0?0h0!0d6S7+7B010y150B7`7,100H0b155N7I7H7A4R7S0!6#3D637R6Y7M6*5s447X2J7Z5-7#8g667*7{7s6o150d2$1A0x817|7~04807I8v505j84867G7r6W8i7N2n3(6h8h8p607#4p8n6:8j6=5.8V8t7*823=7.2P7;7?3v8D8w2t8F8H3b8J5i3i0R152H8P8~257o7q7I8-0h7u0y7w7y933;7D8@8K8_150W9i9483855!8O983:8c8e0W6v8W8%7!5I4L8#8X7U9x1}66896R9u8R8d7O4#6%9N8(7#4$9E9A8q5I4$7%048u8-6{040Q1=219n4S8y8A3y9.5A8F020x0c0e8{3M8}9/047/8;2_8?9t8^95157F889f9v9P0W4`4:8c9T5I4`9W7T6+af9I7(8u8,7|9)0b6~8I999b9d1B9f5A7o0CaD7t048z0c8BaH5j7o0F9?518`9}3.9 5A8M04878a9ja804aa4Pac9N9w5cah9S9B3(5cam8k5.a.3,asa|aWaI7v7xaCa6a$7C15aGb39o8.aJ9;8Cb89g150F0faR8x04b09ebeaEb6aN3i8/7:7=a47_bo51aPbj9k8GbB7-bl9cb17z9La7b504b7a#b99aa18:bv7@bxbPbf04bhbE4daAbIbra%bObKb4baaKaMbyaObg9sbXad8T3D5ua/9Fao5ua@aj3(b|a{a}9%7|bRa2bU7^b#7}150naU2*a~8L9q3Iabbeb_0h5X5Kb}9X8Y5I5M2r6/b~6?csc5c67)azbbaL9=ay8Ecfch9$8-aYcma*coa,ae5Y9RcA616-czcu9GcW9#c69(154Yax8|cGbmb2bXbpbNb)b$cHb:c?bzb?cKbLce8GcNcj2tcQc_017oa)4ucT6)9w0o7Q3oaia;6,7Wc#an6?7Q8+cEd5bFb/cJc/cL04cgcdd7cnb^cUb`3E8gdia:9Y5X8mdna^7#62aq9$a}c+043y0*0wd8dab@b,7KdFcq6,8VdJcYdQ8!dOc2d+dSdscFc8b%bnc}b=c^b;bsc{dwd%bYb!d0b-bRc;bJ6A7|aFd8c9bT8=0Sd!c dxd18`cde9bHd|e4c@b+ecd1egbueiekbZbie7bQd{c=etc~d d}e1dvbdeLa%aQdDe4cp6ucXc$ao0o9Dd;dk3E9ydrcEcGcaeAeE3;8F0Wd4cPcld$6A0G6U6B6P6D6M1D0c6Gf02;2,0d20e}0G6E1J7J3;2!0H0!0r0d0.0w0!0$631v1x1z1B0qdbe_1R1M040s0x0q0*001(2U0q0K0X0q7/200)2JfFfEfG7+1w040i0A0d0Y0w0dfH0z1i1!0K0?7jfR0q2_0L2x0c0PfI1d0q0g0M0q0i0b0t0.0*2f0df^1O3N1K0I22790q3o2R7hg87e0h0=1)fF000l2f222X3w0D0c0q3z0P0?2!0q0;fFg60A0b0w0Agqg6f621fYgv1n0X1ndY0Xfz2m0~1(0w0Mg2fbg5fW0hf%gefA1:1k0@0b0*0d2Vgvf~gq0b84f/220Egx0_2cfX0X0%2j1r2S0P0_2X2f0yg)0q7e217hh2g)120h2$1BgT1Q3Nf90;0?0^04.

Tri fusion (récursif)⚓︎

Le tri fusion sur une liste L consiste à : - Séparer L en deux listes L1 et L2 de même taille. - Trier récursivement L1 et L2 pour obtenir des listes triées L1' et L2' - Fusionner L1' et L2' pour avoir un tri de L.

Le tri fusion est donc récursif.

Voici une illustration des étapes avec la liste [23, 12, 4, 56, 35, 32, 42, 57, 3] :

Pour commencer, on souhaite écrire le code de la fonction fusion qui prend en paramètres deux listes d'entiers liste_a, liste_b triées par ordre croissant et les fusionne en une seule liste triée liste_triee qu'elle renvoie.

Exemples
Python
>>> fusion([1, 6, 10], [0, 7, 8, 9])
[0, 1, 6, 7, 8, 9, 10]
Python
>>> fusion([1, 6, 10], [])
[1, 6, 10]
Python
>>> fusion([], [0, 7, 8, 9])
[0, 7, 8, 9]
###(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 : 5/5
.128013it3a;dv,n2.S5wbcy+14: f-up08)_9eklohrP=[s6(]/mg7050g0G0c0e0b0I0P0w0q0I0e0P0P0N010c0b0A010406050P0z0U0U0e0L0r040m0J0I0z0;0J0j050T0{0}0 110_0A04051h1a1k0T1h0_0g0b0h0)0+0-0/0+0j0V0z0e0V0G0y0A0r0c0K180w0K0b0V0K0I1M0K0c0@050!0p0I0G1t0,0.011L1N1P1N0c1V1X1T0c0L1i1H0)140P0A0e0j0/0k011Z1v010x0$0G0j0e0U0G1T1^1`1 1#221X25270@0a0w0M0L0J0A0J0P0b170j0w0Y1?0L0L0G0q2s1a2a0j1i0T1H2F1/1;1:1U0g2c1w0b0j242p1T1q1s0*1!2P2R0j0J2V1T0A2y1i2D2F2,0`1_2t2X202#0L0~0I1T0e1K2y0x0/030E0E0q2$0G1P2!0J0y0Q0y0t0@0w0t1a0e2-2:0^2/2b2=1#2@2_2{2}0G2 01313335372S3a0y1}040w0k3h3j1`3l2D2O013q0e2`1i2|0K2~3032340Y3A2#3C0d3e0d3I2C3k0_3M3o0/3P3R053T3V3w3X3z2Q3B3b0u3e0u3*1b3,3m2;1u3p0J2^3Q3s3U3u3W3y3Z3|3#3b0n3e0n422,3-2:3N3;4c3^3x3Y364i393b0Q3e0Q4o443.473:493r3S3t3v4w3{383C0W3e0W4F3K4q3n4I3O4K4b4M4d4O3`4h4R3b0C3e0C4W2E4Y462Y4#4a3=3@4e3_4g4y4-0y0F3e0F4=3L4r3/4`4L3?4N4f4x3!4A3c0B0@0t0B574@4s4$4|5e4 5g4z3C0t3d045y5o455q4{4u4~4P4,3}3c3E0t3H0T3i3+4X5D5a4t4(4v4+515K0t3%5A3)5P3J4?5T4!5V5d4)5f4Q5!3 5A415)5R5+4H4_5.4}4*505h5x4l5A4n5`435S5}2?5r5G615v520t4C5A4E684p5,5~6d5W5H5Y633b0t4T5A4V6m4G595-6q5/5X625w6v4/5A4;6A6a6C6p5F6r6f5=4j3c545A566N5|6P6c6R6F6s6H520k5k046-571l2*1a2V2I0g1;2N5a4x2U1r1i2)0G2+3k5{1i4x742b0b0g0/322D5x3s7b7d6+5!1~2g0G7j6g7l2F5Q6b1#0H0@0Y0x766o200o3e7A7u3:0x0@0x0z2q187F6$1#0?040R7O4Z5~0@1P0P0c0G0E0e7U4^207R0i760w7B3p7X0b7Z7#0p7(3N7R0D0v760_692E5D7i017e2:3C3E5d836t6I3D7m267o847k6V885)0w8m7.7G3O0@0!0$1X7$7-7/0/0J0@0N8w8p0j0p7X247_5a7R7T80797)7:047Y7!8v8M8x017{7~7_8a0E7f3b5$897c8h7q6V3%0w7n7p6U5i8(8l8n8V0j8r0#0I8u7^8M8o7P8y8A8C933O8F8Q8H8U8p8K8I5-7;7?0E902.9d0@0D8Y9c4r8!8$0y5@8)8;5J6V3 8/8f9x5Z9z1T8^8m8`9h8S1/0b0G0G967V208z048B918V7R0O0S9p9l9r8*851`3C659w8+8=9+8e279D6u0y9,9H929R8P0b8T2,9{8O949U9Qa2010U0b0@5n8M7 9$7a9(8#864B7hag8,5i4C9B9=9.9yan9G3i8na14s0@9~9k3kax5a9T9Va08Va8aa9#753M9sai0y6x9-8b524Tap8gaT5KaR9`8V7w040o1L1Xa57`0@8Laea68{049~7%9W8p9T020I0c0fa,5U8|8t7#a^a:a-049oa_970J7D041`0gb04!9e9q9|3:az9jbh4_a{a}a babl8q048s8~7@9f4_7{7}ac8Zag9t6KaS8i5i4/aW9?8cbJ9`awa$az7zbua;9Kb4bB7*0@0Ob#9}9 aM977R9!bX3Nbra~bp2?bZbobka69Yb)bma?b`b68J0@0SbE6nb{0waO9*3b6XbKam3C54bOar9E5icdbSaw8_8Db_9M9Ob~019T0lcua=0e0A0A24bgc8c37Scyb_b5b,bvb}cF9gc0cK3K9Xc4b9c7b6ca0j5x6.ce9/6v5kciaY6V5mau3Fcoc:9JcQb@1#9T0saGaCaIa95AaLcSaNbHaP5yakbP6h3dc*bL5x5z3Ic:9I8pa%360P9PcObC0@c644c8cY5x882|8!cf6v1}dadx5Lc.dfcoc=8R7#csdlc24!cwcIbecBcDcubjdL7W8Q7=8SaBd1b-b%dOaAdScUd081d27j9t5#d6cj9@d:dAc%3c8@avdEcp97a=d(b:aE0@c`c@0/aJc bFdrd3cb3c9vdvald_0t9A8:d=8cejdDd~bva%a)23e6bwa@evb=btaHcqbx8}8ucRd,d#04dp5Sebd.d49,egd75!4ld^as64epc;eCdH0EdJcudNdmb^dPcC0jcEdUb$cHe)8Pe!eG8Nb7b(e=b exe|8Wd*eacXeccZ6v6jc$eVf69;aXdbfa7sc/eYd bne^aDdMe4c{3Kfl4_e83gf2cL2tds6vaReQem6haVelc+5i6weXfq20esa*dKc|eCe1eBbb0@a|b?e2cPby8 d)eJd+e_fx3cbJfAfF5xbNfEfdf)fIbTeZdXdI0L9NfNd!bve(e/8PcAe,e.fvb7a/g5b1dW9idZeHcMd$e e0c1g8bif1cWg5f(0tcdf+f:gpfbeRc,cmd|fhbvghgc3F8Vc_fo2EfJ1#fsf$82f487c#grdB6-gufB5KgSfff?97a%2y0c0z0L19fWdVe!e$fucS0T786=736@701a0c6`g_2L2G0e1Wg?0T6^7 0Yby0P04.