Lorsqu'on programme, il arrive souvent qu'on doive traiter des données organisées en plusieurs dimensions. Par exemple, imaginez que vous deviez parcourir un tableau en deux dimensions (comme une grille ou un échiquier), générer une table de multiplication ou dessiner des motifs complexes comme une pyramide d'étoiles. Dans ces cas-là, une simple boucle ne suffit pas, car il faut répéter des actions sur plusieurs niveaux.
C'est ici que les boucles imbriquées deviennent indispensables. Elles permettent d'effectuer des actions à l'intérieur d'autres actions répétées, comme parcourir chaque cellule d'un tableau ligne par ligne, ou manipuler des ensembles de données structurées. Les boucles imbriquées sont donc la solution parfaite pour répondre à ces besoins de traitement multiple et de manipulation de données complexes.
Autrement dit, dès que tu dois manipuler/traiter des données organisées en plusieurs dimensions ou à plusieurs niveaux, tu devras probablement utiliser plusieurs boucles imbriquées. Et tu utiliseras autant de boucles imbriquées qu'il y a de dimmensions/niveaux.
Dans cet article, nous allons explorer les boucles imbriquées en Python, comprendre leur utilité et illustrer leur fonctionnement avec des exemples concrets.
Les boucles imbriquées se produisent lorsque vous placez une boucle à l'intérieur d'une autre. Cela vous permet de parcourir des données en deux dimensions/niveaux, ou de répéter un ensemble d'actions de manière itérative en fonction de plusieurs critères.
La structure d’une boucle imbriquée en Python est assez simple :
for i in range(3): # Boucle extérieure
for j in range(3): # Boucle intérieure
print(f"i = {i}, j = {j}")
Dans cet exemple, la boucle extérieure s'exécute trois fois, et à chaque itération de celle-ci, la boucle intérieure est exécutée trois fois à son tour. Le résultat est une série d'affichages où i prend les valeurs 0, 1, 2, et pour chaque i, j prend également les valeurs 0, 1, 2.
Résultat :
i = 0, j = 0
i = 0, j = 1
i = 0, j = 2
i = 1, j = 0
i = 1, j = 1
i = 1, j = 2
i = 2, j = 0
i = 2, j = 1
i = 2, j = 2
Voyons maintenant quelques exemples concrets d’utilisation des boucles imbriquées.
Ecris un programme qui permet d'afficher un carré de caractères (en utilisant toujours le même caractère).
Exemple pour un carré de 5x5:
*****
*****
*****
*****
*****
Ecris un programme qui affiche un carré de nombres aléatoires.
Exemple pour un carré de 5x5:
5,7,8,9,5
3,2,4,7,5
0,9,8,7,5
3,8,2,1,0
9,6,3,5,4
Les boucles imbriquées sont également utiles pour manipuler des données sous forme de grille ou de tableau. Par exemple, nous pouvons afficher un carré de nombres consécutifs.
taille = int(input("Entrez la taille du carré : "))
compteur = 1
for i in range(taille): # Boucle pour les lignes
for j in range(taille): # Boucle pour les colonnes
print(f"{compteur:4}", end=" ") # Affichage du nombre
compteur += 1 # Incrémentation du compteur
print() # Changement de ligne
Résultat pour une taille de 4 :
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
Explication :
f"{compteur:4}" permet d'aligner les nombres correctement pour un affichage propre.Ici vous allez afficher un triangle d'étoiles.
** Exemple de sortie pour une hauteur de 5**:
*
**
***
****
*****
Un excellent exemple pour illustrer les boucles imbriquées est la création d'une table de multiplication. Dans cet exercice, nous allons afficher une table de multiplication allant de 1 à 10.
for i in range(1, 11): # Boucle pour les lignes
for j in range(1, 11): # Boucle pour les colonnes
print(f"{i * j:4}", end=" ") # Calcul du produit
print() # Changement de ligne après chaque ligne de la table
Résultat :
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
Explication :
i et j.end=" " dans la fonction print() empêche le saut de ligne automatique à la fin de chaque appel et aligne les résultats sur une seule ligne.Enfin, un dernier exemple pour manipuler des boucles imbriquées consiste à créer un damier de caractères. Alternons les caractères X et O dans un damier de 8x8.
for i in range(8): # Boucle pour les lignes
for j in range(8): # Boucle pour les colonnes
if (i + j) % 2 == 0:
print("X", end=" ")
else:
print("O", end=" ")
print() # Changement de ligne
Résultat :
X O X O X O X O
O X O X O X O X
X O X O X O X O
O X O X O X O X
X O X O X O X O
O X O X O X O X
X O X O X O X O
O X O X O X O X
Explication :
(i, j), si la somme des indices est paire, un X est affiché, sinon c'est un O.Pour afficher un véritable damier sur la console, vous pouvez remplacer les 'O' par des '░░' (alt-176) et les 'X' par des '▓▓' (alt-178).
Une adresse IP est composée de 4 nombres allant de 0 à 255, séparés par des points. Ex: 192.168.0.1. Elle possèdent donc 4 'dimensions' ou niveaux. Crée un programme capable de générer toutes les combinaisons possibles, depuis 0.0.0.0 jusqu'à 255.255.255.255.
Cela pourrait par exemple être utilisé pour scanner un réseau et détecter les équipements accessibles. Tu verras que ça prend du temp pour générer et afficher les 2554 combinaisons possibles. Dans ce cadre, on essaie généralement de se limiter à des ranges plus restreints. On pourrait par exemple scanner toutes les adresses du range 192.168.*.* ou encore 10.10.10.* .
Une attaque par force brute est une méthode pour deviner un mot de passe ou une clé en essayant toutes les combinaisons possibles jusqu'à ce que la bonne soit trouvée. Bien que cette méthode puisse être lente et inefficace face à des mots de passe longs ou complexes, elle reste une technique basique utilisée par les attaquants.
L'objectif d'une attaque par force brute est simple : tester chaque combinaison possible d'un ensemble de caractères jusqu'à trouver la bonne. Plus le mot de passe est long et plus l'ensemble de caractères est grand, plus l'attaque prendra du temps.
Pour générer toutes les combinaisons de caractères d'un mot de passe, on peut utiliser des boucles imbriquées. Imaginons que nous souhaitions tester toutes les combinaisons possibles d'un mot de passe composé de 4 caractères. Si les caractères disponibles sont [a-z], nous pouvons générer toutes les combinaisons possibles avec 4 boucles imbriquées.
Indice:
import string
print(string.ascii_letters) # lettres de a-z en minuscules et majuscules (52
print(string.ascii_lowercase) # lettres de a-z en minuscules (26 possibilités)possibilités)
Crée un algorithme à base de boucles imbriquées qui permet de générer toutes les combinaisons de lettres possibles (de a à z en minuscules).
Les mots de passe sont généralement cryptés avant d'être stocker sur un ordinateur. Une forme de cryptage qui était couramment utilisée est le hash md5. Il s'agit d'une fonction appliquée à un texte qui génère une clé "unique", le hash, à partir de ce texte. Ce cryptage est irréversible. C'est à dire qu'il est impossible de retrouver mathématiquement le texte original depuis le hash généré.
Voici un exemple de fonction pour calculer le hash mld5 d'un texte en Python:
import hashlib
def md5(texte):
# Créer un objet MD5
hash_md5 = hashlib.md5()
# Mettre à jour l'objet MD5 avec le texte (encodé en bytes)
hash_md5.update(texte.encode('utf-8'))
# Récupérer la valeur du hash sous forme hexadécimale
md5_hash = hash_md5.hexdigest()
return md5_hash
Ta misison: tu as récupéré une série de mots de passes hashés sur un serveur. Retrouve les mots de passe originaux (4 lettres) qui correspondent aux hash suivants:
881cc4157ed641a365a86452f27ed74540ea57d3ee3c07bf1c102b466e1c30918d777f385d3dfec8815d20f7496026dca7c3c2aa70d99921f9fb23ac8738299702c425157ecd32f259548b33402ff6d3Maintenant, allons plus loin:
trouve un moyen de mesurer le temps que prend ton programme pour trouver le mot de passe (en millisecondes).
mesure les temps d'exécution pour trouver les 5 mots de passe précédents.
double le nombre de combinaisons possibles en utilisant des lettres minuscules et majuscules
mesure les temps d'exécution pour trouver les 5 mots de passe précédents.
teste avec ce hash: 3f85a60ff25237dc58f2ad63a1c75e78
maintenant, on refait la même chose avec 5 caractères (minuscules et majuscules). Trouves les mdp suivants:
a85e7c8ba38d00af2f47681123ae4ec2522a8fa409901e49937e4261e60d1713** Que remarques-tu au niveau du temps d'exécution, d'abord en ayant doublé le nombre de caractères et ensuite en ayant ajouté 1 seul caractère?
Ces exemples sont limités à des mots de passe d'une longueur fixe. Dans la pratique, les mots de passe peuvent être plus longs et l'ensemble des caractères beaucoup plus large (lettres majuscules, minuscules, chiffres, symboles, etc.). Pour générer des mots de passe de longueur variable, il est préférable d'utiliser une approche récursive ou de gérer dynamiquement les boucles.
Les boucles imbriquées sont des outils puissants pour effectuer des opérations répétitives sur des structures à plusieurs dimensions, comme des tableaux, des grilles ou des formes géométriques. Elles permettent d’automatiser et de simplifier des tâches complexes qui seraient fastidieuses à coder ligne par ligne.
Il est important de bien comprendre la logique des boucles imbriquées pour les utiliser efficacement et éviter les erreurs. Plus vous pratiquez avec des exemples comme ceux-ci, plus vous serez à l'aise avec leur utilisation dans des contextes variés.
N’hésitez pas à essayer ces exemples, et à les modifier pour explorer davantage le potentiel des boucles imbriquées dans vos propres projets Python.