Favicon
NSI Première

Algorithmes sur les tableaux - Correction

Recherche d'une valeur dans un tableau

Algorithme "naïf"

Recherche de la présence ( ou pas ! )


from random import randint

def recherche_seq(tableau, valeur) :			
	""" Fonction qui recherche séquentiellement la présence d'une valeur dans un tableau d'entiers.
	Entrées :
		tableau = le tableau (list)
		valeur = la valeur à rechercher (int)
	Sortie :
		booléen indiquant la présence ou non de la valeur dans le tableau (bool)
	"""
	i = 0                    # indice de parcours du tableau
	trouve = False           # drapeau de présence ou d'absence de la valeur
	while i < len(tableau) and trouve == False:
		if element == valeur:
			trouve = True
	    i += 1
	return trouve			
			
tableau = [randint(1, 100) for i in range(50)]
print(recherche(tableau, 12) # par exemple...			
			

Recherche de toutes les occurrences d'une valeur

Ici, il est nécessaire parcourir le tableau en intégralité : une boucle for est donc mieux adaptée; et comme ce sont les indices des éléments ayant la valeur recherché qui nous intérèsse(nt), un parcours par indice est judicieux.


from random import randint

def recherche_seq_occurrences(tableau, valeur):			
	""" Fonction qui recherche séquentiellement les occurrences d'une valeur dans un tableau d'entiers
	Entrées :
		tableau = le tableau (list)
		valeur = la valeur à rechercher (int)
	Sorties :
		le tableau des indices des éléments ayant la valeur recherchée (list)
	"""
	occurrences = []
	for i in range(len(tableau)) :
		if tableau[i] == valeur :
			occurrences += [i]
	
	return occurrences			
			
tableau = [randint(1,100) for i in range(50)]
print(recherche(tableau, 12) # par exemple...			
			

Comparaison avec l'algorithme utilisé par Python


from random import randint
from timeit import timeit

def recherche_python(tableau, valeur) :
    return valeur in tableau  # renvoi direct de l'évaluation de l'expression booléenne !

# Programme principal

tableau = [randint(1,100) for i in range(50)]

t1 = timeit(stmt = 'recherche_naive(tableau, 15)', globals=globals(), number = 1000)
print(t1)

t2 = timeit(stmt = 'recherche_python(tableau, 15)', globals=globals(), number = 1000)
print(t2)				
			

0.001952268999957596
0.0007598840002174256

>>> 		
			

→ on constate que les durées mesurées changent d'une exécution à l'autre, mais que celle de la version Python est toujours plus petite que la version "naïve" : l'algorithme utilisé par Python doit donc être plus efficace...

Extremum ( maximum ou minimum ), moyenne

Recherche du maximum


def maximum(tableau):
    """
        Fonction renvoyant le maximum dans un tableau d'entiers
        Entrée :
        	tableau = tableau d'entiers (list)
        Sortie :
        	la valeur maximale dans le tableau (int)
    """
    maximum = tableau[0] # maximum initialisé à la valeur du premier élément du tableau
    for element in tableau:
        if element > maximum:
            maximum = element
    return maximum	
			

Recherche du minimum


def minimum(tableau):
    """
        Fonction renvoyant le minimum dans un tableau d'entiers
        Entrée :
        	tableau = tableau d'entiers (list)
        Sortie :
        	la valeur minimale dans le tableau (int)
    """
    minimum = tableau[0] # initialisé avec la valeur du premier élément du tableau
    for element in tableau:
        if element < minimum:
            minimum = element
    return minimum		
			

Calcul d'une moyenne


def moyenne(tableau):           
    """
        Fonction renvoyant la moyenne d'un tableau d'entiers
        Entrée :
        	tableau = tableau d'entiers (list)
        Sortie :
        	la valeur moyenne du tableau (float)
    """
    
    somme = 0
    
    for element in tableau:
        somme += element
        
    return somme / len(tableau)	
			

Applications

Mot le plus long


def mot_le_plus_long(tab):

    longueur_max = len(tab[0])        # la taille du premier élément du tableau

    mot_max = tab[0]                  # le premier élément du tableau

    for mot in tab :                  # pour chaque mot dans le tableau

        if len(mot) > longueur_max :  # si le mot est plus long que le plus grand actuel

            longueur_max = len(mot)   # alors on stocke sa longueur
            mot_max = mot             # et le mot lui même

    return mot_max, longueur_max      # renvoi des résultats


print(mot_le_plus_long(['chaton', 'licorne', 'ratatouille', 'bip'])) # ('ratatouille', 11)
print(mot_le_plus_long(['chatons', 'licorne', 'or', 'car', 'etoile'])) # ('chatons', 7) 
print(mot_le_plus_long(['licorne', 'chatons', 'or', 'car', 'etoile'])) # ('licorne', 7) 
			

Deux plus grandes valeurs


def deux_max(tab):
    """
    Renvoie les deux premiers maximum ( = les deux plus grandes valeurs )
    dans un tableau.
    Entrée : un tableau d'entiers ( list de int ou de float )
    Sortie : les deux plus grandes valeurs ( tuple de int ou de float )
    """
    max1 = tab[0] 
    max2 = tab[0]
    for valeur in tab:
        if valeur > max1:
            max2 = max1
            max1 = valeur
        elif valeur > max2:
            max2 = valeur
    return max1, max2
    		

Face au Soleil

On utilise en fait ici le même algorithme que la recherche du maximum : on parcourt le tableau des buildings de gauche à droite, et à chaque fois que le maximum est modifié, cela signifie que l'on a trouvé un building "plus haut que le plus haut" rencontré jusqu'ici, c'est à dire plus grand que tous ceux situés à sa gauche : ce building verra donc forcément le Soleil; on incrémente donc le compteur des buildings face au Soleil.


def building_sunset(buildings):
    """
    Renvoie le nombre de buildings qui verront le soleil.
    Entrée : un tableau dont chaque élément est la taille d'un building ( list de int)
    Sortie : le nombre de buildings qui voient le soleil ( int )
    """
    n = 1                # compteur de buildings face au Soleil : celui situé tout à gauche verra forcément le Soleil !
    h_max = buildings[0] # initialisation de la hauteur maximale courant ( celle du premier building au début )
    
    for h in buildings:
        if h > h_max:
            n += 1
            h_max = h 
    
    return n			


def building_sunset_v2(buildings):
    """
    Renvoie les indices des buildings qui verront le soleil.
    Entrée : un tableau dont chaque élément est la taille d'un building ( list de int)
    Sortie : le tableau des indices des de buildings qui voient le soleil ( list de int )
    """
    n = [0]
    h_max = buildings[0]
 
    for i in range(len(buildings)):
        if buildings[i] > h_max:
            n += [i]
            h_max = buildings[i]
    return n		
    
    
b1 = [4, 2, 5, 4, 6]
print(building_sunset_v2(b1))	# renvoie 3
print(building_sunset_v2(b1))	# renvoie [0, 2, 4]
		

Complexité en temps : on parcourt les n éléments du tableau dans tous les cas, la complexité est donc O(n).

Profit maximum

Méthode par force brute

On utilise ici deux boucles imbriquées :

On peut ainsi tester toutes les possibilités de profit si on vend à l'indice j après avoir acheté à l'indice i, et on cherchera alors le maximum parmi toutes ces possibilités.

	
def profit_maximum(cours):
    """ Fonction qui détermine le profit maximum en achetant à une date, puis en vendant à une date ultérieure
    Entrée :
        cours = le tableau des valeurs de l'action (list de int)
    Sorties :
        le profit maximum, la date d'achat et la date de vente pour obtenir ce profit ( tuple de 3 int ).
    """
    
    profit_max = 0
    achat = 0
    vente = 0
    
    for i in range(len(cours)-1):        # on s'arrête à l'avant-dernier élément du tableau ( on ne peut pas acheter et vendre le même dernier jour !)
        for j in range(i+1, len(cours)): 
            profit = cours[j]-cours[i]
            if profit > profit_max:
                profit_max = profit
                achat = i
                vente = j
    
    return profit_max, achat, vente
            

Complexité en temps : plus délicat !!...

On parcourt les n-1 éléments du tableau, et pour chacun de ces éléments, on parcourt le reste du tableau : on parcourt donc globalement n x n éléments ( un peu moins en réalité, voir ci-dessous ), soit opérations de comparaison → la complexité est O(n²).

Plus précisément :

  • pour i = 0 → n-1 éléments restent à parcourir,
  • pour i = 1 → n-2 éléments restent à parcourir,
  • pour i = 3 → n-3 éléments restent à parcourir,
  • etc...
  • pour i = n-2 → 2 éléments restent à parcourir,
  • pour i = n-1 → 1 seul élément reste à parcourir.

En tout, il faut donc parcourir : 1+2+....+(n-3)+(n-2)+(n-1); en maths, on montre que cette somme est égale à : n(n-1)/2.
→ le coût en temps est donc égal exactement à (n²-n)/2, la complexité est O(n²).

On retiendra très généralement que la présence de deux boucles imbriquées donne une complexité en O(n²) à un algorithme.

Méthode plus subtile


def profit_maximum_v2(cours):
    profit_max = 0
    achat = 0
    vente = 0

    for i in range(len(cours)):
        if cours[i] < cours[achat]:
            achat = i
        else:
            profit = cours[i]-cours[achat]
            if profit > profit_max:
                profit_max = profit
                vente = i

    return profit_max, achat, vente
            

C'est un algorithme qui revient donc à chercher en même temps le maximum et le minimum dans le tableau...

Intérêt : comme on ne parcourt qu'une seule fois le tableau, la complexité est cette fois en O(n).

Plus grande période de gel

On a besoin ici de déterminer DEUX valeurs différentes :


def longueur_gel(temperatures):
    """
    Renvoie la durée de la plus grande période de gel, c'est à dire le plus grand nombre de jours consécutifs 
    où la température est restée inférieure ou égale à 0°C.
    Entrée : 
        le tableau des températures des jours successifs ( list de int )
    Sortie : 
        la durée de la plus longue période de gel ( int )
    """
    maxi = 0               # durée de la PLUS GRANDE période de gel
    l = 0                  # durée d'une période de gel QUELCONQUE

    for t in temperatures: # pour chaque température dans le tableau,
        if t <= 0 :        # si la température est négative,
            l += 1         # on incrémente la durée de la période actuelle.
            if l > maxi:   # a-t-on trouvé une longueur plus grande que la maximum déterminé jusque là ?
                maxi = l   # dans ce cas, la longueur de la période actuelle devient le nouveau maximum.
        else:              # sinon  ( sous-entendu : si la température n'est pas inférieure ou égale à 0 ),
            l = 0          # alors on réinitialise le comptage de la durée de la période à 0.

    return maxi
    		

Suivi par un GPS de sport

Meilleur 100m

Il s'agit simplement d'une recherche de minimum dans un tableau :

			
def meilleur100m(L):
    mini = L[0]
    for hecto in L:
        if hecto < mini:
            mini = hecto
    return mini			
			

Meilleur km

Force brute

Plus délicat : c'est aussi une recherche de minimum, mais d'un minimum calculé à partir de 10 éléments successifs du tableau, sur toutes les "plages" possibles de 10 éléments successifs dans le tableau :


def meilleurkm(L):
    
    minikm = 100**100          # valeur suffisamment grande pour qu'on trouve ensuite forcément une plus petite !
    
    for i in range (len(L)-9): # attention, il faut parcourir le tableau jusqu'à 9 éléments avant le dernier sinon 'Index out of range'....
        
        mkm = 0
        for j in range(10):    # boucle de calcul du temps au kilomètre, donc sur 10 éléments successifs à partir de l'indice i;
            mkm = mkm + L[i+j] # revient donc à calculer : L[i+0] + L[i+1] + L[i+2] +.... + L[i+9]
            
        if mkm < minikm:    # test si le temps calculé est un nouveau minimum
            minikm = mkm
    
    return minikm				
			
Soyons plus subtils...

En y réfléchissant bien...on fait beaucoup de calculs inutiles : à chaque nouvelle "plage" de 10 éléments successifs, il n'y a que deux valeurs qui changent, la première et la dernière; tous les autres éléments ont été déjà inclus dans la calcul du kilomètre précédent !

Meilleur km plus subtil !

C'est donc un peu idiot de refaire totalement le calcul sur 10 éléments : il suffit en fait, à chaque nouvelle "plage", de retrancher la première valeur et d'ajouter la nouvelle au km calculé sur la "plage" précédente :


def meilleurkm_v2(L):
    debut = 0   # indice de début de "plage" de calcul 
    fin = 9     # indice de fin de "plage" ( entre 'debut' et 'fin' inclus, il y aura toujours 10 éléments)
    
    # initialisation du meilleur km :
    # c'est, au début, celui calculé sur les 10 premiers éléments du tableau
    km_min = 0
    for i in range(debut, fin+1):
        km_min += L[i]
    
    km = km_min
    while fin < len(L)-1:   # on parcourt ensuite le reste du tableau; 
        km = km - L[debut]  # pour calculer le nouveau km, on retranche au km précédent la première valeur,
        debut += 1          # on incrémente 'debut'
        fin += 1            # et 'fin' pour "avancer" la plage de calcul de 1 élément,
        km = km + L[fin]    # et on ajoute au calcul du nouveau km la valeur du nouvel élément en fin de "plage".
        if km < km_min:     # on teste alors si
            km_min = km     # on a trouvé un meilleur km.
    
    return km_min
            

Intérêt de cette approche ?

Cette façon de faire, où l'on mémorise un calcul afin de ne pas avoir à le refaire par la suite, s'appelle programmation dynamique; cette notion est au programme de Terminale.