Algorithmique Et Programmation

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 15

UNIVERSITE DE KINSHASA

FACULTE POLYTECHNIQUE

TRAVAIL PRATIQUE D’ALGORITHMIQUE ET


PROGRAMMATION

FAIT PAR : KOBANGE WANGBAO Obed L2/ G.C

MUNZUMBU PADE André L2/ G.C

2023-2024
Les fondements mathématiques de l’algorithmique et l’évaluation des algorithmes

Répondre aux questions suivantes (révision de la matière):

1. Qu'est-ce qu'un algorithme ?


Algorithme se définit comme étant une procédure de calcul qui prend en entre une valeur, ou un
ensemble de valeurs, et qui donne en sortie une valeur ou un ensemble de valeurs. Autrement dit Un
algorithme est une séquence d'instructions précises et logiques qui permet de résoudre un problème ou
d'effectuer une tâche spécifique.

2. Qu'est-ce qu'un algorithme efficace ?


Un algorithme efficace est un algorithme qui parvient à résoudre un problème de manière rapide et
économique en termes de ressources telles que le temps et l'espace

3. Que pouvez-vous dire à propos de l'efficacité d'un algorithme ?

L'efficacité d'un algorithme se réfère à sa capacité à résoudre un problème de manière rapide et


économique. Cela signifie qu'un algorithme efficace parvient à obtenir le résultat souhaité en utilisant le
moins de temps et de ressources possibles, comme la mémoire ou l'espace de stockage

4. Citer quelques-unes des techniques de conception d'un algorithme ?


Les techniques de conception d’un algorithme sont : la méthode de la force brute, la méthode
gloutonne, la méthode du diviser pour régner, la méthode probabiliste, et la méthode de la
programmation dynamique

5. Commentez en quelques phrases les techniques de conception des algorithmes


suivantes : la méthode de la force brute, la méthode gloutonne, la méthode du diviser
pour régner, la méthode probabiliste, la méthode de la programmation dynamique.
La méthode de la force brute consiste à examiner toutes les possibilités pour résoudre un problème, ce
qui peut être coûteux en termes de temps et de ressources. La méthode gloutonne fait des choix
localement optimaux sans garantir la meilleure solution globale. La méthode du diviser pour régner
divise un problème en sous-problèmes plus simples, puis combine les résultats. La méthode probabiliste
utilise des techniques basées sur le hasard pour obtenir des résultats approximatifs. La méthode de la
programmation dynamique décompose un problème en sous-problèmes, calcule et stocke les résultats
intermédiaires pour économiser du temps de calcul.

6. Qu'est-ce qu'un pseudo-code ? Qu'est-ce qu'un ordinogramme ? De quelle autre façon


peut-on présenter un algorithme ?
Un pseudo-code est une représentation textuelle d'un algorithme qui décrit les étapes nécessaires pour
résoudre un problème de manière abstraite. Il utilise un langage informel proche du langage courant et
n'est pas lié à un langage de programmation spécifique.

Un ordinogramme, également appelé diagramme de flux, est une représentation graphique d'un
algorithme. Il utilise des symboles graphiques standardisés pour représenter les étapes et les décisions
de l'algorithme, facilitant ainsi la visualisation de l'enchaînement des actions.

En plus du pseudo-code et des ordinogrammes, d'autres façons courantes de présenter un algorithme


incluent :

Écriture dans un langage de programmation spécifique pour une implémentation directe.

Diagrammes de séquence pour représenter l'interaction entre les composants logiciels.

Diagrammes d'activité pour des processus complexes et des interactions entre entités.

Mise en évidence des structures de données utilisées et modifiées avec des diagrammes de structure de
données.

Utilisation de diagrammes de classes pour représenter les classes, attributs et relations des objets dans
une approche orientée objet.

7. Pour quelles raisons une équipe de développeurs de logiciels choisit-elle de


représenter les algorithmes par du pseudo-code, des organigrammes ou des bouts de
code.
Une équipe de développeurs utilise le pseudo-code, les organigrammes et les bouts de code pour les
raisons suivantes :

Compréhension générale : Le pseudo-code et les organigrammes offrent une représentation claire et


abstraite des étapes et des décisions de l'algorithme, permettant une compréhension rapide de l'idée
générale sans se préoccuper des détails du langage de programmation.

Communication et collaboration : Le pseudo-code et les organigrammes sont des langages universels qui
transcendent les barrières linguistiques et les préférences de langage de programmation. Ils facilitent la
communication entre les membres de l'équipe en permettant l'expression d'idées et de solutions
compréhensibles pour tous.

Flexibilité : Le pseudo-code et les organigrammes ne sont pas liés à un langage de programmation


spécifique, ce qui permet à l'équipe de se concentrer sur la logique et la conception de l'algorithme
plutôt que sur les détails d'implémentation propres à un langage donné. Cela offre une plus grande
flexibilité lors du choix du langage de programmation pour l'implémentation réelle.
Réutilisation : Les extraits de code réels peuvent être utilisés pour représenter des parties spécifiques de
l'algorithme nécessitant une attention particulière ou une compréhension détaillée. Cela permet
d'illustrer les points clés de l'algorithme et de montrer comment ils peuvent être mis en œuvre dans un
langage de programmation donné.

8. En général pour un problème donné, on peut développer plusieurs algorithmes.


Comment identifier le meilleur algorithme de cet ensemble?
Pour identifier le meilleur algorithme parmi plusieurs options pour un problème donné, voici quelques
approches courantes :

Analyse théorique : Évaluez les algorithmes en termes de complexité temporelle et spatiale pour
déterminer leur efficacité en termes de ressources.

Étude des cas : Testez les algorithmes avec des données représentatives et mesurez leur temps
d'exécution pour évaluer leurs performances pratiques.

Expérimentation comparative : Comparez les résultats des algorithmes en termes de temps d'exécution,
d'utilisation de la mémoire et de précision des résultats en les exécutant sur des ensembles de données
de différentes tailles.

Considérations du contexte : Tenez compte du contexte spécifique du problème et des contraintes. Par
exemple, privilégiez la simplicité et la facilité de maintenance si elles sont prioritaires, même si cela
implique une légère baisse de performance.

Consultation d'experts : Demandez l'avis d'experts tels que des chercheurs ou des développeurs
expérimentés pour bénéficier de leur expertise dans l'évaluation des algorithmes pour le problème
spécifique.

9. En quoi consiste l'analyse d'un algorithme?


L'analyse d'un algorithme évalue ses performances en fonction du temps d'exécution, de l'utilisation de
la mémoire et de la précision des résultats. Elle vise à comprendre son comportement dans différents
scénarios et à déterminer son efficacité et son efficience.

Les aspects clés de l'analyse d'un algorithme incluent :


La complexité temporelle, qui estime le temps d'exécution en fonction de la taille de l'entrée. Une
complexité temporelle plus faible est préférable, indiquant une exécution plus rapide de l'algorithme.

La complexité spatiale, qui évalue la quantité de mémoire utilisée en fonction de la taille de l'entrée.
Une complexité spatiale plus faible est souhaitable, surtout pour les problèmes avec des contraintes de
mémoire.

L'analyse des cas d'entrée typiques et extrêmes, qui permet de comprendre le comportement de
l'algorithme dans différentes situations. Certains algorithmes peuvent être performants avec de petites
entrées mais ralentir considérablement avec des entrées plus grandes.

L'analyse expérimentale, qui consiste à implémenter l'algorithme et à le tester sur des ensembles de
données réels. Cette approche mesure les performances réelles de l'algorithme et permet de le
comparer avec d'autres solutions. Les tests expérimentaux fournissent des informations précieuses sur
le comportement de l'algorithme dans des conditions réelles.

10.Quelles sont les deux méthodes d'analyse d'un algorithme?


Les deux principales méthodes d'analyse d'un algorithme sont l'analyse théorique et l'analyse
expérimentale.

11.Quels sont les inconvénients de la méthode expérimentale?


Les inconvénients de la méthode expérimentale pour l'analyse d'un algorithme sont les suivants :

Coût en temps et en ressources

Limitations de l'échantillonnage

Variabilité des conditions expérimentales

