Formation I.S.N.

Le principe de fusion

Illustration.

Vous pouvez observer ci-dessous la fusion de deux listes triées en action.

On dispose de deux listes triées : une verte et une jaune.

Cliquez sur le bouton "Manuel" pour dérouler les étapes à votre rythme et comprendre ces étapes d'exécution.

Le bouton "Automate" enchaîne les étapes avec un intervalle de temps d'environ 3 secondes entre deux étapes (vous pouvez réduire ce temps en cliquant sur la petite flèche du formulaire contenant le nombre 3000).


formulation de l'algorithme.

Formulez, en français, l'algorithme de fusion de deux listes triées.

  • une formulation
Entrée Deux listes triées L1, L2.
Initialisation L = [] (liste vide)
Traitement
  1. On sélectionne le plus petit des deux éléments en tête des deux listes L1, L2.
  2. On place cet élément en queue de la liste L et on le supprime de la tête de sa liste d'origine.
  3. On reprend au point 1 tant que les deux listes L1, L2 sont non vides.
  4. Si L1 est vide, on peut placer tous les éléments de L2 en queue de L.
  5. Si L2 est vide, on peut placer tous les éléments de L1 en queue de L.
Sortie L, liste triée contenant tous les éléments des deux listes d'entrée.

programmer.

Écrire un programme python :

Entrée Deux listes triées L1, L2.
Sortie L, liste triée contenant tous les éléments des deux listes d'entrée.

Le principe utilisé devant être, bien entendu, celui de la fusion exposé ci-dessus.

  • un code possible

On rappelle que liste.pop(0) (où liste est du type list) retourne l'élément de tête de la liste tout en supprimant cet élément de la liste.


from random import randint


def fusion(liste1, liste2) :
	L = []
	while liste1 != [] and liste2 != [] :
		if liste1[0] < liste2[0] :
			L.append(liste1.pop(0))
		else : 
			L.append(liste2.pop(0))
	# on 'concatène' L, liste1, liste2
	# (l'une des listes liste1, liste2 étant vide à ce stade)
	return L + liste1 + liste2


if __name__ == '__main__' :

	# tirage  de listes au hasard pour les entrées :
	L1 = [ randint(1,100) for _ in range(randint(5,15))]
	L2 = [ randint(1,100) for _ in range(randint(5,15))]
	# tri de ces deux listes :
	L1.sort()
	L2.sort()
	# affichage pour contrôle visuel après exécution :
	print("La liste L1 : ")
	print(L1)
	print("La liste L2 : ")
	print(L2)
	#fusion des deux listes :
	L = fusion(L1, L2)
	# affichage du résultat :
	print("Liste résultant de la fusion : ")
	print(L)

programmer.

Écrire un programme python :

Entrée Une liste Liste telle que la première partie (de l'indice 0 à l'indice d-1) soit triée (ordre croissant) et la seconde partie (de l'indice d à l'indice len(Liste)-1) soit triée (ordre croissant). d est la seconde entrée.
Sortie L, liste triée contenant tous les éléments de la liste d'entrée.

Le principe utilisé sera celui de la fusion. On s'interdira de créer des listes autre que la liste L.

  • un code possible

from random import randint


def fusion(Liste, d) :
	L = []
	i, j  = 0, d
	while i != d and j != len(Liste) :
		if Liste[i] < Liste[j] :
			L.append(Liste[i])
			i += 1
		else :
			L.append(Liste[j])
			j += 1
	for k  in range(i,d) :
		L.append(Liste[k])
	for k  in range(j,len(Liste)) :
		L.append(Liste[k])
	return L  
		

 


if __name__ == '__main__' :

	# tirage  de listes au hasard pour les entrées :
	L1 = [ randint(1,100) for _ in range(randint(5,15))]
	L2 = [ randint(1,100) for _ in range(randint(5,15))]
	# tri de ces deux listes :
	L1.sort()
	L2.sort()
	# affichage pour contrôle visuel après exécution :
	print("La liste L1 : ")
	print(L1)
	print("La liste L2 : ")
	print(L2)
	# liste concaténée :
	L = L1 + L2
	print("Liste en deux parties : ")
	print(L)
	#fusion des deux listes :
	L = fusion(L, len(L1))
	# affichage du résultat :
	print("Liste résultant de la fusion : ")
	print(L)



fusion de deux segments de liste.

Programmer l'algorithme suivant :
Entrée Une liste L de nombres, trois entiers p, q, r tels que :
  • p < q < r
  • les sous-listes [Lp, Lp+1, ..., Lq-1] et [Lq, Lq+1, ..., Lr-1] sont triées.
Sortie La liste L dans laquelle les sous-listes [Lp, Lp+1, ..., Lq-1] et [Lq, Lq+1, ..., Lr-1] ont été fusionnées.
  • un code possible

def fusion(L, p, q, r) :
	L3 = []
	i, j = p, q
	
	# boucle posant en queue de L3
	# les min des têtes des sous-listes L1, L2
	while i < q and j < r :
		if L[i] < L[j] :
			L3.append(L[i])
			i += 1
		else :
			L3.append(L[j])
			j += 1
			
	# une sous-liste étant vidée
	# on colle tout le contenu 
	# non encore traité de l'autre sous-liste
	# en queue de L3 
	if i == q : 
		for k in range(j, r) :
			L3.append(L[k])
	else :
		for k in range(i, q) :
			L3.append(L[k])
			
	# on copie dans la liste L initiale
	# la fusion obtenue :
	for k in range(p, r) :
		L[k] = L3[k-p]
		
	
# exemple d'utilisation : 
L = [2,8,1, 2,5,6,9,  5,7,8,11,  -5,12]
print(L)
fusion(L, 3, 7, 11)
print(L)

doubler la taille des segments triés.

On dira qu'une liste est triée k par k si les 'segments' successifs de longueur k dans la liste sont triés, c'est à dire L0 ≤ L1 ≤ ... ≤ Lk-1 puis Lk ≤ Lk+1 ≤ ... ≤ L2k-1 puis ... (le dernier segment étant éventuellement de longueur inférieure à k puisque la liste n'a pas nécessairement une longueur multiple de k).

Par exemple T=[2, 3, 7, 1, 5, 8, 9,11] est triée 3 par 3 puisque [2,3,7], [1,5,8] et [9, 11, /] sont triées. Mais elle n'est pas triée 4 par 4 puisque [2,3,7,1] n'est pas triée.

Programmer une fonction :

Paramètre d'entrée Une liste triée k par k (où k est un entier non nul)
Effet la liste initiale est triée 2k par 2k en sortie de fonction

On utilisera la fonction fusion de l'exercice précédent.

  • un code possible

La seule 'difficulté' est de ne pas oublier de tenir compte de la fin de liste avec le fait, notamment, que la longueur de la liste initiale n'est pas nécessairement multiple de k, ni de 2k.


def fusion(L, p, q, r) :
	L3 = []
	L1 = L[p:q]
	L2 = L[q:r]
	
	# boucle posant en queue de L3
	# les min des têtes des sous-listes L1, L2
	while L1 and L2 :
		if L1[0] < L2[0] : L3.append(L1.pop(0))
		else : L3.append(L2.pop(0))
			
	# une sous-liste étant vidée
	# on colle tout le contenu de l'autre
	# en queue de L3 
	if L1 : L3.extend(L1)
	else : L3.extend(L2)
			
	# on copie dans la liste L initiale
	# la fusion obtenue :
	del(L[p:q])
	L[p:q]=L3
		

def doublekTri(L,k) :
	p = 0
	q = min(p+k, len(L))
	r = min( q+k, len(L)) 
	while p < len(L)-1 :
		fusion(L, p, q, r)
		p = r
		q = min(p+k, len(L))
		r = min(q+k, len(L)) 




# exemple d'utilisation : 
L = [1,3, 2,8, 5,9,  -3,10,  1,5, 3]
print(L)
doublekTri(L,2)
print(L)