Choisissez votre style : style 1,    style 2,    sans commentaire,    impression,    ancien.

Correction
Structures

Exercice 1 Exercice 2 Exercice 3 Exercice 4 Exercice 5 Exercice 6 Exercice 7 Exercice 8

Exercice 1 : nombres complexes

Exercice n°22 (pages 60 et 226) de l'ouvrage C++ par la pratique.

Le code fourni ici est en C++11. Pour une version compilant avec l'ancien standard (C++98) voir ci-dessous.

#include <iostream>
using namespace std;
 
struct Complexe {
  double x;
  double y;
};
 
// Solution simple
void affiche(Complexe z) 
{
  cout << "(" << z.x << "," << z.y << ")";
 
  // autre solution :  cout << z.x << "+" << z.y << "i";
}
 
// Solution plus complexe mais plus élégante
void affiche2(Complexe z)
{
  if ((z.x == 0.0) and (z.y == 0.0)) {
    cout << "0";
    return;
  }
 
  if (z.x != 0.0) {
    cout << z.x;
    if (z.y > 0.0)
      cout << "+";
  }
  if (z.y != 0.0) {
    if ((z.x == 0.0) and (z.y == -1.0))
      cout << "-";
    else if (z.y != 1.0)
      cout << z.y;
    cout << "i";
  }
}
 
Complexe addition(Complexe z1, Complexe z2)
{
  return { z1.x + z2.x, z1.y + z2.y };
}
 
Complexe soustraction(Complexe z1, Complexe z2)
{
  return { z1.x - z2.x, z1.y - z2.y };
}
 
Complexe multiplication(Complexe z1, Complexe z2)
{
  return { z1.x * z2.x - z1.y * z2.y ,
           z1.x * z2.y + z1.y * z2.x };
}
 
Complexe division(Complexe z1, Complexe z2)
{
  const double r(z2.x*z2.x + z2.y*z2.y);
  return { (z1.x * z2.x + z1.y * z2.y) / r ,
	   (z1.y * z2.x - z1.x * z2.y) / r };
}
 
int main()
{
  Complexe un = { 1.0, 0.0 };
  Complexe i  = { 0.0, 1.0 };
 
  affiche(un); cout << " + "; affiche(i); cout << " = ";
  Complexe j(addition(un, i));
  affiche(j); cout << endl;
 
  affiche(i); cout << " * "; affiche(i); cout << " = ";
  affiche(multiplication(i,i)); cout << endl;
 
  affiche(j); cout << " * "; affiche(j); cout << " = ";
  Complexe z(multiplication(j,j));
  affiche(z); cout << endl;
 
  affiche(z); cout << " / "; affiche(i); cout << " = ";
  affiche(division(z,i)); cout << endl;
 
  z= { 2.0, -3.0 };
  affiche(z); cout << " / "; affiche(j); cout << " = ";
  affiche(division(z,j)); cout << endl;
 
  return 0;
}
 

La seule différence en C++98, c'est que la syntaxe d'initialisation n'est pas permise en affectation. En clair, les expression du type :

  return { z1.x + z2.x, z1.y + z2.y };

doivent être remplacées par une initialisation de variable :

  Complexe z = { z1.x + z2.x, z1.y + z2.y };
  return z;

De même dans le main()

  z= { 2.0, -3.0 };

doit être remplacé par :

  z.x = 2.0; z.y = -3.0;

Encore une illustration des avantages de la nouvelle norme C++11 !


Exercice 2 : QCM

Exercice n°24 (pages 61 et 228) de l'ouvrage C++ par la pratique.

Le code fourni ici est en C++11. Pour une version compilant avec l'ancien standard (C++98) voir ci-dessous.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
 
struct QCM {
  string question;
  vector<string> reponses;
  unsigned int solution;
};
 
typedef vector<QCM> Examen;
 
void affiche(const QCM& question);
unsigned int demander_nombre(unsigned int min, unsigned int max);
unsigned int poser_question(const QCM& question);
Examen creer_examen();
 
// ======================================================================
int main()
{
  unsigned int note(0);
  Examen exam(creer_examen());
 
  for (auto question : exam) {
    if (poser_question(question) == question.solution) {
      ++note;
    }
  }
 
  cout << "Vous avez trouvé " << note << " bonne";
  if (note > 1) cout << 's';
  cout << " réponse";
  if (note > 1) cout << 's';
  cout << " sur " << exam.size() << "." << endl;
 
  return 0;
}
 
// ======================================================================
void affiche(const QCM& q)
{
  cout << q.question << " ?" << endl;
  unsigned int i(0);
  for (auto reponse : q.reponses) {
    cout << "    " << ++i << "- " << reponse << endl;
  }
}
 
