dimanche 12 novembre 2017

Prendre la bonne définition du problème

Énoncé

Écrire un programme qui lit un entier et vous dit par la suite est ce que cet entier est pair ou bien impair.

Cet exercice est donné, généralement, comme pratique sur la structure conditionnelle. Le programme doit "tester si" l'entier saisi est pair ou bien impair. La présence de Si, tester, ou de "est ce que" est un indicateur sur la structure conditionnelle. Nous avons deux possibilités seulement, alors, la forme la plus simple d'un conditionnel, c'est à dire, "Si...Alors...Sinon..." peut suffire.
Mais, le problème que cet exercice pose aux nouveaux étudiants ne réside pas dans son objectif ou bien dans la forme de sa solution. Le problème est l'écriture de la condition; les mathématiciens tendent souvent à choisir la mauvaise définition.

Mauvaise solution

Un nombre entier n est dit pair si il existe un autre nombre entier k tel que :
n = 2k
Il est dit impair dans le cas contraire.

En essayant de résoudre le problème posé en exploitant cette définition, la chance de tomber dans l'erreur augmente. En effet, cette définition invoque deux entiers; n et k. Ainsi, il est logique de déclarer les deux entiers. Par la suite, nous devons vérifier la condition donnée, ce qui donne à peu près :

En Pascal :

Program PairImpair;
Var
 n, k : Integer;
Begin
 WriteLn('Donnez le nombre à vérifier :');
 ReadLn(n);
 If(n = 2 * k)Then 
  WriteLn('Pair')
 Else
  WriteLn('Impair');
End.

En Java :

import java.util.Scanner;

public class Exemple {

 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  int n, k;

  System.out.println("Donnez le nombre à vérifier :");
  n = scanner.nextInt();

  if(n == 2 * k)
   System.out.println("Pair");
  else
   System.out.println("Impair");
 
 }
}

J'ai lu cette solution des dizaines de fois. Elle semble bien réfléchie, néanmoins, elle est fausse. En effet, l'expression booléenne :
        x = 2 * k
N'est pas équivalente à l'énoncé :
il existe un k tel que x = 2 * k; 
La première compare la valeur actuelle de "x" au double de la valeur actuelle de "k". Comme vous n'avez donné aucune valeur à "k", la comparaison ne donner pas la bonne réponse. En Java, ce code ne peut même pas être compilé; le compilateur se rend compte que vous n'avez pas donné de valeur à la variable "k". En Pascal, il vous donne juste un avertissement.



Une traduction plus fiable de l'énnoncé : s'il existe un k tel que x = 2 * k sera probablement :

En Pascal :

Program PairImpair;
Var
 n, k : Integer;
 trouve : Boolean;
Begin
 WriteLn('Donnez le nombre à vérifier :');
 ReadLn(n);
 trouve := false;
 k := 0;
 While ((k <= n) and not(trouve)) Do
 Begin
  If(n = 2 * k)Then 
   trouve := true
  Else
   k := k + 1;
 End;
 If(trouve)Then
  WriteLn('Pair')
 Else
  WriteLn('Impair');
End.

En Java :

import java.util.Scanner;

public class Exemple {

 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  int n, k;
  boolean trouve = false;
  System.out.println("Donnez le nombre à vérifier :");
  n = scanner.nextInt();
  k = 0;
  while(k <= n && !trouve) {
   if(n == 2 * k)
    trouve = true;
   else
    k++;
  }
  if(trouve)
   System.out.println("Pair");
  else
   System.out.println("Impair");
 }
}

Effectivement, si vous voulez que le programme vérifie est ce qu'il existe une valeur de k ou non, alors il faut le faire explicitement. Dans ce code, il faut ajouter toute une boucle pour vérifier les valeurs de 0 à n pour voir s'il existe un entier k qui vérifie l'égalité n == 2 * k. Le résultat de la vérification est mis dans une variable booléenne.

Meilleure solution

C'était un long code pour un problème assez simple. Peut être il fallait prendre la définition la plus simple. Par exemple, un nombre pair est un nombre divisible par 2; autrement dit, le reste de sa division sur 2 est nul. En prenant cette deuxième définition, il nous suffit de :
  1. Calculer le reste de division,
  2. Le comparer avec 0 : s'il l'égale alors le nombre est pair, sinon, il est impair.

En Pascal :

Program PairEtImpair;
Var
 n : Integer;
Begin
 WriteLn('Donnez le nombre à tester :');
 ReadLn(n);
 If (n mod 2 = 0) Then
  WriteLn('Pair')
 Else
  WriteLn('Impair');
End.

En Java :

import java.util.Scanner;

public class Exemple {

 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  System.out.println("Donnez le nombre à tester :");
  int n = scanner.nextInt();
  if(n % 2 == 0)
   System.out.println("Pair");
  else
   System.out.println("Impair");
 }
}

On peut aller un peu plus loin et écrire en Java :


import java.util.Scanner;

public class Exemple {

 public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  System.out.println("Donnez le nombre à tester :");
  System.out.println(scanner.nextInt() % 2 == 0 ? "Pair" : "Impair");
 }
}

A la fin 

Dans cet article, je voulais mettre le doigt sur un fait courant pendant la programmation; des fois le problème réside dans l'angle avec lequel nous voyons le problème. Il n'y a pas une méthode que vous garantit que vous avez choisi la bonne approche, la bonne méthode ou la bonne définition. Seule l'expérience vous donnera la réponse. Néanmoins, si vous arrivez à traduire parfaitement la modélisation mathématique en un algorithme, il sera correct même s'il n'est pas le plus optimal.

Aucun commentaire: