Correction 6
Fonctions (2)
Par ailleurs, les solutions proposées correspondent à l'état de vos connaissances au moment où la série est abordée. D'autres solutions pourront être envisagées une fois de nouveaux concepts acquis.
| Exercice 1 | Exercice 2 | Exercice 3 | Exercice 4 | Exercice 5 | Exercice 6 |
Exercice 1 : Surcharge de fonction (niveau 1)
En C++, une fonction est identifiée par son nom et par les types de ses paramètres. On peut en effet imaginer que certaines fonctions se comportent différemment selon le type de paramètre transmis : on n'inverse par exemple pas une matrice de la même manière que l'on inverse un nombre réel.
Dans cet exercice par contre, le contenu de la fonction n'est pas modifié car on échange toujours 2 objets de la même façon. Ce qui change par contre ici, c'est le type des paramètres et de la variable intermédiaire.
void echange(double& a, double& b)
{
double copie(a);
a=b;
b=copie;
}
void echange(char& a, char& b)
{
char copie(a);
a=b;
b=copie;
}
|
|
Notez que le langage C++ offre une possibilité de
faire ce genre de surcharges plus facilement : définir des fonctions
indépendamment du type de leurs arguments.
Ce sont les templates, que nous verrons au second semestre.
C'est un peu comme si on écrivait la fonction générique : void echange(<type>& a, <type>& b)
{
<type> copie(a);
a = b;
b = copie;
}
justement sans avoir à préciser le type. La façon correct de l'écrire en C++ est : template<typename Type>
void echange(Type& a, Type& b)
{
Type copie(a);
a = b;
b = copie;
}
|
|
|
template<typename Type>
void echange(Type& a, Type& b)
{
Type tmp(std::move(a)); // Cela rend a invalide (mais peu importe ici puisqu'on l'écrase à la ligne suivante)
a = std::move(b); // Cela rend b invalide (mais peu importe ici puisqu'on l'écrase à la ligne suivante)
b = std::move(tmp); // Cela rend tmp invalide (mais peu importe ici puisqu'on ne l'utilise plus)
}
|
Exercice 2 : factorielle
- Ouvrez le fichier
factorielle.ccdans votre éditeur favori et saisissez les lignes classiques pour la base du programme :#include <iostream> using namespace std; int main() { return 0; } - Pour la fonction
factorielleIterative, nous devons tout d'abord la prototyper :puis nous pouvons ensuite la définir (par exemple à la fin du fichier). Je vous conseille de toujours commenter vos programmes, et en particulier de dire ce que fait chaque fonction, quels sont ses arguments et à quoi correspond la valeur de retour.#include <iostream> using namespace std; unsigned int factorielleIterative(unsigned int n); int main() { return 0; }La définition (c.-à-d. le corps) de la fonction
factorielleIterativene présente aucune difficulté. Il s'agit d'une simple bouclefor. Il faut simplement veiller aux bonnes conditions initiales.Voici donc l'état de votre programme à ce stade :
#include <iostream> using namespace std; unsigned int factorielleIterative(unsigned int n); int main() { return 0; } /* ---------------------------------------------------------------------- * Calcule de façon itérative la factorielle d'un nombre entier positif. * Entrée : le nombre n dont on veut calculer la factorielle * Sortie : n! * ---------------------------------------------------------------------- */ unsigned int factorielleIterative(unsigned int n) { unsigned int fact(1); for (unsigned int i(2); i <= n; ++i) { fact = fact * i; } return fact; } - De même nous prototypons puis définissons la fonction
factorielleRecursive:#include <iostream> using namespace std; unsigned int factorielleIterative(unsigned int n); unsigned int factorielleRecursive(unsigned int n); int main() { return 0; } /* ---------------------------------------------------------------------- * Calcule de façon itérative la factorielle d'un nombre entier positif. * Entrée : le nombre n dont on veut calculer la factorielle * Sortie : n! * ---------------------------------------------------------------------- */ unsigned int factorielleIterative(unsigned int n) { unsigned int fact(1); for (unsigned int i(2); i <= n; i++) { fact *= i; } return fact; } /* ---------------------------------------------------------------------- * Calcule de façon récursive la factorielle d'un nombre entier positif. * Entrée : le nombre n dont on veut calculer la factorielle * Sortie : n! * ---------------------------------------------------------------------- */ unsigned int factorielleRecursive(unsigned int n) { if (n == 0) { return 1; } else { return (n * factorielleRecursive(n-1)); } } - Détermination de la condition d'arrêt :
if (n == 0) return 1;
On pourrait même optimiser un peu plus et écrire :if (n <= 1) return 1;
- Écriture de la relation de récurrence :
n * factorielleRecursive(n-1)
- L'ajout de la fonction
demander_nombrese fait exactement comme déjà vu les semaines passées. - Il faut maintenant écrire le corps principal du programme
(fonction
main).La base est assez simple :
- Déclarer une variable entière :
unsigned int n;
- Demander sa valeur. Tant qu'à faire pourquoi ne pas le faire
lors de l'initialisation de cette variable ? :
unsigned int n(demander_nombre(0, 12));
- Appeler successivement les deux méthodes et afficher les résultats :
cout << "Méthode itérative :" << endl; cout << " " << n << "! = " << factorielleIterative(n) << endl; cout << "Méthode récursive :" << endl; cout << " " << n << "! = " << factorielleRecursive(n) << endl;
- Déclarer une variable entière :
- La boucle demandant à l'utilisateur s'il souhaite recommencer se fait exactement comme dans l'exercice 0.
Cette fonction récursive est construite sur le schéma vu en cours :
On a donc finalement le programme suivant :
#include <iostream>
#include <limits>
#include <string>
using namespace std;
unsigned int demander_nombre(unsigned int min, unsigned int max);
unsigned int factorielleIterative(unsigned int n);
unsigned int factorielleRecursive(unsigned int n);
// ----------------------------------------------------------------------
int main()
{
char rep;
do {
unsigned int n(demander_nombre(0,12));
cout << "Méthode itérative :" << endl;
cout << " " << n << "! = " << factorielleIterative(n)
<< endl;
cout << "Méthode récursive :" << endl;
cout << " " << n << "! = " << factorielleRecursive(n)
<< endl;
do {
cout << "Voulez-vous recommencer [o/n] ? ";
cin >> rep;
} while ((rep != 'o') and (rep != 'n'));
} while (rep == 'o');
return 0;
}
/* ----------------------------------------------------------------------
* Calcule de façon itérative la factorielle d'un nombre unsigned int positif.
* Entrée : le nombre n dont on veut calculer la factorielle
* Sortie : n!
* ---------------------------------------------------------------------- */
unsigned int factorielleIterative(unsigned int n)
{
unsigned int fact(1);
for (unsigned int i(2); i <= n; ++i) {
fact *= i;
}
return fact;
}
/* ----------------------------------------------------------------------
* Calcule de façon récursive la factorielle d'un nombre unsigned int positif.
* Entrée : le nombre n dont on veut calculer la factorielle
* Sortie : n!
* ---------------------------------------------------------------------- */
unsigned int factorielleRecursive(unsigned int n)
{
if (n == 0) {
return 1;
}
else {
return (n * factorielleRecursive(n-1));
}
}
/* --------------------------------------------------------------
* fonction demandant à l'utilisateur un nombre compris
* dans un intervalle [a, b], ou supérieur ou égal à a
* si b < a.
* Entrée : les deux nombres a et b définissant l'intervalle
* Sortie : le nombre saisi par l'utilisateur
* -------------------------------------------------------------- */
unsigned int demander_nombre(unsigned int a, unsigned int b)
{
unsigned int res;
do {
cout << "Entrez un nombre entier ";
if (a >= b) {
cout << "supérieur ou égal à " << a;
}
else {
cout << "compris entre " << a << " et " << b;
}
cout << " : ";
cin >> res;
} while ((res < a) or ((a < b) and (res > b)));
return res;
}
Exercice 3 : nombres de Fibonacci
Cet exercice est très similaire au précédent et se fait de la même façon.
Le programme complet est :
#include <iostream>
#include <limits>
#include <string>
using namespace std;
int demander_nombre(int min, int max);
int Fibonacci(int n);
int FibonacciIteratif(int n);
// ----------------------------------------------------------------------
int main()
{
char rep;
do {
int n(demander_nombre(0, 40));
cout << "Méthode itérative :" << endl;
cout << " F(" << n << ") = " << FibonacciIteratif(n)
<< endl;
cout << "Méthode récursive :" << endl;
cout << " F(" << n << ") = " << Fibonacci(n) << endl;
do {
cout << "Voulez-vous recommencer [o/n] ? ";
cin >> rep;
} while ((rep != 'o') and (rep != 'n'));
} while (rep == 'o');
return 0;
}
/* ----------------------------------------------------------------------
* Calcule de façon itérative le n-ieme nombre de Fibonacci.
* Entrée : le nombre n
* Sortie : F(n)
* ---------------------------------------------------------------------- */
int FibonacciIteratif(int n)
{
int Fn(0); // stocke F(i) , initialisé par F(0)
int Fn_1(Fn); // stocke F(i-1), initialisé par F(0)
int Fn_2(1); // stocke F(i-2), initialisé par F(-1)
for (int i(1); i <= n; ++i) {
Fn = Fn_1 + Fn_2; // pour n>=1 on calcule F(n)=F(n-1)+F(n-2)
Fn_2 = Fn_1; // et on decale...
Fn_1 = Fn;
}
return Fn;
}
/* ----------------------------------------------------------------------
* Calcule de façon récursive le n-ieme nombre de Fibonacci.
* Entrée : le nombre n
* Sortie : F(n)
* ---------------------------------------------------------------------- */
unsigned int Fibonacci(unsigned int n)
{
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return Fibonacci(n-1) + Fibonacci(n-2);
}
/* --------------------------------------------------------------
* fonction demandant à l'utilisateur un nombre compris
* dans un intervalle [a, b], ou supérieur ou égal à a
* si b < a.
* Entrée : les deux nombres a et b définissant l'intervalle
* Sortie : le nombre saisi par l'utilisateur
* -------------------------------------------------------------- */
int demander_nombre(int a, int b)
{
int res;
do {
cout << "Entrez un nombre int ";
if (a >= b) {
cout << "supérieur ou égal à " << a;
}
else {
cout << "compris entre " << a << " et " << b;
}
cout << " : ";
cin >> res;
} while ((res < a) or ((a < b) and (res > b)));
return res;
}
Exercice 4 : Calcul approché d'une intégrale (niveau 2)
#include <iostream>
#include <cmath>
using namespace std;
double f(double x);
double integre(double a, double b);
double demander_nombre();
int main()
{
double a(demander_nombre());
double b(demander_nombre());
// on définit la précision de l'affichage à 12 chiffres après la virgule
cout.precision(12);
cout << "Integrale de f(x) entre " << a
<< " et " << b << " :" << endl;
cout << integre(a,b) << endl;
return 0;
}
double f(double x) { return x*x; }
// double f(double x) { return x*x*x ; }
// double f(double x) { return 1.0/x ; }
// double f(double x) { return sin(x); }
double integre(double a, double b)
{
double res;
res = 41.0 * ( f(a) + f(b) )
+ 216.0 * ( f((5*a+b)/6.0) + f((5*b+a)/6.0) )
+ 27.0 * ( f((2*a+b)/3.0) + f((2*b+a)/3.0) )
+ 272.0 * f((a+b)/2.0) ;
res *= (b-a)/840.0;
return res;
}
double demander_nombre()
{
double res;
cout << "Entrez un nombre réel : ";
cin >> res;
return res;
}
Exercice 5 : Fonctions logiques
On peut remarquer que A ET A = A. NON(A ET A) vaut donc NON(A). On peut alors écrire la fonction non ainsi :
bool non(bool A)
{
return non_et(A, A);
}
Comme NON(NON(C)) = C, on a NON(NON(A ET B)) = A ET B. On peut donc écrire la fonction et ainsi:
bool et(bool A, bool B)
{
return non(non_et(A, B));
}
NON(A OU B) = NON(A) ET NON(B). Donc, (A OU B) = NON(NON(A OU B)) = NON(NON(A) ET NON(B)). On peut donc écrire la fonction ou ainsi:
bool ou(bool A, bool B)
{
return non_et(non(A), non(B))
}
Ces relations sont intéressantes en électronique: il suffit d'un
seul type de composant (effectuant la fonction non_et) pour
réaliser n'importe quel circuit logique.
Exercice 6 : recherche dichotomique
Aucune difficulté majeure pour cet exercice qui est niveau 2 uniquement parce que l'énoncé est moins détaillé.
Voici le code correspondant :
#include <iostream>
#include <limits>
using namespace std;
int demander_nombre(int min, int max);
unsigned int cherche(unsigned int borneInf, unsigned int borneSup);
constexpr unsigned int MIN(1);
constexpr unsigned int MAX(100);
// ----------------------------------------------------------------------
int main()
{
cout << "Pensez à un nombre entre " << MIN << " et " << MAX << "."
<< endl;
cout << "Tapez sur 'Entrée' quand vous êtes prêt(e)." << endl;
// l'instruction suivante permet de faire une lecture en ignorant le contenu
// lu (nous aurons l'occasion d'y revenir)
cin.ignore(numeric_limits<streamsize>::max(), '\n');
const unsigned int solution( cherche(MIN, MAX) );
cout << endl << "Votre nombre était " << solution << '.'
<< endl;
return 0;
}
/* ----------------------------------------------------------------------
* Recherche d'un nombre par dichotomie dans un intervalle [a b]
* Entrée : les bornes de l'intervalle
* Sortie : la solution
* ---------------------------------------------------------------------- */
unsigned int cherche(unsigned int a, unsigned int b)
{
// cout << "[ " << a << ", " << b << " ]" << endl;
if (b < a) {
cerr << "ERREUR: vous avez répondu de façon inconsistante !" << endl;
return b;
}
unsigned int pivot((a+b)/2);
char rep;
do {
cout << "Le nombre est il <, > ou = à " << pivot << " ? ";
cin >> rep;
} while ((rep != '=') and (rep != '<') and (rep != '>'));
switch (rep) {
case '=':
return pivot;
case '<':
return cherche(a, pivot-1);
case '>':
return cherche(pivot+1, b);
}
}
Le seul petit truc auquel il faut penser est peut être le switch : oui ! on peut faire un
switch sur un caractère.
Dernière mise à jour : 2025/10/14 (16:36)