// ======================================================================
unsigned int demander_nombre(unsigned int a, unsigned int b)
{
  /* échange les arguments s'ils n'ont pas été donnés dans *
   * le bon sens.                                          */
  if (a > b) { unsigned int tmp(b); b=a; a=tmp; }
 
  unsigned int res;
  do {
    cout << "Entrez un nombre entier compris entre "
         << a << " et " << b <<" : ";
    cin >> res;
  } while ((res < a) or (res > b));
 
  return res;
}
 
// ======================================================================
unsigned int poser_question(const QCM& q)
{
  affiche(q);
  return demander_nombre(1, q.reponses.size());
}
 
// ======================================================================
Examen creer_examen()
{
  return {
    // Question 1
    { "Combien de dents possède un éléphant adulte",
      { "32", "de 6 à 10", "beaucoup", "24", "2" },
      2 // réponse
    },
 
    // Question 2
    { "Laquelle des instructions suivantes est un prototype de fonction",
      { "int f(0);"     ,
        "int f(int 0);" ,
        "int f(int i);" ,
        "int f(i);"     },
      3 // réponse
    },
 
    // Question 3
    { "Qui pose des questions stupides",
      { "le prof. de math",
        "mon copain/ma copine",
        "le prof. de physique",
        "moi",
        "le prof. d'info",
        "personne, il n'y a pas de question stupide",
        "les sondages" } ,
      6 // réponse
    }
  };
}
 

La principale différence en C++98 est que la syntaxe d'initialisation n'est pas permise ni pour les vectors, ni en affectation pour les structs. Cela change pas mal la fonction creer_examen :

Examen creer_examen()
{
  QCM q;
  Examen retour;
 
  q.question = "Combien de dents possède un éléphant adulte";
  q.reponses.clear();
  q.reponses.push_back("32");
  q.reponses.push_back("de 6 à 10");
  q.reponses.push_back("beaucoup");
  q.reponses.push_back("24");
  q.reponses.push_back("2");
  q.solution=2;
  retour.push_back(q);
 
  q.question = "Laquelle des instructions suivantes est un prototype de fonction";
  q.reponses.clear();
  q.reponses.push_back("int f(0);");
  q.reponses.push_back("int f(int 0);");
  q.reponses.push_back("int f(int i);");
  q.reponses.push_back("int f(i);");
  q.solution=3;
  retour.push_back(q);
 
  q.question = "Qui pose des questions stupides";
  q.reponses.clear();
  q.reponses.push_back("le prof. de math");
  q.reponses.push_back("mon copain/ma copine");
  q.reponses.push_back("le prof. de physique");
  q.reponses.push_back("moi");
  q.reponses.push_back("le prof. d'info");
  q.reponses.push_back("personne, il n'y a pas de question stupide");
  q.reponses.push_back("les sondages");
  q.solution=6;
  retour.push_back(q);
 
  return retour;
}
 

L'autre changement est que les boucle for(:) (range-based for) n'existent pas en C++98. Il faut les remplacer par des boucles «à la C» :

int main()
{
  //...
  for (unsigned int i(0); i < exam.size(); i++) {
    if (poser_question(exam[i]) == exam[i].solution) {
      ++note;
    }
  }
  //...
}
 
// ======================================================================
void affiche(const QCM& q)
{
  cout << q.question << " ?" << endl;
  for (unsigned int i(0); i < q.reponses.size(); ++i) {
    cout << "    " << i+1 << "- " << q.reponses[i] << endl;
  }
}
 
// ...
 

Exercice 3 : nombres complexes revisités

Exercice n°25 (pages 63 et 230) de l'ouvrage C++ par la pratique.

Le code fourni ici est en C++11. Pour C++98, voir les remarques de l'exercice 1.

#include <iostream>
#include <cmath>
using namespace std;
 
// ----------------------------------------------------------------------
struct Complexe {
  double x;
  double y;
};
 
struct Solutions {
  Complexe z1;
  Complexe z2;
};
 
// ----------------------------------------------------------------------
void affiche(Complexe z);
 
Complexe addition      (Complexe z1, Complexe z2);
Complexe soustraction  (Complexe z1, Complexe z2);
Complexe multiplication(Complexe z1, Complexe z2);
Complexe division      (Complexe z1, Complexe z2);
Complexe sqrt          (Complexe z);
 
Solutions resoudre_second_degre(Complexe b, Complexe c);
 
// ======================================================================
int main()
{
  Complexe b  = { 3.0, -2.0 };
  Complexe c  = { -5.0, 1.0 };
  Solutions s;
 
  s = resoudre_second_degre(b, c);
  cout << "Avec b="; affiche(b); cout << " et c="; affiche(c); 
  cout << " on a :" << endl;
  cout << "  z1="; affiche(s.z1); cout << endl;
  cout << "  z2="; affiche(s.z2); cout << endl;
 
  return 0;
}
 
