IF 121 : "Informatique Fondamentale"

Corrigé du partiel du 25 novembre 2000

Sommaire


Organisation

Objectifs

Connaissance élémentaire des ordinateurs
Apprentissage de la programmation impérative
(sauf pointeurs, récursivité)
Initiation à l'algorithmique et à la conception de programmes

Organisation du cours et des TD

Cours : 5/10, 12/10, 19/10, 26/10, 9/11, 23/11, 7/12
TD/TP : 13 semaines

Contrôle des connaissances

Pa Partiel le samedi 25 novembre à 13h30.
Ej Examen en janvier
Es Examen de septembre

CC 4 tests de 30 minutes effectués pendant les TP.
Chaque test est noté sur 5
La présence aux tests est obligatoire

Ecrit : NE = max (Ej, (Pa+Ej) / 2)

Note finale janvier : (2NE + CC) / 3

Note finale septembre : max(Es, (2Es + CC) / 3)


Bibliographie

Programmation en C
Méthodologie de la programmation en langage C, J.-P. Braquelaire,
Masson, 1994. ISBN 2-225-84353-8
Programmation en C++
Le langage C++, B. Stroustrup,
Addison-Wesley, 1992. ISBN 2-87908-013-4

L'essentiel du C++, S.B. Lippman,
Addison-Wesley, 1992. ISBN 2-87908-002-9

Et aussi
Concepts fondamentaux de l’informatique, A. Aho, J. Ullman,
Dunod, 1998. ISBN 2-10-003127-9

MiniC et C--

Pour générer la version restreeinte de C++ utilisée pour ce cours, vous devez procéder comme suit :
flex miniC.l
bison -d miniC.y
gcc lex.yy.c miniC.tab.c -o miniC
Ensuite, il suffit de compiler les programmes avec la commande
g-- programme.C


Introduction

Informatique = Science de l'information
Computer science = science de l'ordinateur
En fait, traitement automatisé de l'information.

Conception d'un programme

Modélisation et abstraction du problème
Algorithmique : élaboration d'une solution automatisée
Choix des structures de données
Conception des algorithmes
Programmation :
traduction dans un langage compréhensible par l'ordinateur des structures de données et algorithmes
Pour concevoir une solution informatique, il faut savoir ce que l'on peut faire avec un ordinateur.

Présentation des ordinateurs : Modèle de von Neumann

BUS : véhicule les données
CPU : Central Processing Unit
    UC : Unité de contrôle
    UAL : Unité arithmétique et logique
    Registres
Mémoire cache
Mémoire principale : rapide, volatile, stocke le programme et les données
Mémoire secondaire : plus lente, permanente, grande capacité : Disque dur, CDROM, DVD, bandes, ...
Périphériques d'E/S : Clavier, souris, scanner, écran, imprimante, modem, réseau

Quelques unités

Bit, octet, mot, Ko, Mo, Go, To
MHz

Programmation : les différents niveaux d'abstraction.

Matériel : logique numérique, microprogrammes
Langage machine : ajouter 2 entiers, déplacer une donnée, ...
Langage assembleur : représentation symbolique des instructions et données
Langage de programmation de haut niveau : C, C++, Fortran, Java, Lisp, ...
Générateurs d'applications

Système d'exploitation (unix, windows, macOS, ...)

Gère les ressources de l'ordinateur (mémoire, périphériques, cpu, ...)
langage de commandes : shell, interface graphique

Cycle du programme :

Édition
Compilation
Exécution

Exemples de programmes :

Hello world!
PGCD
Puissance
Sierpinski

Pseudo langage : Pourquoi ?

pas de contraintes syntaxiques
niveau d'abstraction adapté aux problèmes et aux connaissances
lire les données
trier les données
afficher les données triées


Structure d'un programme

déclarations de constantes

Pseudo :
CONSTANTE Max=100
C++ :
const int Max=100;

déclarations de types

Pseudo :
TYPE polynôme = tableau de Max réels
C++ :
TYPEDEF float polynome[Max];

déclarations de procédures et fonctions

Pseudo :
FONCTION nomFonction (liste de paramètres) : typeRésultat
bloc

PROCEDURE nomProcédure ( liste de paramètres )
bloc

C++ :
nomType nomFonction ( liste de paramètres )
bloc

déclaration du programme principal

Pseudo :
PROGRAMME nomProgramme
bloc
C++ :
void main ()
bloc

bloc

Pseudo :
DEBUT nom Fonction/Procédure/Programme
déclarations de constantes
déclarations de variables
liste d'instructions
FIN nom Fonction/Procédure/Programme
C++ :
{ // nom Fonction/Procédure/Programme
déclarations de constantes
déclarations de variables
liste d'instructions
} // nom Fonction/Procédure/Programme


Modèle ou Type de données

On utilise des modèles de données pour représenter l'information manipulée en informatique.
Ils sont définis par

Exemple 1 : Polynômes

Valeurs : R(X). P(X)=X2-5X+6

Opérations :

Exemple 2 : Ensembles d'entiers

Valeurs : P(N). {0,2,4,6,…}

Opérations :

Chaque langage de programmation propose des types de base (prédéfinis) et des moyens pour construire des types plus élaborés.

En machine, les données sont codées par une suite de bits et l'ensemble de valeurs est fini


Types de base

(Syntaxe C++ entre parenthèses)

Booléens (bool)

Valeurs : vrai (true), faux (false)

Opérations :

  • lecture, écriture
  • et (&&), ou (||), implique, équivaut (==), …
  • non (!)
  • Utilisation des tables de vérité pour évaluer les expressions booléennes.

    Exemple :


    Entiers (short, int, long)

    Valeurs : N

    Opérations :

    Remarque : Les opérations peuvent faire intervenir plusieurs types.

    Comparaisons : NxN->Booléens

    Littéraux (comment écrire les constantes) :

    En C++ on peut aussi utiliser la base 8 ou la base 16. En C++ l'ensemble de valeurs est un intervalle d'entiers

    L'intervalle dépend du nombre de bits utilisés pour coder un entier.

    [-215, 215-1]= [-32768,32767]
    [-231, 231-1]= [-2147483648,2147483647]

    Il y a aussi des entiers non signés

    Les calculs sur les entiers sont exacts si on ne dépasse pas la capacité.


    Réels (float, double, long double)

    Valeurs : R

    Opérations :

    Littéraux :

    Notation virgule flottante : 3.14159 1000. 0.0 -.125

    Notation exponentielle : 2.31e+5 1E10 -3.5E-3

    En C++, on ne représente qu'un intervalle de réels et avec une certaine précision.

  • float : simple précision
  • double : double précision
  • long double : réels longs
  • Standard IEEE simple précision sur 32 bits :
  • Signe 1 bit
  • Mantisse 23 bits
  • Exposant 8 bits
  • Les calculs sur les réels ne sont pas exacts car la précision est limitée.

    3 * (1 / 3) - 1 ? 0 !


    Caractères (char)

    Valeurs : ASCII

    Opérations :

    Littéraux :le caractère entre quotes

    'a','b',…,'A','B',…,'1','2',…,'!','?',';',…

    séquences d'échappement pour les caractères spéciaux
     
    \n newline \\ backslash \b backspace
    \r retour chariot \? ? \f form feed
    \t tabulation \' ' \a bell
    \v tab. verticale \" "    


    Chaînes de caractères (string)

    Valeurs : suites de caractères

    Opérations :

    Littéraux :la suite de caractères entre doubles quotes

    "Bonjour" + " " + "tout le monde."
    "Ligne 1\nLigne 2"

    En C++, la longueur des chaînes est en général limitée.

    Codage "Pascal" : longueur sur 8 bits suivie de la chaîne
    Codage "C" : chaîne suivie d'un caractère spécial de terminaison (\0).


    Identificateurs

    Noms utilisés pour désigner les objets (constantes, types, variables, fonctions, …) dans les programmes. Exemples : i, tab, x1, x2, RayonMax, rayon_max, …

    Il y a des identificateurs réservés : int, float, if, while, …

    Il faut choisir soigneusement les identificateurs.


    Constantes

    Utilisation d'un nom symbolique pour désigner une valeur constante Il faut donner le nom, le type et la valeur.

    Pseudo

    CONSTANTES
        Max = 1000 : entier
        Pi = 3.14159 : réel
        Tab = '\t' : caractère

    C++

        const int Max=1000;
        const double Pi=3.14159;
        const char Tab='\t';


    Variables

    L'information est mémorisée Déclaration de variables

    Pseudo

    VARIABLES
        n : entier
        x : réel
        tab : caractère
        i,j,k : entiers
        trouvé : booléen

    C++

        int n;
        float x;
        char tab;
        int i,j,k;
        bool trouve;

    Le compilateur alloue des zones mémoire et mémorise les triplets (nom,type,adresse).

    On peut initialiser une variable lors de sa déclaration.

    Pseudo

    VARIABLES
        n : entier initialisé à 100
        tab <- '\t' : caractère
        i <- 0,j,k : entiers
        trouvé <- true : booléen

    C++

        int n=100;
        char tab='\t';
        int i=1,j,k;
        bool trouve=true;


    Affectation

    Pseudo : nomVariable <- expression

    C++ : nomVariable = expression

    L'expression doit être compatible avec le type de la variable.

    x <- 1.25                     x = 1.25;
    i <- 5                        i = 5;
    j <- 3*i*i+2*i+4              j = 3*i*i+2*i+4;
    x <- sin(Pi/5)                x = sin(Pi/5);
    x <- (1+racine(5))/2          x = (1+sqrt(5))/2;
    trouve <- (i<j) et (i>0)      trouve = (i<j) && (i>0);

    Attention à la priorité des opérateurs.

    On utilise des parenthèses pour

    Entrées / sorties

    Pseudo :

        LIRE nomVariable
        ECRIRE expression

    C++ :

        cin >> nomVariable
        cout << expression

    Pseudo

        LIRE i,x,k
        ECRIRE "P(",i,") = ",3*i*i+2*i+4

    C++

        cin >> i >> x >> k;
        cout << "P(" << i << ") = " << 3*i*i+2*i+4 << endl;


    Composition séquentielle

    La liste d'instructions d'un bloc est exécutée séquentiellement.

    Certaines instructions modifient cet enchaînement séquentiel.


    Commentaires

    Nécessaires pour améliorer la lisibilité des programmes.

    void sierpinski(int a, int x, int y)
    /*
    * Dessine un triangle de Sierpinski de cote "a"
    * au point (x,y).
    *
    */
    {
        const int coteMin=4; // en dessous de cette valeur,
                            // on dessine un triangle plein.
        int i;
        if (a < coteMin)
            for (i=0;i<=a;i++) Drawline(x,y+i,x+i,y+i);
        else {
            sierpinski(a/2,x,y);
            sierpinski(a/2,x,y+a/2);
            sierpinski(a/2,x+a/2,y+a/2);
        }
    }


    Instructions conditionnelles

    Pseudo

    si condition alors
        liste d'instructions
    finsi

    Exemple 1

    programme valeur_absolue
    variable y : réel
    début programme valeur_absolue
    lire y
    si y<0 alors y <- -y finsi
    ecrire "La valeur absolue est ", y
    fin programme valeur_absolue



    Pseudo

    si condition alors
        liste d'instructions 1
    sinon
        liste d'instructions 2
    finsi

    Exemple 2

    programme maximum
    variables x,y : réels
    début programme maximum
    lire x,y
    si x<y alors
        ecrire "Le maximum est : ", y
    sinon
        ecire "Le maximum est : ", x
    finsi
    fin programme maximum


    Pseudo

    si condition 1 alors
        liste d'instructions 1
    sinonsi condition 2 alors
        liste d'instructions 2
    ...
    sinonsi condition k alors
        liste d'instructions k
    sinon
        liste d'instructions k+1
    finsi

    Exemple 3

    programme trinôme
    variables
        a,b,c,delta : réels
    début programme trinôme
    ecrire "donner les coefficients du trinôme"
    lire a,b,c
    si a=0 alors
        ecrire "Ce n'est pas un trinôme"
    sinon
        delta <- b*b-4*a*c
        si delta < 0 alors
            ecrire "pas de racine réelle"
        sinonsi delta = 0 alors
            ecrire "une racine double : ",-b/(2*a)
        sinon
            ecrire "deux racines réelles : ",
                    (-b+racine(delta))/(2*a),
                    (-b-racine(delta))/(2*a)
        finsi
    finsi
    fin programme trinôme


    C++

    if (expression) instruction ou if (expression) instruction 1 else instruction 2

    Pour commander plusieurs instructions, il faut écrire

    if (expression) {
        liste d'instructions
    }
    ou if (expression) {
        liste d'instructions 1
    } else {
        liste d'instructions 2
    }

    Pour éviter les ambiguïtés, il est préférable d'utiliser cette forme même lorsqu'il n'y a qu'une instruction par groupe.

    Attention : Un else se rapporte au dernier if qui n’a pas encore de else.

    cin >> a;
    x=1;
    if (a>=0)
        if (a==0) x=2;
    else
        x=3;
    cout << x;
    a -1 0 1
    affichage 1 2 3

    La présentation améliore la lisibilité mais ne change pas le comportement du compilateur. Pour avoir un programme qui correspond à la présentation ci-dessus, il faut écrire :

    cin >> a;
    x=1;
    if (a>=0) {
        if (a==0) x=2;
    } else {
        x=3;
    }
    cout << x;
    a -1 0 1
    affichage 3 2 1


    Exemple 4

    #include <iostream.h>
    void main ()
    // calcule la date du lendemain
    { // programme main

    int jour, mois, annee;

    cout << "Calcul du successeur d'une date." << endl;
    cout << "Donner la date sous forme de 3 entiers : ";
    cin >> jour >> mois >> annee ;

    // on suppose que la date donnée est valide.

    if (jour==31 && mois==12) { // changement d'année
        jour=1;
        mois=1;
        annee=annee+1;
    } else if (jour==31) { // changement de mois
        jour=1;
        mois=mois+1;
    } else if (jour==30 &&
              (mois==4 || mois==6 || mois==9 || mois==11)) {
        jour=1;
        mois=mois+1;
    } else if (jour==29 && mois==2) {
        jour=1;
        mois=mois+1;
    } else if (jour==28 && mois==2 && annee % 400 != 0 &&
              (annee % 4 != 0 || annee % 100 == 0)) {
        jour=1;
        mois=mois+1;
    } else// changement de jour
        jour=jour+1;

    cout << "Le successeur de cette date est : ";
    cout << jour << "/" << mois << "/" << annee << endl;

    } // programme main

    Exécutions :

    Calcul du successeur d'une date.
    Donner la date sous forme de 3 entiers : 12 10 2000
    Le successeur de cette date est : 13/10/2000

    Calcul du successeur d'une date.
    Donner la date sous forme de 3 entiers : 31 12 1999
    Le successeur de cette date est : 1/1/2000

    Calcul du successeur d'une date.
    Donner la date sous forme de 3 entiers : 28 2 2000
    Le successeur de cette date est : 29/2/2000

    Calcul du successeur d'une date.
    Donner la date sous forme de 3 entiers : 29 2 1900
    Le successeur de cette date est : 1/3/1900

    Calcul du successeur d'une date.
    Donner la date sous forme de 3 entiers : 30 2 2000
    Le successeur de cette date est : 31/2/2000

    Calcul du successeur d'une date.
    Donner la date sous forme de 3 entiers : -5 22 43
    Le successeur de cette date est : -4/22/43

    Exemple 4 bis

    #include <iostream.h>
    void main ()
    // calcule la date du lendemain
    { // programme main

    int jour, mois, annee;

    cout << "Calcul du successeur d'une date." << endl;
    cout << "Donner la date sous forme de 3 entiers : ";
    cin >> jour >> mois >> annee ;

    // on suppose que la date donnée est valide.

    if (jour==31 && mois==12) { // changement d'année
        jour=1;
        mois=1;
        annee=annee+1;
    } else if ( // changement de mois
               (jour==31)
            || (jour==30 &&
               (mois==4 || mois==6 || mois==9 || mois==11))
            || (jour==29 && mois==2)
            || (jour==28 && mois==2 && annee % 400 != 0 &&
               (annee % 4 != 0 || annee % 100 == 0))
              ) {
        jour=1;
        mois=mois+1;
    } else // changement de jour
        jour=jour+1;

    cout << "Le successeur de cette date est : ";
    cout << jour << "/" << mois << "/" << annee << endl;

    } // programme main


    Instruction sélection (switch)

     

    Pseudo

    cas expression parmi
        cte1,1,cte1,2,... : liste d'instructions 1
        cte2,1,cte2,2,... : liste d'instructions 2
        ...
        ctek,1,ctek,2,... : liste d'instructions k
        défaut : liste d'instructions k+1
    fin cas

    Exemple :

    programme conversion
    variable
        val : entier initialisé à 0
        ch : caractère

    début programme conversion

    ecrire "conversion hexadécimal -> décimal"
    lire ch

    tant que ch != '.' faire
        cas ch parmi
            '0' : val <- 16*val+0
            '1' : val <- 16*val+1
            ...
            '9' : val <- 16*val+9
            'a','A' : val <- 16*val+10
            ...
            'f','F' : val <- 16*val+15
            défaut : ecrire "erreur : ", ch , " n'est pas un chiffre hexadecimal"
        fin cas
        lire ch
    fin tant que

    ecrire "La valeur décimale est ", val

    fin programme conversion

    C++

    switch (expression) {
        case cte: ... case cte: liste d'instructions 1 break;
        case cte: ... case cte: liste d'instructions 2 break;
        ...
        case cte: ... case cte: liste d'instructions k break;
        default : liste d'instructions k+1
    }

    Exemple :

    #include <iostream.h>

    void main ()
    // Conversion hexadécimal -> décimal
    { // programme main
    int val=0;
    char ch;

    cout << "Conversion hexadécimal -> décimal." << endl;
    cout << "Donner le nombre hexadécimal : ";
    cin >> ch;

    while (ch!='.') {
        switch (ch) {
            case '0' : val = 16*val+0; break;
            case '1' : val = 16*val+1; break;
            ...
            case '9' : val = 16*val+9; break;
            case 'a': case 'A': val = 16*val+10; break;
            ...
            case 'f': case 'F': val = 16*val+15; break;
            default: cout << "Erreur." << endl;
        } // end switch
        cin >> ch;
    } // end while

    cout << "La valeur décimale est : " << val << endl;

    } // programme main


    En pseudo, on peut aussi écrire :

    Selon que
        jour=31 et mois=12 : // changement d'année
            jour<-1
            mois<-1
            annee<-annee+1
        jour=31 et mois<12 :// changement de mois
            jour<-1
            mois<-mois+1
        jour=30 et mois dans {4,6,9,11} :
            jour<-1
            mois<-mois+1
        jour=29 et mois=2 :
            jour<-1
            mois<-mois+1
        jour=28 et mois=2 et annee non divisible par 400
        et non (annee divisible par 4 mais pas par 100) :
            jour<-1
            mois<-mois+1
        sinon// changement de jour
            jour<-jour+1
    fin selon

    Itérations


    Il y a 4 formes de boucles :

    Les 3 premières sont les itérations non bornées.
    On peut simuler toutes les boucles avec la boucle tant que.
    Les autres boucles sont introduites simplement pour faciliter l'écriture des programmes.
    Il faut prouver que les boucles terminent.
    Il faut prouver la correction des boucles.


    Boucle Tant que

    Pseudo

    tant que condition faire
        liste d'instructions
    fin tant que
    On commence par évaluer la condition.
    Si elle est vraie, on effectue la liste d'instructions.
    Puis on recommence.
    Lorsque la condition devient fausse, on sort de la boucle.

    Exemple 1 : Calcul du pgcd

    Lire n,m

    tant que m != 0 faire
        r <- n mod m
        n <- m
        m <- r
    fin tant que

    Ecrire n

    Exécution à la main pour n=42 et m=75.


    Boucle while

    C++

    while (condition) instruction

    En général, on met une liste d'instructions

    while (condition) {
        liste d'instructions
    } // end while

    Exemple 2 : Puissance.

    cin >> a >> n;
    p = 1;

    while (n > 0) {
        if (n % 2 != 0) p = p * a;
        a = a * a;
        n = n / 2;
    } // end while

    cout << p;

    Exécution à la main pour a=2 et n=10.


    Attention à la terminaison des boucles

    Exemple 3 : Puissance.

    Lire a, n
    P <- 1

    tant que n != 0 faire
        p <- p * a
        n <- n - 1
    fin tant que

    Ecrire p

    Exécution à la main pour a = 2 et n = 3.

    Exécution à la main pour a = 2 et n = -3.

    Pour prouver la terminaison d'une boucle :

  • Définir une fonction qui dépend des variables à valeurs dans N
  • Montrer qu'à chaque passage dans la boucle, cette fonction décroît strictement.
  • Montrer que la condition devient fausse en dessous d’une certaine valeur.
  • Exemple 1 : PGCD

    f(n,m,r) = m. OK si n >= 0 et m >= 0.

    Il faut donc faire précéder la boucle d'un test :

    Lire n,m

    si n >= 0 et m >= 0 alors
        tant que m != 0 faire
            r <- n mod m
            n <- m
            m <- r
        fin tant que
    finsi

    Ecrire n

    Exemple 2 : puissance

    f(a,n,p) = n. OK grâce à la condition n > 0.

    Exemple 3 : puissance

    f(a,n,p) = n. OK si n > 0. Il faut donc changer la condition.


    Invariants : preuve de correction d'une boucle.

    // Invariant : I
    tant que C faire
        liste d'instructions
    fin tant que

    Exemple 1 : PGCD

    Lire a, b

    si a < 0 ou b < 0 ou (a = 0 et b = 0) alors
        Ecrire "Erreur dans les données."
    sinon
        n <- a
        m <- b

        // Invariant : pgcd(a,b)=pgcd(n,m) et n>=0 et m>=0

        tant que m != 0 faire
        // pgcd(a,b)=pgcd(n,m) et n>=0 et m>0
            r <- n mod m
        // pgcd(a,b)=pgcd(r,m) et r>=0 et m>0
            n <- m
        // pgcd(a,b)=pgcd(r,n) et r>=0 et n>0
            m <- r
        // pgcd(a,b)=pgcd(m,n) et m>=0 et n>0
        fin tant que

        // pgcd(a,b)=pgcd(n,m) et n>=0 et m=0
        // Donc, n=pgcd(a,b)

        Ecrire n
    finsi

    Exemple 2 : puissance

    Lire a, n

    si n < 0 alors
        Ecrire "Erreur dans les données."
    sinon
        p <- 1

        // On note b et m les valeurs initiales de a et n
        // Invariant : b^m = p*a^n et n>=0
        tant que n > 0 faire
        // b^m = p*a^n et n>0
            si n est impair alors
            // b^m = p*a^n et n=2k+1>0
                p <- p * a
            // b^m = p*a^(2k) et n=2k+1>0
            sinon
            // b^m = p*a^(2k) et n=2k>0
            finsi
        // b^m = p*a^(2k) et n>0 et k=n div 2
            a <- a * a
        // b^m = p*a^k et n>0 et k=n div 2
            n <- n div 2
        // b^m = p*a^n et n>=0
        fin tant que

        // b^m = p*a^n et n>=0 et n<=0
        // Donc b^m = p

        Ecrire p
    finsi


    Boucle Faire

    Pseudo

    faire
        liste d'instructions
    tant que condition
    On commence par effectuer la liste d'instructions.
    Puis on évalue la condition.
    Si elle est vraie, on recommence sinon on sort de la boucle.

    Cela équivaut à

    liste d'instructions
    tant que condition faire
        liste d'instructions
    fin tant que

    C++

    do {
        Liste d'instructions
    } while (condition)

    Exemple :

    Faire
        Lecture de deux entiers
        Calcul et affichage du pgcd
        Ecrire "Voulez-vous recommencer (O/N) ?"
        Lire réponse
    Tant que réponse = 'O'

    Terminaison :

    On procède comme pour la boucle tant que.
    Remarque : la boucle ci-dessus ne termine pas forcément.

    Correction :

    On se ramène à une boucle tant que et on utilise la méthode des invariants. Il faut donc


    Boucle Répéter

    Pseudo

    répéter
        liste d'instructions
    jusqu'à condition
    On commence par effectuer la liste d'instructions.
    Puis on évalue la condition.
    Si elle est vraie, on sort de la boucle sinon on recommence.

    Cela équivaut à

    liste d'instructions
    tant que non condition faire
        liste d'instructions
    fin tant que

    C++

    do {
        Liste d'instructions
    } while (! condition)

    Exemple :

    répéter
        écrire "Donner deux entiers positifs non tous deux nuls"
        lire m,n
    jusqu'à (m>0 et n>=0) ou (m>=0 et n>0)

    Terminaison :

    On procède comme pour la boucle tant que.
    Remarque : la boucle ci-dessus ne termine pas forcément.

    Correction :

    On se ramène à une boucle tant que et on utilise la méthode des invariants. Il faut donc


    Boucle Pour

    Pseudo

    Pour variable <- expression1 à expression2 faire
        liste d'instructions
    fin pour

    Cela équivaut à

    variable <- expression1
    tant variable <= expression2 faire
        liste d'instructions
        variable <- variable +1
    fin tant que

    Remarques :

    1 + expression2 - expression1

    Correction :

    On se ramène à une boucle tant que et on utilise la méthode des invariants. Il faut donc Remarque : Si exp1 <= exp2 alors à la sortie de la boucle on a vc = exp2+1.

    Exemple 1 : S = 1*1 + 2*2 + … + n*n

    variables i,s,n : entiers

    écrire "Donner un entier positif"
    répéter lire n jusqu'à n >= 0
    s <- 0
    // Inv : S = 1*1 + … + (i-1)*(i-1) et i <= n+1
    pour i <- 1 à n faire
        s <- s + i*i
    fin pour
    afficher s

    Exemple 2 : Fibonacci

    variables i,n,a,b : entiers

    écrire "Donner un entier positif"
    répéter lire n jusqu'à n >= 0
    a <- 1; b <- 1
    // Inv : a = F(i) et b = F(i-1) et 1 <= i <= n
    pour i <- 1 à n-1 faire
        a <- a + b
        b <- a - b
    fin pour
    afficher a

    C++

    for (affectation;condition;incrémentation) {
        Liste d'instructions
    }

    Cela équivaut à

    affectation;
    while (condition) {
        liste d'instructions
        incrémentation
    }

    C--

    La terminaison n'est assurée que s'il s'agit bien d'une boucle pour :

    Exemple :

    cin >> n;
    fact = 1;
    // Inv : fact = (i-1)! et i <= n+1
    for (i=2; i<=n; i++)
        fact = fact*i;
    cout << fact;

    a=1; b=1;
    // Inv : a = F(i) et b = F(i-1) et 1 <= i <= n
    for (i=1; i<n; i++) {
        a = a+b;
        b = a-b;
    }
    cout << a;


    Tableaux

    Plan :

    Recherche dichotomique dans un tableau trié

    i <- 1; j <- N

    // Inv : 1 <= i <= j <= N et t trié et
    // x dans t si et seulement si x dans t[i..j]
    tant que i < j faire
        // 1 <= i < j <= N et t trié et ...
        m <- (i+j) div 2
        // 1 <= i <= m < j <= N et t trié et ...
        si x > t[m] alors
        // 1 <= i <= m < j <= N et t trié et
        // x dans t si et seulement si x dans t[m+1..j]
            i <- m+1
        // Inv
        sinon
        // 1 <= i <= m < j <= N et t trié et
        // x dans t si et seulement si x dans t[i..m]
            j <- m
        // Inv
        finsi
        // Inv
    fin tant que
    // Inv et i >= j
    // donc : 1 <= i = j <= N et t trié et
    // x dans t si et seulement si x = t[i]
    si x = t[i] alors
        afficher x, " se trouve en position ", i
    sinon
        afficher x, " ne se trouve pas dans le tableau"
    finsi

    Terminaison : j-i >= 0 diminue strictement à chaque itération


    Procédures et fonctions

    Introduction

    Les utilisations les plus courantes des procédures et fonctions sont L'un des principes de base de la conception de gros logiciels est
    Diviser pour régner
    Pour cela, une procédure ou fonction doit être Comment y parvenir ? Exemples d'entêtes : fonction pgcd(valeur a,b:entier):entier
    // Retourne le pgcd de deux entiers a et b
    // qui doivent être positifs et non tous deux nuls

    procédure échange(référence x,y:réel)
    // Echange les valeurs de x et y


    Bloc d'une procédure ou fonction

    Comme le bloc du programme, il consiste en Dans les instructions on peut utiliser les paramètres, les objets locaux et les constantes globales, mais pas les variables locales du programme ou des autres procédures et fonctions.

    Dans une fonction, il doit y avoir une instruction

    Retourner expression qui termine l'exécution de la fonction et définit la valeur du résultat.


    Procédure ou fonction ?

    Une procédure est utilisée comme une instruction si x < y alors échange(x,y) finsi

    lireTab(t,1,N)
    trierTab(t,1,N)
    écrireTab(t,1,N)

    Une fonction est utilisée comme un facteur dans une expression. m <- a * b / pgcd(a,b)

    Exemple : PGCD

    fonction pgcd(valeur a,b:entier):entier
    // Retourne le pgcd de deux entiers a et b
    // qui doivent être positifs et non tous deux nuls
    variable c:entier

    début pgcd
    tant que b ? 0 faire
        c <- a Mod b
        a <- b
        b <- c
    fin tant que
    retourner a
    fin pgcd

    Exemples d'utilisations

    n <- 48; m <- 21; p <- pgcd(n,m)
    afficher n, m, p

    p <- pgcd(2*n+1,3*m-2)
    afficher n, m, p

    a <- 48; b <- 21; p <- a/pgcd(a,b); q <- b/pgcd(a,b)
    afficher "fraction irréductible : ", p, q

    lire n,m
    si pgcd(n,m)=1 alors
        afficher n, " et ", m, " sont premiers entre eux"
    fsi

    Exemple : Echange

    procédure échange(référence x,y:réel)
    // Echange les valeurs de x et y
    variable z:réel

    début échange
    z <- x; x <- y; y <- z
    fin échange

    Exemples d'utilisations

    a <- 48; b <- 21; z <- 12; échange(a,b)
    afficher a, b, z

    a <- 48; x <- 21; z <- 12; échange(a,x)
    afficher a, x, z

    x <- 48; y <- 21; z <- 12;
    échange(2*x,3*y) // Erreur


    Paramètre formel ou effectif ?

    Le paramètre formel est celui utilisé dans l'entête de la procédure ou fonction. Le paramètre formel est celui donné au moment de l'appel de la procédure ou fonction.

    Variable locale

    Une variable locale est allouée à chaque activation de la procédure ou fonction et elle est désallouée lorsque la procédure ou fonction termine.

    Paramètre valeur

    Paramètre référence

    Le paramètre formel est un alias du paramètre effectif.
    Il n'y a pas création d'une variable et on travaille directement sur le paramètre effectif.
    Le paramètre effectif peut être modifié.
    Le paramètre effectif doit être une variable de même type que le paramètre formel.


    Portée, masquage

    constante n = 3

    procédure P(valeur x:entier, référence y:entier)
    variables
        z:entier
        n: réels // La variable n masque la constante n

    début P
    y <- 3*x+2
    z <- 2
    n <- 0.3 // OK. Ici n est une variable réelle
    b <- 8 // Erreur : b n'existe pas ici
    fin P // fin de la portée de x,y,z,n

    programme Q
    variables
        a,b : entiers
        x : réel

    début Q
    a <- n // il s'agit de la constante n
    y <- 8 // erreur : la portée de y et z
    z <- 2*a+b // se limite à la procédure P
    x <- 0.5 // OK. C'est le x de Q
    fin Q


    Paramètre valeur ou référence : exemple

    procédure P(valeur x:entier, référence y:entier)
    variable z:entier

    début P
    z <- y div 2; y <- x*x; x <- y*y + z*z
    fin P

    programme Test
    variables a,b,x,y,z : entiers

    début Test
    a <- 3; b <- 0; x <- 5; y <- -2; z <- 8
    P(a,b)
    afficher a, b, x, y, z

    a <- 4; P(a,a)
    afficher a, b, x, y, z

    P(x,y)
    afficher x, y, z

    x <- 2; z <- 3; P(x,y)
    afficher x, y, z
    fin Test


    Exemple : Tri sélection en pseudo

    constante N=50
    type Tab = tableau de N réels

    procédure échange(référence x,y:réel)
    // Echange les valeurs de x et y
    variable z:réel

    début échange
    z <- x; x <- y; y <- z
    fin échange

    fonction posmin(valeur t:Tab, valeur i,j:entier):entier
    // Retourne la position du minimum de t[i..j]
    // On doit avoir 1 <= i <= j <= dimension(t)
    variable k:entier

    début posmin
    // Invariant : t[i] <= t[i_0..k-1] et k <= j+1
    // i_0 dénote la valeur initiale de i
    pour k <- i+1 à j faire
        si t[k] < t[i] alors i <- k fsi
    fin pour
    retourner i
    fin posmin

    procédure tri(référence t:Tab, valeur i,j:entier)
    // tri de t[i..j]
    // On doit avoir 1 <= i <= j <= dimension(t)
    variable k:entier

    début tri
    // Invariant : t[i..k-1] est trié et
    // t[1..k-1] <= t[k..j] et i <= k <= j
    pour k <- i à j-1 faire
        échange(t[k],t[posmin(t,k,j)])
    fin tri

    programme tri_sélection
    variable a:Tab

    début tri_sélection
    lire a
    tri(a,1,N)
    afficher a
    fin tri_sélection


    Procédures et fonctions en C++

    Déclaration d'une fonction : nom_type_résultat nom_fonction (liste de paramètres)
    bloc de la fonction
    Déclaration d'une procédure : void nom_procédure (liste de paramètres)
    bloc de la fonction
    Déclaration d'un paramètre valeur : nom_type nom_paramètre Déclaration d'un paramètre référence : nom_type &nom_paramètre

    Cas particulier des paramètres tableaux

    Un tableau ne peut pas être transmis par valeur en C++

    La déclaration de paramètre
        Tab t
    correspond à une transmission par référence.

    Ne pas utiliser la déclaration
        Tab &t

    Pour interdire la modification d'un paramètre tableau par une procédure ou une fonction on utilise la déclaration de paramètre
        const Tab t
    Il s'agit toujours d'une transmission par référence (pas de copie) mais le compilateur interdit toute modification du paramètre.

    Remarque : on peut aussi utiliser la déclaration const pour un paramètre d'un autre type.
        const double &x
    Le paramètre est transmis par référence (pas de copie) mais ne peut pas être modifié.
    Ceci est utile pour éviter de copier un gros paramètre (e.g. une structure) qui ne doit pas être modifié.


    Exemple : Tri sélection en C++

    #include <iostream.h>
    const int N=50;
    typedef float Tab[N+1];

    void echange(float &x, float &y)
    // Echange les valeurs de x et y
    { // echange
    float z;
    z = x; x = y; y = z;
    } // echange

    int posmin(const Tab t, int i, int j)
    // Retourne la position du minimum de t[i..j]
    { // posmin
    int k;
    for(k=i+1;k<=j;k++)
        if (t[k] < t[i]) i <- k;
    return i;
    } //posmin

    void tri(Tab t, int i, int j)
    // tri de t[i..j]
    { // tri
    int k;
    for(k=i;k<j;k++)
        echange(t[k],t[posmin(t,k,j)]);
    } // tri

    void lireTab(Tab t, int i, int j)
    // lecture de t[i..j]
    { // lireTab
    int k;
    for(k=i;k<=j;k++)
        cin >> t[k];
    } // lireTab

    void ecrireTab(const Tab t, int i, int j)
    // écriture de t[i..j]
    { // ecrireTab
    int k;
    for(k=i;k<=j;k++)
        cout << t[k];
    } // ecrireTab

    void main()
    { // tri_sélection
    Tab a;

    lireTab(a,1,N);
    tri(a,1,N);
    ecrireTab(a,1,N);
    } // tri_sélection


    Structures

    But : regrouper sous un même nom plusieurs variables qui ne sont pas nécessairement du même type. Types
        Tcomplexe = structure
            re, im : réels
        fin structure

        Tdate = structure
            jour : entier
            mois : chaîne de caractères
            année : entier
        fin structure

        Tfiche = structure
            nom, prénom : chaîne de caractères
            né_le : Tdate
            adresse : chaîne de caractères
        fin structure

        TabCoef = tableau de 100 réels

        Tpolynôme = structure
            degré : entier
            coef : TabCoef
        fin structure

    Exemple d'utilisation

    Programme test

    variables
        x,y : Tcomplexe
        date : Tdate
        fiche : Tfiche
        p : Tpolynôme

    début test
    lire x.re, x.im, y.re, y.im
    afficher "somme : ", x.re + y.re, x.im + y.im

    lire date.jour, date.mois
    si date.jour>30 et date.mois = "novembre" alors
        afficher "date erronée"
    finsi

    lire fiche.nom, fiche.né_le.jour

    répéter lire p.degré jusqu'à 0 <= p.degré <= 100

    pour i <- 1 à p.degré faire
        lire p.coef[i]
    finpour
    fin test


    Structures en C++

    #include <iostream.h>
    #include <string>
    #include <iomanip.h>

    typedef struct {
        float re, im;
    } Tcomplexe;

    typedef struct {
        int jour;
        string mois;
        int annee;
    } Tdate;

    typedef struct {
        string nom, prenom;
        Tdate ne_le;
        string adresse;
    } Tfiche;

    typedef float TabCoef[100];

    typedef struct {
        int degre;
        TabCoef coef;
    } Tpolynome;

    void main () {
    Tcomplexe x,y;
    Tdate date;
    Tfiche fiche = {"Frigand", "Elyse",
                    {20,"mai",1970},
                    "Paris"};
    Tpolynome p;
    int i;

    cin >> x.re >> x.im >> y.re >> y.im;
    cout << "somme : " << x.re + y.re << x.im + y.im << endl;

    cin >> date.jour >> date.mois;
    if (date.jour>30 && date.mois == "novembre")
        cout << "date erronée" << endl;

    do cin >> p.degre; while (p.degre<0 || p.degre>100);

    for(i=1;i<=p.degre;i++)
        cin >> p.coef[i];
    }


    Un autre exemple utilisant des structures.

    #include <iostream.h>
    #include <string>
    #include <iomanip.h>

    const int N=10;

    typedef struct {
        string nom; // nom étudiant
        int numet; // numéro étudiant
        int rang; // rang à l'examen
    } fiche;

    // un tableau de structures sera notre base de données
    typedef fiche base[N];

    void main()
    {   // initialisation de la base de données:
        // une entrée par case du tableau;
        // les cases étant des sructures,
        // une entrée par champ de la structure

        base b = {{"Toto",1,3},{"Titi",2,4},{"Tata",3,5},
                {"Tutu",4,1},{"Tete",5,2},{"Toto2",6,8},
                {"Titi2",7,9},{"Tata2",8,10},
                {"Tutu2",9,6},{"Tete2",10,7}};

        base bc; /* bc sera notre base classée */
        int i;

        // écriture de la base avant classement
        for (i=0;i<N;i++){
            cout << b[i].nom << "\t" << b[i].numet << "\t";
            cout << b[i].rang << endl;
        }

        // classement
        for (i=0;i<N;i++) bc[b[i].rang-1]=b[i];

        // écriture de la base classée
        cout << endl << "Base classée." << endl << endl;
        for (i=0;i<N;i++){
            cout << bc[i].nom << "\t" << bc[i].numet << "\t";
            cout << bc[i].rang << endl;
        }
    }


    Cours 7

    Plan :

    Tours de Hanoi en pseudo.

    type tour = énumération (Gauche, Droite, Milieu)

    procédure déplace( valeur n: entier; valeur orig,dest,tmp: tour)
    // déplace une tour de n disques de orig à dest
    // en utilisant tmp comme tour auxiliaire
    début déplace
       si n = 1 alors
          imprime "déplacer un disque de " orig " à " dest
       sinon
          déplace(n-1, orig, tmp, dest)
          imprime "déplacer un disque de " orig " à " dest
          déplace(n-1, tmp, dest, orig)
       finsi
    fin déplace

    programme Hanoi
    variables ndisques: entier
    début Hanoi
       lire ndisques
       déplace(ndisques, Gauche, Droite, Milieu)
    fin Hanoi

    Tours de Hanoi en C++.

    #include <iostream.h>
    #include <string>

    typedef enum tour {Gauche, Droite, Milieu};

    string chaine(tour t) {
       if (t==Gauche)
          return "Gauche";
       else if (t==Droite)
          return "Droite";
       else
          return "Milieu";
    }

    void deplace(int n, tour orig, tour dest, tour tmp)
    // déplace une tour de n disques de orig à dest
    // en utilisant tmp comme tour auxiliaire
    { // deplace
      if (n == 1)
        cout << "déplacer un disque de " << chaine(orig)
             << " à " << chaine(dest) << endl;
      else {
        deplace(n-1, orig, tmp, dest);
        cout << "déplacer un disque de " << chaine(orig)
             << " à " << chaine(dest) << endl;
        deplace(n-1, tmp, dest, orig);
      }
    } // deplace

    void main () Hanoi
    { int nb;
      cout << "Combien de disques?"; cin >> nb;
      deplace(nb,Gauche,Droite,Milieu);
    }