Anagrammes

 

Supposons qu'on veuille faire un programme qui affiche les combinaisons d'une chaîne de caractères; par exemple les anagrammes des lettres de "abc" sont "abc, acb, bac, bca, cab, cba".

Pour réaliser ce programme nous allons commencer par des petites chaînes, et nous allons d'abord le faire de manière itérative.

Pour "a", toutes les combinaisons sont : "a".

Pour "ab", toutes les combinaisons sont : "ab,ba".

On remarque que ces combinaisons ne sont en fait qu'une rotation de la chaîne initiale vers la gauche, et ceci 2 fois de suite.

On peut donc en déduire l'algorithme général, qui ne sera constitué que de rotations successives vers la gauche.

Pour "abc", toutes les combinaisons sont :

   première rotation  : "bca" suivi d'une rotation de "ca" donc "bac"

                              suivi de 2 rotations de "ca" donc "bca"

   deuxième rotation  : "cab" suivi d'une rotation de "ab" donc "cba"

                              suivi de 2 rotations de "ca" donc "cab"

   troisième rotation : "abc" suivi d'une rotation de "bc" donc "acb"

                              suivi de 2 rotations de "bc" donc "abc"

Nous allons écrire la procédure qui affiche les combinaisons des lettres d'une chaîne de 3 caractères.

 

procedure combinaison(st: string);
var i1, i2, i3: integer;
  tete1, tete2, reste1, reste2: string;
begin
  for i1 := 1 to 3 do
  begin
    tete1 := st[1];
    reste1 := copy(st, 2, length(st) - 1);
    st := reste1;
    for i2 := 1 to 2 do
    begin
      tete2 := st[1];
      reste2 := copy(st, 2, length(st) - 1);
      st := reste2;
      for i3 := 1 to 1 do
        memo1.lines.add(tete1 + tete2 + st); { on affiche les têtes successives }
      st := reste2 + tete2; { ici on fait la rotation }
    end;
    st := reste1 + tete1; { ici on fait la rotation }
  end;
end;
 

Nous allons transformer maintenant cette procédure itérative en une procédure récursive. On obtient la procédure suivante, qui est appelée avec l'instruction "combinaison('abc','');" :

 

procedure combinaison1(st, tete: string);
var i: integer;
  tete_local, reste_local: string;
begin
  if length(st) = 1 then memo1.lines.add(tete + st)
  else
    for i := 1 to length(st) do
    begin
      tete_local := st[1];
      reste_local := copy(st, 2, length(st) - 1);
      combinaison1(reste_local, tete + tete_local);
      st := reste_local + tete_local;
    end;
end;
 

Maintenant on peut supprimer les variables locales qui sont "tete_local" et "reste_local" et on obtient :

 

procedure combinaison2(st, tete: string);
var i: integer;
begin
  if length(st) = 1 then memo1.lines.add(tete + st)
  else
    for i := 1 to length(st) do
    begin
      combinaison1(copy(st, 2, length(st) - 1), tete + st[1]);
      st := copy(st, 2, length(st) - 1) + st[1];
    end;
end;
 

Une autre solution à ce problème, et qui appartient à mon ancien professeur d'informatique de lycée, est la suivante :

on prend un mot et on permute sa première lettre avec chacune des autres lettres. Ensuite pour chacun des nouveaux mots obtenus, on ne touche pas à la première lettre et à partir de la deuxième lettre on refait les permutations entre la deuxième lettre et toutes les autres lettres jusqu'à la fin; ensuite pour chacun des nouveaux mots obtenus, on ne touche pas à la deuxième lettre et à partir de la troisième lettre on refait les permutations entre la troisième lettre et toutes les autres lettres jusqu'à la fin; on voit donc la récursivité :

 

procedure combinaisons;
var ch: string;
  l: byte;
 
  procedure EchangeCar(var ch: string; i, j: byte);
  var car: char;
  begin
    if i <> j then
    begin
      car := ch[i];
      ch[i] := ch[j];
      ch[j] := car;
    end
  end;
 
  procedure anagram(ch: string; i: byte);
  var j: byte;
  begin
    if i = l then memo1.lines.add(ch)
    else
      for j := i to l do
      begin
        EchangeCar(ch, i, j);
        Anagram(ch, i + 1);
        EchangeCar(ch, i, j);
      end;
  end;
 
begin
  ch := 'abc'; l := length(ch);
  anagram(ch, 1);
end;

 

Télécharger le code source Delphi