Exercices sur les algorithmes et problèmes #
La notion d’algorithme a été abordée implicitement dès les premiers cours de mathématiques, par exemple avec l’algorithme de la division longue. Ces exercices visent à vous faire décrire formellement un algorithme. La principale difficulté pour la plupart des étudiants réside dans la rigueur et la précision requises. Au-delà d’un certain point, il n’existe pas de lectures supplémentaires : la pratique est essentielle.
Il est permis d’utiliser le robot conversationnel du cours pour ces exercices (voir ici). Toutefois, entraînez-vous à produire vos propres réponses.
Comment procéder pour les exercices :
- Lisez attentivement la question.
- Cherchez une solution. Si vous ne trouvez pas immédiatement, consacrez 10 à 15 minutes à y réfléchir. Si le problème exact vous résiste, tentez une solution partielle. Pour cet exercice, vous devez produire du pseudo-code, et non du Java.
- Rédigez votre solution avec précision, comme une suite de consignes qu’un enfant pourrait suivre.
- Exécutez votre pseudo-code (voir l’exécution d’un pseudo-code à l’activité 1.2).
- Consultez ensuite la ou les solutions proposées.
- Assurez-vous de comprendre toutes les solutions. Posez des questions si nécessaire, en fournissant votre propre solution pour appuyer vos interrogations. Comprendre les solutions proposées est impératif.
Ces exercices sont obligatoires et ne doivent pas être survolés ou omis.
Pour lire les formules mathématiques sur le site du cours, utilisez un navigateur compatible avec MathML, comme Chrome, Edge, Firefox ou Safari.
Ces exercices sont conçus pour l’autoévaluation ; ils ne sont pas corrigés. Nous répondons cependant à vos questions sur la matière.
Les solutions à ces exercices ne sont pas uniques. Il existe plusieurs syntaxes possibles pour décrire un algorithme en pseudo-code. Cela ne signifie pas que toutes les solutions sont correctes. Un pseudo-code peut être erroné s’il ne décrit pas une solution logiquement correcte ou s’il manque de précision pour être considéré comme un algorithme. Un pseudo-code doit pouvoir être exécuté littéralement par un humain sans jugement, comme un automate.
Rappel : les mathématiques du collégial sont un préalable obligatoire à ce cours. Une aisance en algèbre, fonctions et arithmétique est nécessaire. Sans ces prérequis, réussir ce cours peut être difficile.
Réponses uniques ? #
Les exercices incluent une solution pour comparer votre approche à la nôtre. Il n’existe pas de solution unique ; votre solution peut être meilleure ou moins bonne que celle proposée.
Exercice 1 : La somme d’un tableau #
Dans la plupart des langages informatiques, un tableau correspond à un vecteur en algèbre linéaire, soit une série de nombres, comme \(\langle 1,6,4,10 \rangle\). Dans cet exercice, vous devez proposer un algorithme pour calculer la somme des nombres entiers d’un tableau à une dimension de longueur quelconque (de 0 à plus d’un million de nombres). Utilisez une structure d’itération (boucle) pour parcourir chaque nombre du tableau.
Pour manipuler le tableau, vous pouvez écrire « Récupérer le nombre à l’index i » (où i est une variable contenant l’index) ou utiliser une syntaxe proche des langages de programmation, par exemple : Entier e = monTableau[i]
. Pour obtenir la longueur du tableau, utilisez « la taille de monTableau ».
Testez votre pseudo-code en l’appliquant ligne par ligne à un exemple, comme si vous étiez un robot. Prenez votre temps.
Si vous introduisez d’autres conventions de notation, soyez précis. Spécifiez le type de toutes vos variables et donnez explicitement des valeurs initiales, sauf si elles sont reçues en paramètre.
Concevez cet algorithme en pseudo-code, en utilisant des termes concis, explicatifs et cohérents.
Réponse
Entrée :
Tableau d’entiers monTableau de taille N
Variables :
Entier somme = 0 // La somme du tableau
Entier index = 0 // Index de l’élément du tableau
Sortie :
Entier somme
Algorithme sommeTableau :
TANT QUE index < taille de monTableau FAIRE
somme = somme + monTableau[index] // Addition des nombres
index = index + 1 // Incrémentation de l’index
FIN TANT QUE
retourne somme
Exercice 2 : La recherche d’un entier #
La recherche d’information dans une structure de données (tableau, graphe, arbre, etc.) est un domaine clé en informatique. Bien que les bases de données comme MySQL simplifient la recherche, il est souvent nécessaire de concevoir ses propres solutions. À partir de l’exercice 1, proposez un algorithme en pseudo-code pour vérifier si un entier (par exemple, un numéro de téléphone) est présent dans un tableau et retourner son index, ou -1 s’il est absent. Utilisez une structure itérative et une structure de contrôle (SI _ ALORS _ FIN SI).
Réponse
Entrée :
Tableau d’entiers monTableau de taille N
Entier nombreATrouver
Variables :
Entier index = 0 // Index de l’élément du tableau
Sortie :
Index de l’entier ou -1 si non trouvé
Algorithme trouverEntier :
TANT QUE index < taille de monTableau FAIRE
SI nombreATrouver est égal à monTableau[index] ALORS
retourner index // Fin de l’algorithme
FIN SI
index = index + 1 // Incrémentation de l’index
FIN TANT QUE
retourner -1 // Nombre non trouvé
Exercice 3 : Somme des multiples de 3 ou 5 #
Additionnez tous les nombres naturels inférieurs à \(1000\) qui sont multiples de \(3\) ou de \(5\).
Réponse
Voici un algorithme inefficace. Vous pouvez faire mieux :
Variable entière i = 0
Variable entière somme = 0
TANT QUE i < 1000
SI le reste de la division par 3 de i est zéro OU le reste de la division par 5 de i est zéro ALORS
somme = somme + i
i = i + 1
FIN TANT QUE
Retourne somme
Exercice 4 : Plus grand diviseur premier #
Trouvez le plus grand nombre premier qui divise \(317584931803\).
Réponse
Voici un algorithme inefficace (effectuant plus d’opérations que nécessaire). Vous pouvez faire mieux :
Variable entière i = 1
Variable entière solution = 1
TANT QUE i < 317584931803
SI le reste de la division de 317584931803 par i est zéro ALORS
Variable entière j = 3
Variable booléenne premier = vrai
TANT QUE j < i
SI le reste de la division de i par j est zéro ALORS
premier = faux
j = j + 1
FIN TANT QUE
SI premier ALORS
solution = i
i = i + 1
FIN TANT QUE
Retourne solution
Pour les curieux, voici une solution exécutable en Python (voir ici) :
i = 1
solution = 1
while i < 317584931803:
if 317584931803 % i == 0:
j = 3
premier = True
while j < i:
if i % j == 0:
premier = False
j = j + 1
if premier:
print(i, " est premier")
solution = i
i = i + 1
print(solution)
Vous pouvez supprimer la ligne print(i, " est premier")
pour n’obtenir que la réponse finale. Notez que j commence à 3, car tout diviseur premier (sauf 2) est impair d’après le théorème fondamental de l’arithmétique.
Exercice 5 : Chiffre des dizaines #
Pour un entier positif \(x\), trouvez le chiffre occupant la position des dizaines.
Réponse
Variable entière x
Divise x par 10, stocke le quotient dans la variable y
Divise y par 10, retourne le reste de la division
Exemple : si x est 531, le quotient de 531 divisé par 10 est 53, reste 1. Le quotient de 53 divisé par 10 est 5, reste 3.
Exercice 6 : Erreur dans un pseudo-code #
Trouvez l’erreur dans le pseudo-code suivant :
Entrées :
Tableau R de longueur N
Valeur X
Sortie :
Est-ce que la valeur X se trouve dans le tableau R ?
Variables :
Itérateur i = 0
Tant que i <= N
Si R[i] = X Alors retourne Vrai
i = i + 1
retourne Faux
Réponse
L’itérateur i prend les valeurs de 0 à N, accédant ainsi à N+1 éléments du tableau R, ce qui provoque une erreur d’accès hors limites.
Exercice 7 : Racines d’un polynôme du second degré #
Soit \(P(x) = ax^2 + bx + c\) un polynôme du second degré à coefficients réels. Les racines se calculent via le discriminant \(A = b^2 - 4ac\).
- Si \(A < 0\), il n’y a pas de racine.
- Si \(A > 0\), il existe deux racines : \(X_1 = \frac{-b - \sqrt{A}}{2a}\) et \(X_2 = \frac{-b + \sqrt{A}}{2a}\).
- Si \(A = 0\), il existe une racine double : \(X_1 = X_2 = \frac{-b}{2a}\).
Écrivez un algorithme qui, pour un polynôme donné par ses coefficients, calcule le discriminant, affiche « ce polynôme n’a pas de racine dans R » si A < 0, et calcule les racines sinon.
Réponse
Algo pol
Entrée :
Nombres réels a, b, c // Coefficients du polynôme
Variables :
Nombres réels X1, X2, A // Racines et discriminant
Début
A = b² - 4ac
Si A < 0 Alors
Afficher « ce polynôme n’a pas de racine dans R »
Sinon
Si A égale 0
X1 = X2 = -b/(2a)
Sinon // A > 0
X1 = (-b - √A)/(2a)
X2 = (-b + √A)/(2a)
Fin Si
Fin Si
Fin
Exercice 8 : Exécution de l’algorithme des racines #
Exécutez l’algorithme de l’exercice 7 pour \(P(x) = x^2 - 5x + 6\), en présentant les résultats dans un tableau.
Réponse
Initialisation | Étape 1 | Étape 2 | Étape 3 | Fin | ||
---|---|---|---|---|---|---|
Entrée | a | 1 | 1 | 1 | 1 | 1 |
b | -5 | -5 | -5 | -5 | -5 | |
c | 6 | 6 | 6 | 6 | 6 | |
Variables | X1 | 2 | 2 | 2 | ||
X2 | 3 | 3 | ||||
A | 1 | 1 | 1 | 1 | ||
Sorties | écran |
Exercice 9 : Conversion de base #
Pour un entier \(B > 1\) et un nombre \(M\), la représentation en base \(B\) de \(M\) s’obtient par division successive : \(M = B \times Q_1 + R_1\), puis \(Q_1 = B \times Q_2 + R_2\), jusqu’à un quotient inférieur à \(B\). La représentation est \(Q_{n-1}R_n\ldots R_1\). Si \(B > 10\), les chiffres de \(10\) à \(B-1\) sont notés \(A, B, C, \ldots\) (par exemple, pour \(B = 16\), \(10 = A\), \(11 = B\), etc.).
Écrivez un algorithme pour convertir un nombre \(M\) dans une base \(B ≥ 2\) (\(B < 17\)). Affichez un message d’erreur si \(B < 2\).
Réponse
Algo base
Entrée :
Nombre entier positif B // Base
Nombre entier positif M // Nombre à convertir
Variables :
Nombre entier q, r
Suite de caractères alphanumériques S
Début
S = chaîne vide
Si B < 2 Alors
Afficher « entrez un entier supérieur ou égal à 2 »
Sinon
q = M
Tant que q > 0
r = q - (q ÷ B) × B
au cas où r
égal à 10 : ajouter A au début de S
égal à 11 : ajouter B au début de S
égal à 12 : ajouter C au début de S
égal à 13 : ajouter D au début de S
égal à 14 : ajouter E au début de S
égal à 15 : ajouter F au début de S
dans tous les autres cas : ajouter r au début de S
q = q ÷ B
Fin Tant que
Fin Si
Fin
Voici l’équivalent en Python (voir ici) :
def f(M, B):
s = ""
if B < 2:
print("entrez un entier supérieur ou égal à 2")
return
q = M
while q > 0:
r = q - (q // B) * B
if r == 10: s = "A" + s
elif r == 11: s = "B" + s
elif r == 12: s = "C" + s
elif r == 13: s = "D" + s
elif r == 14: s = "E" + s
elif r == 15: s = "F" + s
else: s = str(r) + s
q = q // B
return s
Exercice 10 : Tester la parité en base 2 #
En utilisant l’algorithme Algo_base, qui retourne la représentation en base \(B\) d’un nombre \(M\) (\(S = \text{Algo\_base}(B, M)\)), écrivez un algorithme qui teste la parité d’un nombre \(M\) et affiche « pair » ou « impair ».
Réponse
Algo parité
Entrée :
Nombre entier positif M // Nombre à tester
Variables :
Suite de caractères alphanumériques S
Début
S = Algo_base(2, M)
Si dernier caractère de S est égal au chiffre 0 Alors
Afficher « pair »
Sinon
Afficher « impair »
Fin Si
Fin
Exercice 11 : Calcul de la factorielle #
Écrivez un algorithme qui calcule la factorielle d’un entier positif \(n\) (\(n!\)).
Solution
Entrée :
Entier positif n
Variable :
Entier fact = 1
Entier i = 1
TANT QUE i <= n FAIRE
fact = fact × i
i = i + 1
FIN TANT QUE
Retourner fact
Exercice 12 : Inverser un tableau #
Proposez un algorithme pour inverser un tableau d’entiers de taille quelconque.
Solution
Entrée :
Tableau d’entiers T de taille N
Variables :
Entier i = 0
Entier j = N - 1
TANT QUE i < j FAIRE
échanger T[i] et T[j]
i = i + 1
j = j - 1
FIN TANT QUE
Retourner T
Exercice 13 : Compter les voyelles #
Écrivez un algorithme qui compte le nombre de voyelles dans une chaîne de caractères donnée.
Solution
Entrée :
Chaîne de caractères S
Variable :
Entier compteur = 0
Entier i = 0
TANT QUE i < longueur de S FAIRE
SI S[i] est une voyelle ALORS
compteur = compteur + 1
FIN SI
i = i + 1
FIN TANT QUE
Retourner compteur
Exercice 14 : Tester si un entier est un palindrome #
Donnez un algorithme pour déterminer si un nombre entier est un palindrome (se lit de la même façon de gauche à droite et de droite à gauche).
Solution
Entrée :
Entier positif n
Variables :
Entier original = n
Entier renverse = 0
TANT QUE n > 0 FAIRE
renverse = renverse × 10 + (reste de la division de n avec 10)
n = n ÷ 10
FIN TANT QUE
SI original = renverse ALORS
Retourner Vrai
SINON
Retourner Faux
FIN SI
Exercice 15 : Minimum et maximum d’un tableau #
Écrivez un algorithme qui trouve le minimum et le maximum dans un tableau d’entiers.
Solution
Entrée :
Tableau d’entiers T de taille N
Variables :
Entier min = T[0]
Entier max = T[0]
Entier i = 1
TANT QUE i < N FAIRE
SI T[i] < min ALORS
min = T[i]
FIN SI
SI T[i] > max ALORS
max = T[i]
FIN SI
i = i + 1
FIN TANT QUE
Retourner min, max
Exercice 16 #
Expliquez la différence entre la complexité en temps et la complexité en espace d’un algorithme.
Solution
La complexité en temps mesure la quantité d’opérations ou le temps d’exécution d’un algorithme en fonction de la taille des données d’entrée. La complexité en espace mesure la quantité de mémoire supplémentaire nécessaire à l’algorithme pour fonctionner. Un algorithme peut être rapide (faible complexité en temps) mais utiliser beaucoup de mémoire (complexité en espace élevée), ou l’inverse.
Exercice 17 #
Quel est le nombre maximal de comparaisons nécessaires pour rechercher un élément dans un tableau non trié de taille \( n \) ? Justifiez votre réponse.
Solution
Dans un tableau non trié de taille \( n \), il faut au pire comparer l’élément recherché à chaque élément du tableau, soit \( n \) comparaisons. Cela correspond à une recherche linéaire, de complexité \( O(n) \).
Exercice 18 #
Pourquoi la recherche binaire n’est-elle applicable qu’aux tableaux triés ? Quelle est sa complexité en temps ?
Solution
La recherche binaire n’est applicable qu’aux tableaux triés, car elle repose sur le fait que l’on peut éliminer la moitié des éléments à chaque étape en comparant la valeur recherchée à l’élément du milieu. Si le tableau n’est pas trié, on ne peut pas savoir dans quelle moitié chercher. Sa complexité en temps est \( O(\log n) \).
Exercice 19 #
Donnez un exemple d’algorithme ayant une complexité en \( O(n^2) \) et expliquez pourquoi.
Solution
Un exemple classique est le tri à bulles (bubble sort). Pour chaque élément, on compare avec tous les autres, ce qui fait environ \( n^2 \) comparaisons pour un tableau de taille \( n \). C’est pourquoi sa complexité est \( O(n^2) \).
Exercice 20 #
Un algorithme de tri efficace comme le tri fusion (merge sort) a une complexité en \( O(n \log n) \). Expliquez ce que cela signifie et pourquoi c’est plus rapide qu’un tri naïf pour de grands tableaux.
Solution
Une complexité en \( O(n \log n) \) signifie que le nombre d’opérations croît plus vite que linéairement, mais beaucoup moins vite que quadratiquement. Par exemple, le tri fusion (merge sort) divise le tableau en deux à chaque étape (logarithmique) et traite chaque élément à chaque niveau de division (linéaire), d’où le \( n \log n \). Pour de grands tableaux, c’est beaucoup plus rapide qu’un tri naïf en \( O(n^2) \).
Exercice 21 #
Alan Kay est considéré comme l’un des pères de la programmation orientée objet. Quelles étaient ses motivations principales lorsqu’il a conçu ce paradigme ? En quoi sa vision différait-elle de l’utilisation courante de la programmation orientée objet aujourd’hui ? Résumez brièvement ses objectifs et l’esprit original de la programmation orientée objet selon Kay.
Réponse
Alan Kay a conçu la programmation orientée objet pour faciliter la création de systèmes logiciels modulaires, flexibles et évolutifs, inspirés par la biologie et la communication entre objets autonomes. Son objectif principal était de permettre à chaque « objet » d’être responsable de son propre état et de communiquer avec d’autres objets uniquement via des messages, favorisant ainsi l’encapsulation et l’indépendance des composants.
Pour Kay, la programmation orientée objet devait avant tout encourager l’émergence de systèmes dynamiques, adaptatifs et faciles à modifier, plutôt que de se limiter à la simple hiérarchie de classes et à l’héritage. Il insistait sur l’importance de la communication par messages et sur la capacité à faire évoluer les programmes sans tout réécrire.
De nos jours, la programmation orientée objet est souvent réduite à l’organisation du code et des données, alors que la vision originale de Kay mettait l’accent sur la modularité, la flexibilité et l’autonomie des objets. Sa conception visait à rendre la programmation plus naturelle, intuitive et proche du fonctionnement des systèmes vivants.
Exercice 22 #
Ole-Johan Dahl et Kristen Nygaard sont les créateurs du premier langage orienté objet, Simula. Quelles étaient leurs motivations principales lors de la création de ce langage ? Expliquez en quoi leur approche a influencé la programmation moderne.
Réponse
Dahl et Nygaard ont conçu Simula pour faciliter la modélisation et la simulation de systèmes complexes, comme des réseaux, des usines ou des processus sociaux. Leur motivation était de représenter chaque entité du système par un « objet » autonome, regroupant données et comportements, afin de refléter la réalité de manière plus naturelle et modulaire.
Ils voulaient permettre l’encapsulation, la réutilisation du code grâce à l’héritage, et la création de structures hiérarchiques. Cette approche a posé les bases de la programmation orientée objet moderne, en rendant la conception de logiciels plus flexible, évolutive et adaptée à la complexité des systèmes réels.
Exercice 23 #
Qui est James Gosling et quel a été son rôle dans la création du langage Java ? Quelles étaient les motivations principales derrière la conception de Java ?
Réponse
James Gosling est un informaticien canadien considéré comme le principal créateur du langage Java, développé chez Sun Microsystems dans les années 1990. Son objectif était de concevoir un langage portable, sécurisé, simple et adapté aux systèmes embarqués et aux réseaux. Java devait permettre d’écrire un programme une seule fois et de l’exécuter partout (« Write Once, Run Anywhere »), grâce à la machine virtuelle Java (JVM).
Exercice 24 #
Citez trois domaines ou secteurs industriels où Java est largement utilisé aujourd’hui. Expliquez brièvement pourquoi Java est apprécié dans ces contextes.
Réponse
Java est largement utilisé dans :
- Le développement d’applications d’entreprise (banques, assurances, télécommunications), grâce à sa robustesse, sa sécurité et la richesse de ses bibliothèques.
- Le développement d’applications mobiles (notamment Android), car Java est le langage principal pour Android et bénéficie d’un vaste écosystème.
- Les systèmes embarqués et l’Internet des objets (IoT), où la portabilité et la fiabilité de Java sont des atouts majeurs.
Exercice 25 #
Qu’est-ce que la notation de Backus-Naur (BNF) ? À quoi sert-elle en informatique ? Donnez un exemple simple de BNF décrivant la syntaxe d’une expression arithmétique composée de chiffres et de l’opérateur +.
Réponse
La notation de Backus-Naur (BNF) est une méthode formelle pour décrire la syntaxe des langages de programmation et des langages formels. Elle permet de spécifier les règles de formation des expressions valides dans un langage, en utilisant des symboles non terminaux, des symboles terminaux et des règles de production.
La BNF est largement utilisée pour définir la grammaire des langages de programmation, des protocoles ou des formats de données.
Exemple de BNF pour une expression arithmétique simple :
<expression> ::= <chiffre> | <expression> "+" <chiffre> <chiffre> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
Cela décrit une expression composée d’un ou plusieurs chiffres séparés par des +.
Exercice 26 #
Mon ordinateur roule à une fréquence de 3 GHz. À tous les cycles, il exécute ses opérations. Quelle distance est-ce que la vitesse de la lumière traverse pendant un cycle ?
Réponse
À 3 GHz, un cycle d’horloge dure :
1 / 3 000 000 000 = 0,333... nanoseconde (ns) par cycle.
La lumière parcourt environ 30 cm (0,3 mètre) en 1 ns. Donc, en 0,333 ns, elle parcourt :
30 cm × 0,333... ≈ 10 cm.
Réponse : Pendant un cycle d’horloge à 3 GHz, la lumière parcourt environ 10 centimètres dans le vide.
Exercice 27 #
Quelle est la différence entre un kibioctet et un kilo-octet ?
Réponse
Un kilo-octet (ko) correspond à 1 000 octets (selon le système décimal, préfixe SI), tandis qu’un kibioctet (Kio) correspond à 1 024 octets (selon le système binaire, préfixe IEC). Le préfixe « kilo » (k) est utilisé pour les puissances de 10, alors que « kibi » (Ki) est utilisé pour les puissances de 2. Ainsi, 1 Kio = 1 024 octets, 1 ko = 1 000 octets.
Exercice 28 #
Écrivez un algorithme qui supprime les doublons d’un tableau d’entiers trié en ordre croissant, en renvoyant la nouvelle taille du tableau.
Réponse
Entrée :
Tableau d’entiers trié T de taille N
Variables :
Entier nouvelle_taille = 1
Entier i = 1
Tant que i < N faire
Si T[i] ≠ T[nouvelle_taille - 1] alors
T[nouvelle_taille] = T[i]
nouvelle_taille = nouvelle_taille + 1
Fin si
i = i + 1
Fin tant que
Retourner nouvelle_taille
Exercice 29 #
Écrivez un algorithme qui calcule \( a^n \), où \( a \) est un entier et \( n \) un entier positif.
Réponse
Entrée :
Entier a
Entier positif ou nul n
Variable :
Entier résultat = 1
Entier i = 0
Tant que i < n faire
résultat = résultat × a
i = i + 1
Fin tant que
Retourner résultat
Exercice 30 #
Écrivez un algorithme qui compte le nombre d’occurrences d’un caractère spécifique dans une chaîne de caractères.
Réponse
Entrée :
Chaîne de caractères S
Caractère c
Variable :
Entier compteur = 0
Entier i = 0
Tant que i < longueur de S faire
Si S[i] = c alors
compteur = compteur + 1
Fin si
i = i + 1
Fin tant que
Retourner compteur
Vidéos #
Des vidéos sur l’algorithmique et le pseudo-code sont disponibles, comme celles de Loïc & Julien.
Logiciels #
Certains étudiants utilisent des logiciels comme AlgoBox. Cela n’est pas nécessaire, car le pseudo-code doit être écrit dans vos propres mots. Si un logiciel vous aide, utilisez-le, mais vous devriez pouvoir écrire du pseudo-code manuellement, sans outils. C’est l’essence du pseudo-code : il est indépendant des syntaxes et des outils.