// ======================================================================
void affiche(Complexe z)
{
  if ((z.x == 0.0) and (z.y == 0.0)) {
    cout << "0";
    return;
  }
 
  if (z.x != 0.0) {
    cout << z.x;
    if (z.y > 0.0)
      cout << "+";
  }
  if (z.y != 0.0) {
    if ((z.x == 0.0) and (z.y == -1.0))
      cout << "-";
    else if (z.y != 1.0)
      cout << z.y;
    cout << "i";
  }
}
 
// ======================================================================
Complexe addition(Complexe z1, Complexe z2)
{
  return { z1.x + z2.x, z1.y + z2.y };
}
 
// ======================================================================
Complexe soustraction(Complexe z1, Complexe z2)
{
  return { z1.x - z2.x, z1.y - z2.y };
}
 
// ======================================================================
Complexe multiplication(Complexe z1, Complexe z2)
{
  return { z1.x * z2.x - z1.y * z2.y ,
           z1.x * z2.y + z1.y * z2.x };
}
 
// ======================================================================
Complexe division(Complexe z1, Complexe z2)
{
  const double r(z2.x*z2.x + z2.y*z2.y);
  return { (z1.x * z2.x + z1.y * z2.y) / r ,
	   (z1.y * z2.x - z1.x * z2.y) / r };
}
 
// ======================================================================
Complexe sqrt(Complexe z)
{
  const double r(sqrt(z.x * z.x + z.y * z.y));
  Complexe retour;
 
  retour.x = sqrt((r + z.x) / 2.0);
  if (z.y >= 0.0)
    retour.y = sqrt((r - z.x) / 2.0);
  else
    retour.y = - sqrt((r - z.x) / 2.0);
 
  return retour;
}
 
// ======================================================================
Solutions resoudre_second_degre(Complexe b, Complexe c)
{
  constexpr Complexe deux   = { 2.0, 0.0 };
  constexpr Complexe quatre = { 4.0, 0.0 };
 
  const Complexe sdelta(sqrt(soustraction(multiplication(b, b),
                                          multiplication(quatre, c))));
 
  // calcule -b
  b.x = -b.x; b.y = -b.y;
 
  Solutions reponse;
  reponse.z1 = division( soustraction(b, sdelta) , deux);
  reponse.z2 = division( addition    (b, sdelta) , deux);
 
  return reponse;
}
 

Exercice 4 : Fractions

#include <iostream>
#include <cmath>
using namespace std;
 
// ======================================================================
// la structure de données
struct Fraction
{
  int numerateur;
  int denominateur;
};
 
// ======================================================================
// Calcul du plus grand diviseur commun
unsigned int pgcd(unsigned int a, unsigned int b) 
{
  unsigned int m(b);
  if (a < b) {
    m = a;
  }
 
  while ((a % m != 0) or (b % m != 0)) {
    --m;
  }
 
  return m;
}
 
// ======================================================================
// Initialisation d'une fraction, sous sa forme irréductible
Fraction init_frac(int num, int den)
{
  const int div( pgcd( abs(num) , abs(den) ) );
 
  if (num < 0 and den < 0) {
    num = -num;
    den = -den;
  }
 
  return { num / div , den / div };
}
 
// ======================================================================
// Affichage d'une fraction
void afficher_frac(Fraction f)
{
  cout << f.numerateur << "/" << f.denominateur;
}
 
// ======================================================================
// Addition de 2 fractions
Fraction add_frac(Fraction f1, Fraction f2)
{
  // Note : la fonction init_frac rend la fraction irreductible
  return init_frac(f1.numerateur * f2.denominateur + f2.numerateur * f1.denominateur,
                   f1.denominateur * f2.denominateur);
}
 
// ======================================================================
// Multiplication de 2 fractions
Fraction mult_frac(Fraction f1, Fraction f2)
{
  return init_frac(f1.numerateur   * f2.numerateur,
                   f1.denominateur * f2.denominateur);
}
 
// ======================================================================
// Multiplication d'une fractions par un nombre
Fraction mult_scal_frac(int scalaire, Fraction f)
{
  return init_frac(f.numerateur * scalaire, f.denominateur);
}
 
// ======================================================================
int main()
{
  Fraction f1 = init_frac(5, 2);  // 5/2
  Fraction f2 = init_frac(3, 12); // 3/12 --> 1/4
 
  cout << "f1 = ";
  afficher_frac(f1);
  cout << " et f2 = ";
  afficher_frac(f2);
  cout << endl;
 
  cout << "f1 + f2 = ";
  afficher_frac(add_frac(f1, f2));
  cout << endl;
 
  cout << "f1 * f2 = ";
  afficher_frac(mult_frac(f1, f2));
  cout << endl;
 
  cout << "2 * f2 = ";
  afficher_frac(mult_scal_frac(2, f2));
  cout << endl;
 
  return 0;
}
 

Exercice 5 : Echantillonnage de signaux

/* Programme pour tester le th. d'échantillonnage 
 */
 
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
 
#ifndef M_PI
#  define M_PI 3.14159265358979323846
#endif
 
// --------------------------------------------------
struct Sinusoide {
  double amplitude;
  double frequence;
  double dephasage;
};
 
// --------------------------------------------------
typedef vector<Sinusoide> Signal;
 
// --------------------------------------------------
struct Signal_Echant {
  double fe;       // fréquence d'échantillonnage
  double t0;       // temps initial
  vector<double> echantillons;
};
 
// ======================================================================
// fonction outil : composante sinusoidale pure
 
double sin_pure(double x, Sinusoide const& s = { 1.0, 1.0, 0.0 })
{
  return s.amplitude * sin(2 * M_PI * s.frequence * x + s.dephasage);
}
 
// ======================================================================
// fonction outil : signal = somme de sinus
 
double signal(double x, Signal const& s)
{
  double val(0.0);
  for (auto composante : s) {
    val += sin_pure(x, composante);
  }
  return val;
}
 
// ======================================================================
// échantillonnage d'un signal
 
Signal_Echant echantillonne(Signal const& s, double freq,
                            double t_min = 0.0, double t_max = 1.0)
{
  Signal_Echant se;
  se.fe = freq;
  se.t0 = t_min;
  se.echantillons = vector<double>(1+size_t(freq * (t_max - t_min)));
 
  for (size_t i(0); i < se.echantillons.size(); ++i) {
    se.echantillons[i] = signal(t_min + i / freq, s);
  }
  return se;
}
 
// ======================================================================
// fonction outil : sinus cardinal
 
double sinc(double x, double precision = 1e-8)
{
  return (abs(x) < precision ? 1.0 : sin(M_PI * x)/(M_PI * x));
}
 
// ======================================================================
// formule de reconstruction
 
double reconstruction(double x, Signal_Echant const& s)
{
  double valeur(0.0);
  cout << endl << "\t+ fe = " << s.fe << endl << "\t+ t0=" << s.t0 << endl
       << "\t+ nb ech = " << s.echantillons.size() << endl;
  for (size_t i(0); i < s.echantillons.size(); ++i) {
    cout << "\t\t" << s.echantillons[i] << " * sinc(" << s.fe << " * (" << x << " - " << s.t0  <<") - "<<i<<" ) = " << s.echantillons[i] << " * " << sinc(s.fe * (x - s.t0) - i) << " = " << s.echantillons[i] * sinc(s.fe * (x - s.t0) - i) << endl;
    valeur += s.echantillons[i] * sinc(s.fe * (x - s.t0) - i);
  }
  cout << "\t-> " << valeur << endl;
  return valeur;
}
 
// ======================================================================
int main()
{
  Signal s({
    { 1.0 , 2.0, 0.0 }, // première composante
    { 0.5 , 4.0, 0.1 }, // ...
    { 0.33, 6.0, 0.2 }, // ...
    { 0.25, 8.0, 0.3 }, // ...
   });
 
  constexpr double t_min(0.0);
  constexpr double t_max(2.0);
 
  Signal_Echant se1(echantillonne(s, 20.0, t_min, t_max));
  Signal_Echant se2(echantillonne(s, 11.0, t_min, t_max));
 
  // « dessin » des trois courbes
  constexpr size_t nb_points(1000);
  constexpr double dt((t_max-t_min) / nb_points);
 
  for (double t(t_min); t <= t_max; t += dt) {
    cout << t 
         << " " << signal(t, s)
         << " " << reconstruction(t, se1)
         << " " << reconstruction(t, se2)
      << endl;
  }
 
  return 0;
}
 

Exercice 6 : Sac à dos

/* ======================================================================
 *
 * Knapsack problem.
 *
 * Version: 1.0 (170901)
 * Author: J.-C. Chappelier (EPFL, 2017)
 *
 * ======================================================================
 */
#include <iostream>
#include <vector>
#include <algorithm> // pour sort()
using namespace std;
 
struct Object {
  double weight;
  double value;
};
 
/* ======================================================================
 * This is the first algorithm:
 * solve it naively, in the given order.
 */
void solve_naive(vector<Object> const& objects, double remaining_weight)
{
  double value(0.0);
  for (auto const& object : objects) {
    cout << "considering : w=" << object.weight << ", v=" << object.value;
    if (object.weight <= remaining_weight) {
      value += object.value;
      remaining_weight -= object.weight;
      cout << " --> taking";
    }
    cout << endl;
  }
  cout << "value = " << value << ", remaining weight = " << remaining_weight << endl;
}
 
// ======================================================================
bool higher_value(Object const& o1, Object const& o2)
{ return o1.value > o2.value; }
 
/* ======================================================================
 * This is the second algorithm: greedy.
 */
void solve_greedy(vector<Object> const& objects, double maximum_weight)
{
  vector<Object> sorted_objects(objects); // need a copy to sort
  sort(sorted_objects.begin(), sorted_objects.end(), higher_value);
  solve_naive(sorted_objects, maximum_weight);
}
 
/* ======================================================================
 * This is the third algorithm: try all.
 */
double solve_exact_recursive(vector<Object> const& objects, size_t start, double& remaining_weight)
{
  double best_value(0.0);
 
  if ((not objects.empty()) and (start < objects.size())) {
    // 1) consider NOT taking first object
    double best_remaining_weight(remaining_weight);
    best_value = solve_exact_recursive(objects, start+1, best_remaining_weight);
 
    // 2) consider taking the first object, if possible
    if (objects[start].weight <= remaining_weight) {
      double weight(remaining_weight - objects[start].weight);
      const double value(objects[start].value + solve_exact_recursive(objects, start+1, weight));
 
 
      // 3) keep the best of the two
      if (value > best_value) {
        best_remaining_weight = weight;
        best_value = value;
      }
    }
    remaining_weight = best_remaining_weight;
  }
  return best_value;
}
 
void solve_exact(vector<Object> const& objects, double remaining_weight)
{
  double value(solve_exact_recursive(objects, 0, remaining_weight));
  cout << "value = " << value << ", remaining weight = " << remaining_weight << endl;
}
 
// ======================================================================
int main()
{
  const vector<Object> objects({
      { 4.0,  8.0 },
      { 2.0,  3.0 },
      { 5.0,  7.0 },
      { 6.0, 10.0 },
  });
  constexpr double MAX(9.0);
 
  solve_naive (objects, MAX);
  solve_greedy(objects, MAX);
  solve_exact (objects, MAX);
 
  return 0;
}
 

Exercice 7 : Code de Huffman

#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std;
 
// ======================================================================
struct Mot_de_code {
  string entree;
  double proba;
  string code;
};
 
// ======================================================================
typedef vector<Mot_de_code> Code;
/* Note : les vector ne sont pas la bonne structure de données pour faire
 *   cela (associer une info à un mot en entrée) car la recherche y
 *   est linéraire.
 *   Il vaudrait mieux utiliser des hash-tables.
 *   Mais celles-ci n'ont pas encore été présentées en cours (arrivent à
 *   la fin du 2e semestre).
 */
 
// ======================================================================
void affiche_code(Code const& c) {
  for (auto el : c) {
    cout << '"' << el.entree << "\" --> \"" << el.code << "\" (" << el.proba << ')' << endl;
  }
}
 
/* ======================================================================
 * ajoute un mot au code ou incrémente son compte si déjà présent.
 */
void add(string const& mot, Code& code) {
  // recherche le mot
  for (auto& el : code) {
    if (el.entree == mot) {
      // le mot y est déjà : on incrémente son compte et on quitte
      ++el.proba;
      return;
    }
  }
 
  // On n'a pas trouvé le mot => on l'ajoute (avec un compte de 1)
  code.push_back({ mot, 1.0, "" });
}
 
// ======================================================================
void normalise(Code& code) {
  double somme(0.0);
  for (auto el : code) {
    somme += el.proba;
  }
  if (somme > 0.0) {
    for (auto& el : code) {
      el.proba /= somme;
    }
  }
}
 
// ======================================================================
Code calcule_probas(string const& texte, size_t tranche = 1)
{
  Code code;
  for (size_t i(0); i < texte.size(); i += tranche) {
    string mot(texte.substr(i, tranche));
 
    // remplir d'espaces à la fin si nécessaire
    while (mot.size() < tranche) mot += ' ';
 
    add(mot, code);
  }
 
  // normalise les probabilités
  normalise(code);
 
  return code;
}
 
// ======================================================================
struct Entite {
  vector<size_t> indices;
  double proba;
};
 
// ======================================================================
Entite fusionne(Entite const& L1, Entite const& L2) {
  Entite L;
  L.proba = L1.proba + L2.proba;
  size_t i(0);
  size_t j(0);
  while ((i < L1.indices.size()) and (j < L2.indices.size())) {
    if (L1.indices[i] < L2.indices[j]) {
      L.indices.push_back(L1.indices[i]);
      ++i;
    } else {
      L.indices.push_back(L2.indices[j]);
      ++j;
    }
  }
  while (i < L1.indices.size()) {
    L.indices.push_back(L1.indices[i]);
    ++i;
  }
  while (j < L2.indices.size()) {
    L.indices.push_back(L2.indices[j]);
    ++j;
  }
  return L;
}
 
// ======================================================================
vector<Entite> listes_initiales(Code const& c) {
  vector<Entite> v(c.size());
 
  for (size_t i(0); i < c.size(); ++i) {
    v[i].indices.push_back(i);
    v[i].proba = c[i].proba;
  }
  return v;
}
 
// ======================================================================
void deux_moins_probables(vector<Entite> tab, size_t& prems, size_t& deuz)
{
  if (tab.size() < 2) return;
  double min1(tab[0].proba); prems = 0;
  double min2(tab[1].proba); deuz  = 1;
  if (min1 > min2) {
    swap(min1, min2);
    swap(prems, deuz);
  }
 
  for (size_t i(2); i < tab.size(); ++i) {
    if (tab[i].proba < min1) {
      min2 = min1;
      deuz = prems;
      min1 = tab[i].proba;
      prems = i;
    } else if (tab[i].proba < min2) {
      min2 = tab[i].proba;
      deuz = i;
    } 
  }
}
 
// ======================================================================
void ajoute_symbole(char bit, Code& c, Entite l)
{
  for(auto i : l.indices) {
    c[i].code = bit + c[i].code;
  }
}
 
// ======================================================================
void Huffman(Code& c)
{
  vector<Entite> arbre(listes_initiales(c));
 
  while (arbre.size() > 1) {
    size_t max_1(0), max_2(0);
 
    // recherche les deux moins probables
    deux_moins_probables(arbre, max_1, max_2);
 
    // ajoute respectivement 0 et 1 à leur code
    ajoute_symbole('0', c, arbre[max_1]);
    ajoute_symbole('1', c, arbre[max_2]);
 
    // les fusionne (en écrasant max_1)
    arbre[max_1] = fusionne(arbre[max_1], arbre[max_2]);
 
    // et réduit l'arbre (= « monte » dans l'arbre) en supprimant max_2
    arbre[max_2] = arbre.back();
    arbre.pop_back();
  }
}
 
// ======================================================================
string recherche_entree(string const& texte, size_t& pos, Code const& c)
{
  for (auto el : c) {
    size_t i(0), j(pos);
    while ((i < el.entree.size()) and (j < texte.size()) and (el.entree[i] == texte[j])) {
      ++i; ++j;
    }
    if ((i == el.entree.size()) and (j <= texte.size())) { // match with el
      pos = j; // update pos to point to the rest of texte
      return el.code;
    }
    // special case for end of texte
    if ((j == texte.size()) and (el.entree[i] == ' ')) {
      while ((i < el.entree.size()) and (el.entree[i] == ' ')) ++i;
      if (i == el.entree.size()) { // match with el (ending with whitespaces
        pos = j;
        return el.code;
      }
    }
  }
  return string();
}
 
// ======================================================================
string recherche_mot_de_code(string const& texte, size_t& pos, Code const& c)
{
  for (auto el : c) {
    size_t i(0), j(pos);
    while ((i < el.code.size()) and (j < texte.size()) and (el.code[i] == texte[j])) {
      ++i; ++j;
    }
    if ((i == el.code.size()) and (j <= texte.size())) { // match with el
      pos = j; // update pos to point to the rest of texte
      return el.entree;
    }
  }
  return string();
}
 
// ======================================================================
string encode(string const& texte, Code const& c)
{
  string code;
  for (size_t i(0); i < texte.size(); // c'est recherche() qui va mettre i à jour
      ) {
    code += recherche_entree(texte, i, c);
  }
  return code;
}
 
// ======================================================================
string decode(string const& texte_code, Code const& c)
{
  string texte;
  for (size_t i(0); i < texte_code.size(); // c'est recherche() qui va mettre i à jour
      ) {
    texte += recherche_mot_de_code(texte_code, i, c);
  }
  return texte;
}
 
// ======================================================================
void test(string const& texte, size_t tranche)
{
  cout << "==== Par tranches de taille " << tranche << endl;
  Code c(calcule_probas(texte, tranche));
  //affiche_code(c);
  Huffman(c);
  affiche_code(c);
  string tc(encode(texte, c));
  cout << "taille orig. = " << texte.size() << " lettres/octets" << endl;
  cout << "taille codee = " << tc.size() << " bits = " << 1 + (tc.size() - 1) / 8 << " octets" << endl;
  cout << "long. moyenne  = " << double(tc.size()) /  double(texte.size()) << " bits" << endl; 
  cout << "compression  = " << 100.0 - 100.0 * tc.size() / (8.0 * texte.size()) << '%' << endl; 
  cout << "texte codé : " << tc << endl;
  cout << "check : " << decode(tc, c) << endl;
}
 
// ======================================================================
int main()
{
   string texte = "Un petit texte à compresser : le but de cet exercice est de réaliser des codes de Huffman de textes. Le principe du code de Huffman (binaire) est assez simple : il consiste à regrouper les deux mots les moins probables à chaque étape.";
 
  test(texte, 1);
  test(texte, 2);
  test(texte, 3);
  test(texte, 5);
 
  cout << "Entrez une chaîne : ";
  cin >> texte;
  test(texte, 1);
 
  return 0;
}
 

Exercice 8 : Machines de Turing

#include <iostream>
#include <string>
#include <vector>
#include <array>
using namespace std;
 
// ----------------------------------------------------------------------
// STRUCTURES DE DONNEES
// ----------------------------------------------------------------------
typedef string Bande;
constexpr char EPSILON('e');
 
typedef int Tete;
constexpr Tete POSITION_INITIALE(0);
 
typedef unsigned int Etat;
constexpr Etat UNDEF(0);
constexpr Etat ETAT_INITIAL(1);
 
struct Transition 
{
  Etat etat        ;
  char caractere   ;
  int  deplacement ;
};
 
typedef array<Transition, 3> Ligne; // [ 0 , 1 , epsilon ]
 
typedef vector<Ligne> TableTransition;
 
struct TMachine {
  Etat  actuel;
  Bande ruban ;
  Tete  tete  ;
  TableTransition table;
};
 
// ----------------------------------------------------------------------
// PROTOYPES
// ----------------------------------------------------------------------
char lecture(Bande& b, Tete& t); // lit le caractère 'courant'
void ecriture(char c, Bande& b, Tete& t); /* remplace le caractère courant par
					   * la valeur transmise en argument */
void avance(Tete& t) {++t;} // déplace la tête de lecture vers la droite
void recule(Tete& t) {--t;} // déplace la tête de lecture vers la gauche
void initialise(Bande& b, const string& valeur) {b=valeur;}
TMachine cree(const string& entree = "");
void reinitialise(TMachine& m, const string& entree);
void lance(TMachine& m);
void lance(TMachine&& m) { lance(m); }
void affiche(const TMachine& m);
 
// ----------------------------------------------------------------------
int main()
{
  lance(cree("1101"));
  return 0;
}
 
/* ====================================================================== *
 * Simule l'aspect infini négatif en ajoutant un caractère en debut de    *
 * chaîne puis en décalant la valeur de la tête (càd la remettre à 0).    *
 * Entrée : la bande et la tête de lecture/écriture                       *
 * ====================================================================== */
void gestion_bornes(Bande& b, Tete& t) 
{
  // simulation du ruban pour les positions négatives
  while (t < 0) {
    b = EPSILON + b;
    avance(t);
  }
  // insertion des EPSILON nécessaires pour les position positives
  while (t >= b.size()) { // ici t >= 0 : comparaison OK 
    b += EPSILON;
  }
}
 
/* ====================================================================== *
 * Lit le caractère courant sur une bande doublement infinie.             *
 * Entrée : la bande à lire et la tête de lecture/écriture                *
 * Sortie : le caractère lu                                               *
 * ====================================================================== */
char lecture(Bande& b, Tete& t) 
{
  // gère les bornes de la bande pour assurer l'existence de la case lue
  gestion_bornes(b, t);
 
  // lecture du caractère
  return b[t];
}
 
/* ====================================================================== *
 * Écriture d'un caractère sur une bande doublement infinie :             *
 * remplace le caractère courant par la valeur transmise en argument.     *
 * Entrée : le caractère à écrire, la bande et la tête de lecture/écriture*
 * ====================================================================== */
void ecriture(char c, Bande& b, Tete& t)
{
  // gère les bornes de la bande pour assurer l'existence de la case écrite
  gestion_bornes(b, t);
 
  // écriture du caractère
  b[t] = c;
}
 
/* ====================================================================== *
 * Crée une machine de Turing : charge sa table de transition et          *
 * initialise la machine.                                                 *
 * ====================================================================== */
TMachine cree(const string& entree)
{
  TMachine m;
   m.table = {
    // table pour le calcul de parité (g++-4.6)
     {{ {    1,     '0',  1}, {    1,     '1',  1}, {    2, EPSILON, -1} }}, 
     {{ {    3, EPSILON, -1}, {    4, EPSILON, -1}, {UNDEF, EPSILON, -1} }}, 
     {{ {    3, EPSILON, -1}, {    3, EPSILON, -1}, {    5,     '1',  1} }}, 
     {{ {    4, EPSILON, -1}, {    4, EPSILON, -1}, {    5,     '0',  1} }}, 
     {{ {UNDEF, EPSILON, -1}, {UNDEF, EPSILON, -1}, {UNDEF, EPSILON, -1} }}  
 
    /*
    // table pour le calcul de parité (normalement)
    { {    1,     '0',  1}, {    1,     '1',  1}, {    2, EPSILON, -1} }, 
    { {    3, EPSILON, -1}, {    4, EPSILON, -1}, {UNDEF, EPSILON, -1} }, 
    { {    3, EPSILON, -1}, {    3, EPSILON, -1}, {    5,     '1',  1} }, 
    { {    4, EPSILON, -1}, {    4, EPSILON, -1}, {    5,     '0',  1} }, 
    { {UNDEF, EPSILON, -1}, {UNDEF, EPSILON, -1}, {UNDEF, EPSILON, -1} }  
    */
 
    /*
    // table pour l'inversion de bits
    { {    2,     '0',  1}, {    3,     '1',  1}, {UNDEF, EPSILON,  1} },
    { {    2,     '0',  1}, {    3,     '1',  1}, {    4, EPSILON,  1} },
    { {    2,     '0',  1}, {    3,     '1',  1}, {    5, EPSILON,  1} },
    { {    4,     '0',  1}, {    4,     '1',  1}, {    6,     '0', -1} },
    { {    5,     '0',  1}, {    5,     '1',  1}, {    6,     '1', -1} },
    { {    6,     '0', -1}, {    6,     '1', -1}, {    7, EPSILON, -1} },
    { {    8, EPSILON, -1}, {    8, EPSILON, -1}, {    7, EPSILON, -1} },
    { {   10,     '0',  1}, {   11,     '1',  1}, {    9, EPSILON,  1} },
    { {UNDEF,     '0', -1}, {UNDEF,     '1', -1}, {    9, EPSILON,  1} },
    { {    4,     '0',  1}, {    4,     '1',  1}, {   10, EPSILON,  1} },
    { {    5,     '0',  1}, {    5,     '1',  1}, {   11, EPSILON,  1} }
    */
 
  };
 
  reinitialise(m, entree);
 
  return m;
}
 
/* ====================================================================== *
 * Réinitialise une machine et écrit les données d'entrée sur la bande.   *
 * Entrée : la machine à initialiser et les données d'entrée              *
 * ====================================================================== */
void reinitialise(TMachine& m, const string& entree)
{
  m.actuel = ETAT_INITIAL;
  initialise(m.ruban, entree);
  m.tete = POSITION_INITIALE;
}
 
/* ====================================================================== *
 * Lance l'execution d'une machine de Turing                              *
 * Entrée : la machine à simuler                                          *
 * ====================================================================== */
void lance(TMachine& m)
{
  cout << "Lancement de la machine de Turing" << endl;
  affiche(m);
 
  while (m.actuel != UNDEF) {
    // Cherche la position à lire dans la table de transition
 
    //      indice de ligne
    int i(m.actuel-1);
 
    //      indice de colonne (caractère lu)
    cout << " lu : " << lecture(m.ruban, m.tete) << ",";
    int j;
    switch (lecture(m.ruban, m.tete)) {
    case '0':     j = 0; break;
    case '1':     j = 1; break;
    case EPSILON: j = 2; break;
    default:
      cout << "ERREUR: j'ai lu un caractère inconnu: " 
	   << lecture(m.ruban, m.tete) << endl;
      affiche(m);
      return;
    };
 
    // mise à jour de la machine
    m.actuel = m.table[i][j].etat;
    cout << " nouvel état : " << m.actuel << ",";
 
    ecriture(m.table[i][j].caractere, m.ruban, m.tete);
    cout << " écrit : " << m.table[i][j].caractere << ",";
 
    switch(m.table[i][j].deplacement) {
    case  1: avance(m.tete); cout << " avance" << endl; break;
    case -1: recule(m.tete); cout << " recule" << endl; break;
    default:
      cout << "ERREUR: déplacement inconnu: " 
	   << m.table[i][j].deplacement << endl;
      affiche(m);
      return;
    }
 
    affiche(m);
  }
 
  cout << "Arrêt de la machine de Turing." << endl;
}
 
/* ====================================================================== *
 * Affiche l'état d'une machine de Turing                                 *
 * Entrée : la machine à afficher                                         *
 * ====================================================================== */
void affiche(const TMachine& m)
{
  cout << "    --------------------------------" << endl;
  cout << "    Etat actuel : " << m.actuel << endl;
  cout << "    Bande :" << endl;
  cout << "       " << m.ruban << endl;
  cout << "      ";
  for (Tete i(-1); i < m.tete; ++i) cout << ' ';
  cout << "^ (Tete : " << m.tete << ')' <<  endl;
  cout << "    --------------------------------" << endl;
}
 

Dernière mise à jour : $Date: 2013-08-16 14:56:52 +0200 (ven 16 aoû 2013) $  ($Revision: 202 $)