Difficulté à reproduire les résultats

12. En quoi consiste la méthode des opérations primitives?


La méthode des opérations primitives consiste à décomposer un algorithme en opérations de base et à
leur attribuer un coût constant. En comptant le nombre total d'opérations primitives, on peut estimer le
temps d'exécution de l'algorithme. Cela permet d'estimer la complexité temporelle de manière
simplifiée et de comparer différentes approches en fonction du nombre d'opérations primitives.

13. Qu'est-ce que la complexité d'un algorithme?


La complexité d'un algorithme mesure les ressources nécessaires à son exécution, comme le temps, la
mémoire et les opérations. On distingue la complexité temporelle, qui mesure le temps d'exécution en
fonction de la taille de l'entrée, et la complexité spatiale, qui mesure l'utilisation de la mémoire en
fonction de la taille de l'entrée. Ces mesures permettent d'évaluer les performances et l'efficacité d'un
algorithme.

14. En quoi consiste la notation asymptotique?


La notation asymptotique est utilisée en analyse des algorithmes pour décrire la complexité temporelle
ou spatiale d'un algorithme lorsque la taille de l'entrée devient grande. Elle utilise les symboles O (grand
O), Ω (Oméga) et Θ (thêta) pour représenter respectivement la borne supérieure, la borne inférieure et
la borne précise de la complexité. Par exemple, O(n) indique une croissance linéaire, Ω(n²) indique une
croissance quadratique, et Θ(n) indique une croissance linéaire précise. Ces notations permettent de
comparer et d'analyser les performances des algorithmes de manière concise.

15. Quelles sont les fonctions qui apparaissent le plus lors de l'analyse théorique des
algorithmes?
Les fonctions les plus courantes lors de l'analyse théorique des algorithmes sont :

Constante (1)

Logarithme (log n)

Linéaire (n)

Quadratique (n²)

Cubique (n³)

Exponentielle (2^n)

16. Quel est l'algorithme le plus efficace parmi un ensemble d'algorithmes permettant de
résoudre un problème
Pour déterminer l'algorithme le plus efficace parmi un ensemble donné, vous pouvez prendre en
compte plusieurs facteurs clés :

Complexité temporelle : Comparez les asymptotes de complexité des algorithmes pour évaluer leur
efficacité en termes de temps d'exécution. Recherchez les algorithmes avec une complexité inférieure,
telle que O(log n) ou O(n), plutôt que des complexités plus élevées comme O(n²) ou O(2^n).

Complexité spatiale : Évaluez l'utilisation de la mémoire par les algorithmes. Choisissez ceux qui
nécessitent moins de mémoire et qui peuvent gérer des ensembles de données plus importants de
manière efficace.
Performances expérimentales : Examinez les résultats des tests et des expériences réelles pour les
algorithmes. Considérez les performances observées dans des scénarios réels ou des ensembles de
données représentatifs du problème. Vérifiez si les algorithmes se comportent de manière optimale
dans ces situations.

Caractéristiques du problème : Tenez compte des spécificités du problème que vous souhaitez résoudre.
Certains algorithmes peuvent mieux s'adapter aux particularités du problème, exploitant ses propriétés
et sa structure spécifiques.

17. Pour évaluer expérimentalement un algorithme on doit l'implémenter et lui fournir


des entrées différentes question de mesurer le temps d'exécution correspondant à
chaque entrée. C'est en dessinant la courbe du temps d'exécution en fonction de la
taille de l'entrée que l'on peut identifier la fonction correspondant à l'évolution du
temps d'exécution en fonction de la taille d'entrée. La notion de la taille d'une entrée
est très importante. Pourriez-vous la définir en quelques mots et donner quelques
exemples de taille d'entrée pour des problèmes simples.

18. Dans l'analyse d'un algorithme on distingue généralement le cas le plus


défavorable, le cas le plus favorable et le cas moyen (probabiliste). Expliquez en quoi consiste
chaque cas. Pourquoi le cas le plus défavorable a une importance particulière?
Le cas le plus défavorable représente la situation où l'algorithme nécessite le plus de temps ou de
ressources pour résoudre un problème. Il fournit une limite supérieure sur la performance de
l'algorithme dans tous les cas possibles. Comprendre ce cas garantit que l'algorithme fonctionne de
manière acceptable même dans les situations les plus difficiles.

Le cas le plus favorable représente la situation idéale où l'algorithme nécessite le moins de temps ou de
ressources. Cependant, il est rarement représentatif de la réalité, car il suppose des conditions
optimales qui sont souvent peu probables dans la pratique.

Le cas moyen (probabiliste) tient compte de la distribution probabiliste des entrées et représente la
performance moyenne de l'algorithme dans des scénarios typiques. Il est plus représentatif de la réalité
que le cas le plus favorable, car il prend en compte la variabilité des entrées.

Le cas le plus défavorable est important car il garantit les performances de l'algorithme,
indépendamment de la taille ou de la nature des entrées. En se concentrant sur le pire des scénarios, on
peut s'assurer que l'algorithme fonctionnera de manière prévisible et acceptable, même dans des
conditions difficiles. Cela permet de prendre des décisions éclairées sur l'utilisation de l'algorithme dans
des situations réelles et assure une certaine robustesse.

19.Définir en quelques mots le concept de récursivité.

La récursivité est un concept en informatique où une fonction ou un algorithme s'appelle lui-même de


manière répétée jusqu'à atteindre une condition de base. Cela permet de résoudre des problèmes en les
décomposant en sous-problèmes plus simples et en utilisant le même processus de résolution pour
chaque sous-problème

20. En quoi consistent la récursivité linéaire, la récursivité binaire et la récursivité


multiple.
La récursivité linéaire est un type de récursivité où une fonction s'appelle elle-même une seule
fois à chaque étape. Cela crée une séquence linéaire d'appels récursifs, où chaque appel résout un sous-
problème plus petit jusqu'à atteindre une condition de base.

La récursivité binaire est un type de récursivité où une fonction s'appelle elle-même deux fois à
chaque étape. Cela divise le problème en deux sous-problèmes, généralement de taille égale, et
combine ensuite les résultats pour obtenir la solution finale. La récursivité binaire est souvent utilisée
dans des algorithmes tels que la recherche binaire ou le tri fusion.

La récursivité multiple, également connue sous le nom de récursivité à plusieurs voies, est un
type de récursivité où une fonction s'appelle elle-même plusieurs fois à chaque étape. Cela permet de
diviser le problème initial en plusieurs sous-problèmes indépendants, qui sont ensuite résolus
séparément. La récursivité multiple est couramment utilisée dans des structures de données telles que
les arbres et les graphes, où chaque nœud peut avoir plusieurs enfants ou voisins.

21. De quelle façon un problème récursif doit-il pouvoir se définir? Donnez un exemple.

Un problème récursif doit pouvoir se définir en termes de sous-problèmes plus simples et de cas de
base. Chaque sous-problème doit être une version réduite ou similaire du problème initial, et la
résolution du problème global doit reposer sur la résolution de ces sous-problèmes.

Prenons l’exemple de la fonction factorielle :

La définition récursive de la factorielle est la suivante :

Cas de base : Si n est égal à 0 ou 1, alors n! est égal à 1.

Cas récursif : Si n est supérieur à 1, alors n! est égal à n multiplié par (n-1) !.

En utilisant cette définition récursive, nous pouvons calculer la factorielle d'un nombre en appelant
récursivement la fonction factorielle sur n-1 jusqu'à atteindre le cas de base.

Par exemple, pour calculer 5!, nous aurions :


factorielle(5) = 5 * factorielle(4)

= 5 * (4 * factorielle(3))

= 5 * (4 * (3 * factorielle(2)))

= 5 * (4 * (3 * (2 * factorielle(1))))

= 5 * (4 * (3 * (2 * 1)))

= 5 * (4 * (3 * 2))

= 5 * (4 * 6)

= 5 * 24

= 120

Ainsi, la factorielle de 5 est égale à 120 en utilisant une approche récursive

Quelques exercices

1. Le nombre d'opérations primitives exécutées par les algorithmes A et B est (50 n log n)
et (45 n2), respectivement. Déterminez n0 tel que A soit meilleur que B, pour tout n ≥
n0.
Soit l’algorithme A donné par (50 nlog (n)) et l’algorithme B donné par (45n²), pour que A soit meilleur
que B, il faut que son temps d’exécution soit inférieur à celui de B

ceci veut dire :

𝟓𝟎𝐧𝐥𝐨𝐠(𝐧) < 𝟒𝟓𝐧² càd 𝟓𝟎/𝟒𝟓 𝐥𝐨𝐠(𝒏) < 𝒏 ssi 𝟏𝟎/𝟗 𝐥𝐨𝐠(𝒏) < 𝒏

On constate que pour des valeurs siffusament grandes de n l’algorithme A sera meilleur que B.

2. Le nombre d'opérations primitives exécutées par les algorithmes A et B est (140 n2) et

(29 n3), respectivement. Déterminez n0 tel que A soit meilleur que B pour tout n ≥ n0. Utiliser
Matlab ou Excel pour montrer les évolutions des temps d'exécution des algorithmes A et B
dans un graphique.

Sachant que A est meilleur que B ssi son nombre d’opérations primitives exécutées est inferieur à celui
de B, C’est a dire 140n²< 29n en divisant les deux membres par n² D’où 𝑛 > 140/29
3. Montrer que les deux énoncés suivants sont équivalents :

(a) Le temps d'exécution de l'algorithme A est toujours O(f(n)).


(b) Dans le pire des cas, le temps d'exécution de l'algorithme A est O(f(n)).

On sait que si le temps d’exécution du cas favorable ou défavorable est O(f(n)), alors il devra exister une
constante k telle que k*f(n) est supérieur au cas pire pour n >no, car théoriquement et pratiquement
dans le pire de cas, on observe un temps d’exécution supérieur. On peut trivialement déduire que le pire
de cas de l’algorithme A est aussi O(f(n)) , ceci car la forme asymptotique ne sera juste qu’un facteur
multiplicatif de k du cas le plus favorable. Comme a >=b et b >=k , alors a >=k , ce qui signifie que k est
O(f(n))

4. Montrer que si d(n) vaut O(f(n)), alors (a x d(n)) vaut O(f(n)), pour toute constante

a > 0.
Si d(n) a comme expression asymptotique O(f(n)), alors il existe une constante k tel que d(n) <= k*f(n)
pour tout n>n0. En multipliant d(n) par a, on obtient (a*d(n)) <= a*k*O(f(n)) = k‘ *O(f(n)) On déduira
aussi trivialement, qu’on a obtenue une nouvelle constante, ce qui sera essentiellement k * a qui
maintiendra la condition originale de notation O qui sera vraie quand n>a*n0

5. Montrer que si d(n) vaut O(f(n)) et e(n) vaut O(g(n)), alors le produit d(n)e(n)
est O(f(n)g(n)).

En haut on a démontré que si d(n) a comme expression de temps d’exécution O(f(n)) et e(n) O(g(n)),
alors d(n)<= k*f(n) pour n_f>n_f0 et e(n)<= l*g(n) pour n_e>n_e0 Ceci veut dire que , d(n)*e(n) <=
(c*f(n))*(d*g(n)) et n_f*n_e > n_f0*n_e0 ce qui signifie en d’autres termes qu'il existe une nouvelle
constante n' = n_f*n_e et et n0' = n_f0*n_e0, et un k' = k*l tel que d(n)*e(n) <= k'(f(n)*g(n)) pour n'>n0',
On conclut aussi que d(n)*e(n) est O(f(n)*g(n))

6. Montrer que si d(n) vaut O(f(n)) et e(n) vaut O(g(n)), alors d(n)+e(n) vaut
O(f(n)+g(n)).

On y vas par raisonnement mathématique, si d(n) a comme expression asymptotique O(f(n)) et e(n)
O(g(n)), alors d(n)<= k*f(n) pour n_f>n_f0 et e(n)<= h*g(n) pour n_e>n_e0 Cela signifie que d(n) + e(n)
<= (k*f(n)) + (h*g(n)) et n > n_f0+n_e0 En se référant du même raisonnement que précédemment, on
peut dire qu’il existe un nouveau n' = n_f+n_e et et n0' = n_f0+n_e0, tel que d(n) + e(n) <= k*f(n) +
h*g(n) pour n>n0' ; En effet, on peut continuer en disant que d(n) + e(n) <= f(k*n)+ g(h*n), ce qui va
nous conduire à d(n) + e(n) <= O(f(n)+ g(n))

7. Montrer que si d(n) est O(f(n)) et e(n) est O(g(n)), alors d(n)−e(n) n'est pas
nécessairement O(f(n)−g(n)).
On se contente à dire que si d(n) a pour notation O(f(n)) , et e(n) notation O(g(n)), Avec comme exemple
d(n) = n et e(n) = n avec f(n) = n et g(n) = n, Alors on peut encore dire d(n) <= k*(f(n)) pour n>=0, et e(n)
<= 2*k*(g(n)) pour n>=0 f(n)- g(n) = 0 et d(n) - e(n) = n-n , pas de valeur pour n>0 telle que 0>=n, ce qui
signifie que d(n) -e(n) n'est pas O(f(n)-g(n))

8. Montrer que si d(n) est O(f(n)) et f (n) est O(g(n)), alors d(n) est O(g(n)).
Si d(n) vaut O(f(n)) et f(n) vaut O(g(n)), alors d(n)<= k*f(n) pour n_f>n_f0 et f(n)<= h*g(n) pour n_g>n_g0
Si c'est vrai, alors d(n)<=k*f(n)<=k*(d(g(n))) = k*d*g(n) = k'*g(n) par substitution, ce qui est vrai pour
n_f*n_g>n_f0*n_g0, ou n>n0

9. Étant donné une séquence de n éléments S, l'algorithme D appelle l'algorithme E sur


chaque
élément S[i]. L'algorithme E s'exécute en un temps O(i) lorsqu'il est appelé sur l'élément
S[i]. Quel est le pire temps d'exécution de l'algorithme D?

L'algorithme E s'exécute en un temps O(i) lorsqu'il est appelé sur l'élément S[i]. Quel est le pire temps
d'exécution de l'algorithme D? Il s’agit ici d’un algorithme constitué de deux blocs , D et E parcourant
une séquence S de n éléments. Le temps d’exécution de E est O(i), il correspondra au temps d’exécution
O(n) de l’algorithme E, car n est la taille de notre séquence, 6 Nous pouvons donc conclure trivialement,
que le pire temps d’exécution de D est O(n²) , car la séquence a n éléments sera appelé par l’algorithme
D qui appellera à son tour l’algorithme E n fois de suite.

10. Alphonse et Bob se disputent à propos de leurs algorithmes. Alphonse revendique le fait
que son algorithme de temps d'exécution O(n log n) est toujours plus rapide que
l'algorithme de temps d'exécution O(n2) de Bob. Pour régler la question, ils effectuent une
série d'expériences. À la consternation d'Alphonse, ils découvrent que si n < 100, l'algorithme
de temps O(n2) s'exécute plus rapidement, et que c'est uniquement lorsque n ≥ 100 est le
temps O(n log n) est meilleur. Expliquez comment cela est possible.
À la consternation d'Alphonse, ils découvrent que si n < 100, l'algorithme de temps O(n2) s'exécute plus
rapidement, et que c'est uniquement lorsque n ≥ 100 est le temps O(n*log(n)) est meilleur. Expliquez
comment cela est possible. D’après nos résultats en haut, il existe C telle que f(n) < C*g(n) Par
conséquent, si l’algorithme de Alphonse A(n*log(n)) fonctionne mieux que celui de Bob B(n²), On peut
s’amuser à résoudre l’expression n*log(n)=n² Le rapport entre n*log(n) / n² donne 100/ 100log(100) =
15.5 s Ce qui signifie que l’algorithme d’alphonse est 15 fois lents sur une itération, mais ceci puisqu’il
effectue moins d’opérations. Plus l’algorithme d’Alphonse exécute plus d’opérations, plus il est meilleur
que celui de BOB.

11. Concevoir un algorithme récursif permettant de trouver l'élément maximal d'une


séquence d'entiers. Implémenter cet algorithme et mesurer son temps d'exécution. Utiliser
Matlab ou Excel pour "fitter" les points expérimentaux et obtenir la fonction associée au
temps d'exécution. Calculer par la méthode des opérations primitives le temps d'exécution
de l'algorithme. Comparer les deux résultats.
Voici un exemple d’algorithme récursif en pseudo-code pour trouver l’élément maximal d’une séquence
d’entiers :

Fonction TrouverMaximal(sequence, debut, fin)

Si debut == fin

Retourner sequence[debut]

Fin Si

milieu = (debut + fin) / 2

maxGauche = TrouverMaximal(sequence, debut, milieu)

maxDroite = TrouverMaximal(sequence, milieu + 1, fin)

Si maxGauche > maxDroite

Retourner maxGauche

Sinon

Retourner maxDroite

Fin Si

Fin Fonction

L’implémentation de cet algorithme donne :


- def trouver_maximal(sequence, debut, fin):
- if debut == fin:
- return sequence[debut]
-
- milieu = (debut + fin) // 2
- max_gauche = trouver_maximal(sequence, debut, milieu)
- max_droite = trouver_maximal(sequence, milieu + 1, fin)
-
- return max(max_gauche, max_droite)
-
- # Exemple d'utilisation :
- sequence = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
- max_element = trouver_maximal(sequence, 0, len(sequence) - 1)
- print(f"L'élément maximal de la séquence est : {max_element}")

le calcul de temps de l’execution donne :


import time
# Mesurer le temps d'exécution
debut = time.time()
max_element = trouver_maximal(sequence, 0, len(sequence) - 1)
fin = time.time()

print(f"Temps d'exécution : {fin - debut} secondes")

12. Concevoir un algorithme récursif qui permet de trouver le minimum et le maximum d'une
séquence de nombres sans utiliser de boucle.

def trouver_min_max(sequence, debut, fin):


if debut == fin: # Si la séquence contient un seul élément
return (sequence[debut], sequence[debut])

if fin - debut == 1: # Si la séquence contient deux éléments


return (min(sequence[debut], sequence[fin]), max(sequence[debut],
sequence[fin]))

# Diviser la séquence en deux moitiés


milieu = (debut + fin) // 2
min_gauche, max_gauche = trouver_min_max(sequence, debut, milieu)
min_droite, max_droite = trouver_min_max(sequence, milieu + 1, fin)

# Retourner le minimum et le maximum globaux


return (min(min_gauche, min_droite), max(max_gauche, max_droite))

# Exemple d'utilisation :
sequence = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
min_element, max_element = trouver_min_max(sequence, 0, len(sequence) - 1)
print(f"Le minimum de la séquence est : {min_element}")
print(f"Le maximum de la séquence est : {max_element}")

13. Concevoir un algorithme récursif permettant de déterminer si une chaine de caractères


contient plus de voyelles que de consonnes.
def est_plus_voyelles_que_consonnes(chaine, index=0, compteur_voyelles=0,
compteur_consonnes=0):
voyelles = 'aeiouAEIOU'

# Cas de base : fin de la chaîne


if index == len(chaine):
return compteur_voyelles > compteur_consonnes
# Vérifier si le caractère actuel est une voyelle ou une consonne
if chaine[index] in voyelles:
compteur_voyelles += 1
elif chaine[index].isalpha():
compteur_consonnes += 1

# Appel récursif avec le caractère suivant


return est_plus_voyelles_que_consonnes(chaine, index + 1,
compteur_voyelles, compteur_consonnes)

# Exemple d'utilisation :
chaine = "Exemple de chaîne de caractères"
resultat = est_plus_voyelles_que_consonnes(chaine)
print(f"La chaîne contient-elle plus de voyelles que de consonnes ?
{resultat}")

REFFERENCES :

- Data Structures and Algorithms in Python ( PDFDrive )


- Algo_Prog_With_Python_01_12_2023_LMD.pdf
- Bing AI (intelligence artificielle)
- Tp algorithmique et programmation 2021-2022 (MARUBA PDF)

Vous aimerez peut-être aussi