Démonstration interactive des algorithmes de tris¶

Dans ce notebook, nous présentons interractivement différent algorithmes de tris et de partitionement:

  • deux variantes du tri à bulles
  • le tri par insertion
  • partitionement de gauche à droite
  • partitionement des deux cotés
  • tri rapide

Nous n'avons pas pu faire de la même manière le tri par fusion car celui-ci n'est pas "en place.

Infrastructure pour "tracer" l'exécution des tris¶

Vous pouvez, sans aucuns problèmes, ignorer ces fonctions

In [1]:
def trace_algo(cmd):
    sort_gen = iter(cmd)
    def bla(cmd=selector(["Step"], 
                       buttons=True)):
        global dessin
        try:
            mess, dessin = next(sort_gen)
        except StopIteration:
            mess = "======== Finished ======="
        print(mess)
        dessin.show(figsize=(7,4))
    interact(bla)
In [2]:
SIZE_POINT=100
def plot_tab(tab):
    return sum(point(v, size=SIZE_POINT) for v in
                enumerate(tab))
def vert(i, tab, **opts):
    return line([(i, 0),(i, max(tab)+1)], **opts)
def horiz(i, tab, **opts):
    return line([(0,i),(len(tab)+1,i)], **opts)
In [ ]:
 

Astuce de point d'arret en Python : l'instruction yield¶

L'instruction yield en python est une sorte de "return" qui quitte temporairement la fonction. On peut ensuite reprendre l'exécution. Voici un exemple:

In [3]:
def carres(n):
    r"""
    Génère la suite des carrées des nombres de 0 à n-1
    """
    for i in range(n):
        yield i*i

On peut, par exemple, parcourir les différents résultats avec une boucle for:

In [4]:
for c in carres(5):
    print(c)
0
1
4
9
16

Ou les ranger dans une liste:

In [5]:
list(carres(9))
Out[5]:
[0, 1, 4, 9, 16, 25, 36, 49, 64]

Le fonctionnement repose sur le protocol suivant:

  • l'appel de la fonction "carres" retourne un objet appelé générateur
  • la fonction builtin "next" interroge l'objet pour avoir le résultat suivant
  • s'il n'y a plus de résultat une exception est levée
In [6]:
it = carres(4); it
Out[6]:
<generator object carres at 0x7f9bbceae500>
In [7]:
next(it)
Out[7]:
0
In [8]:
next(it)
Out[8]:
1
In [9]:
next(it)
Out[9]:
4
In [10]:
next(it)
Out[10]:
9
In [11]:
next(it)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
/tmp/ipykernel_8746/600241529.py in <module>
----> 1 next(it)

StopIteration: 

Note :¶

Il n'est pas important pour la suite de retenir ceci. On a juste besoin de savoir que yield nous permet d'introduire des points d'arrêt dans les fonctions.

In [ ]:
 
In [ ]:
 

Le tris à bulles

In [12]:
def bubble_sort(T):
    #########################################################
    ## dessin qui va être affiché lors des points d'arrêt ###
    ## vous pouvez complètement ingorer ce code.
    def dessin():
        return ("i=%i, j=%i"%(i,j), 
           sum(point((pos,v), size=SIZE_POINT, 
                color="blue" if pos <= i else "green")
               for pos, v in enumerate(tab))
            +vert(i+1/2,tab,color="green")
            +vert(j+1/2,tab))
    #########################################################
    
    yield "Start", plot_tab(T)          ## Point d'arret en début de boucle
    for i in range(len(T)-1, 0, -1):
        for j in range(i):
            yield dessin()              ## Point d'arret avant l'échange
            if T[j] > T[j+1]:
                T[j], T[j+1] = T[j+1], T[j]  
            yield dessin()              ## Point d'arret après l'échange
In [13]:
tab = [randint(0, 20) for i in range(10)]
In [14]:
tab
Out[14]:
[1, 0, 11, 8, 4, 20, 14, 11, 19, 10]
In [15]:
for _ in bubble_sort(tab): pass   # On ignore les points d'arrêt.
In [16]:
tab
Out[16]:
[0, 1, 4, 8, 10, 11, 11, 14, 19, 20]
In [17]:
tab = [randint(0, 20) for i in range(10)]
In [ ]:
 
In [18]:
trace_algo(bubble_sort(tab))
Interactive function <function trace_algo.<locals>.bla at 0x7fe323492ae8> with 1 widget
  cmd: ToggleButtons(d…
In [ ]:
 
In [ ]:
def bubble_sort_2d(T):
    #########################################################
    ## dessin qui va être affiché lors des points d'arrêt ###
    ## vous pouvez complètement ingorer ce code.
    def dessin():
        return ("i=%i, min=%i, max=%i"%(i,min,max), 
           sum(point((pos,v), size=SIZE_POINT, 
                color="blue" if min <= pos <= max else "green")
               for pos, v in enumerate(tab))
            + vert(min-1/2,tab, color="green")
            + vert(max+1/2,tab, color="green")
            + vert(i+1/2, tab, color="red")
            )
    #########################################################
    yield "Start", plot_tab(T)
    min = 0
    max = len(T)-1

    while (min < max):
        # Invariant les éléments d'indice < min ou > max sont à leurs places
        # Phase montante
        nborne = min
        for i in range(min, max):  # min, min+1, ..., max-1
            yield dessin()
            if (T[i] > T[i+1]):
               T[i], T[i+1] = T[i+1], T[i]
               nborne = i
        max = nborne
        # Phase descendante
        nborne = max
        for i in range(max-1, min-1, -1): # max-1, max-2, ..., min
            yield dessin()
            if (T[i] > T[i+1]):
                T[i], T[i+1] = T[i+1], T[i]
                nborne = i + 1
        min = nborne
        yield dessin()
In [ ]:
tab = [randint(0, 100) for i in range(10)]
trace_algo(bubble_sort_2d(tab))

Le tri par insertion


In [ ]:
def insert_sort(T):
    #########################################################
    ## dessin qui va être affiché lors des points d'arrêt ###
    ## vous pouvez complètement ingorer ce code.
    def dessin():
        return ("i=%i, j=%i, e=%i"%(i,j,e), 
            plot_tab(T)+vert(j, tab)+horiz(e, tab))
    #########################################################
    yield "Start", plot_tab(T)
    for i in range(1,len(T)):
        e = T[i]
        j = i
        yield dessin()
        while j>0 and T[j-1] > e:
            T[j] = T[j-1]
            j -= 1
            yield dessin()
        T[j] = e
        yield dessin()
In [ ]:
tab = [randint(0, 40) for i in range(10)]
trace_algo(insert_sort(tab))
In [ ]:
tab

Partitionement

méthode gauche à droite

In [20]:
def partition(Tab, P):
    c = 0; j = 0    
    #########################################################
    ## dessin qui va être affiché lors des points d'arrêt ###
    ## vous pouvez complètement ingorer ce code.
    def dessin():
        return (sum(point((i,v), size=SIZE_POINT, 
                   color="green" if P(v) else "red") 
            for i,v in enumerate(tab)) 
            + vert(c, Tab, color="green") + vert(j, Tab))
    #########################################################
    yield "Start", dessin()
    c = 0
    while c < len(Tab) and P(Tab[c]):
        c +=1
    yield   "c=%i"%c, dessin()+vert(c,Tab,color= "green")
    for j in range(c+1, len(Tab)):
        yield ("c=%i, j=%i"%(c, j), dessin())
        if P(Tab[j]):
            Tab[c], Tab[j] = Tab[j], Tab[c]
            c +=1        
            yield ("c=%i, j=%i"%(c, j), dessin())
    yield ("Retour %i"%c, 
        dessin()+vert(c-1/2, Tab, color="blue"))
In [21]:
tab = [randint(0, 20) for i in range(20)]
trace_algo(partition(tab, lambda x: x % 2 == 0))
Interactive function <function trace_algo.<locals>.bla at 0x7fe31d6682f0> with 1 widget
  cmd: ToggleButtons(d…
In [ ]:
 

Méthod des deux cotés

In [22]:
def partition2(Tab, P):
    #########################################################
    ## dessin qui va être affiché lors des points d'arrêt ###
    ## vous pouvez complètement ingorer ce code.
    def dessin():
        return (sum(point((i,v), size=SIZE_POINT, 
                   color="green" if P(v) else "red") 
            for i,v in enumerate(tab)) 
            + vert(i, Tab, color="green")
            + vert(j, Tab, color="red"))
    #########################################################            
    i = 0
    j = len(Tab)-1
    yield         "Start", dessin()
    while i <= j:
        while P(Tab[i]): i += 1
        while not P(Tab[j]): j -= 1
        yield      "i=%i, j=%i"%(i, j), dessin()
        if i<j:
            Tab[i], Tab[j] = Tab[j], Tab[i]
            yield "i=%i, j=%i"%(i, j), dessin()
            i += 1; j -= 1
    yield         ("Retour %i"%(i), 
            dessin() + vert(i-1/2, Tab, color="blue"))
In [23]:
tab = [randint(0, 20) for i in range(20)]

trace_algo(partition2(tab, lambda x: x % 3 == 0))
Interactive function <function trace_algo.<locals>.bla at 0x7fe31cd07ae8> with 1 widget
  cmd: ToggleButtons(d…
In [ ]:
 

Tri rapide (Quick Sort)

In [24]:
def quicksort(tab, min, max):    
    #########################################################
    ## dessin qui va être affiché lors des points d'arrêt ###
    ## vous pouvez complètement ingorer ce code.
    def dessin():
        return(
           "min = %i, max=%i, pivot=%i, i=%i, j=%i"%(
                min, max, pivot, i, j),
            sum(point((pos,v), size=SIZE_POINT, 
                color = 
                     "blue" if pos < min or pos > max
                     else 
                     "red" if v > pivot 
                     else 
                     "green") 
            for pos,v in enumerate(tab)) 
            + vert(i, tab, color="black")
            # + vert(j, tab, color="blue")
            + vert(min, tab, color="blue")
            + vert(max, tab, color="blue") 
            + horiz(pivot, tab))
    #########################################################           
    if min < max:
        pivot = tab[max]
        # Partitionnement
        i = min
        j = max-1
        yield dessin()
        while True:
            while tab[i] < pivot: i +=1
            while tab[j] > pivot: j -=1
            if i < j:
                tab[i], tab[j] = tab[j], tab[i]
                i += 1; j -= 1
            else:
                tab[i], tab[max] = tab[max], tab[i]
                break
        # Appel récursif
        yield dessin()
        for step in quicksort(tab, min, i-1): 
            yield step
        for step in quicksort(tab, i+1, max): 
            yield step
In [25]:
tab = [randint(0, 20) for i in range(15)]
print(tab)
for step in quicksort(tab, 0, len(tab)-1):
    pass
tab
[0, 17, 4, 13, 19, 15, 0, 12, 9, 20, 16, 19, 19, 5, 3]
Out[25]:
[0, 0, 3, 4, 5, 9, 12, 13, 15, 16, 17, 19, 19, 19, 20]
In [28]:
tab = [randint(0, 20) for i in range(20)]
trace_algo(quicksort(tab, 0, len(tab)-1))
Interactive function <function trace_algo.<locals>.bla at 0x7fe31d47c400> with 1 widget
  cmd: ToggleButtons(d…
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: