retour sur le code (récursivité)

Le problème exposé dans ce sujet a été résolu.

Ce dernier temps je découvre la notion de récursivité. consigne exercice : pour un nombre de joueurs, quel est le nombre de matches total si chaque joueur joue contre tous les autres joueurs qu’une seule fois

En réalité, la consigne de l’exerce était bcp plus complexe que ça, je me suis permis de le simplifier en le rendant plus facile a faire. En tout cas j’appris bcp de choses et j’ai constaté que le nombre de matche a augmente au début de 1 puis 2, puis 3, puis 4 et ainsi de suite :

Avec j : nombre de joueurs et m : nombre de matches

pour j = 1 , m = 0 car il n’y a qu’un seul joueur

pour j = 2 , m = 1 : 1 VS 2

pour j = 3 , m = 3 : 1 vs 2 , 1 vs 3 , 2 vs 3

pour j = 4 , m = 6 : 1 vs 2 , 1 vs 3 , 1 vs 4 , 2 vs 3 , 2 vs 4 , 3 vs 4

Au premier essaye, j’avais implémenté une fonction récursive non terminal or j’avais lu et compris qu’elle était plus longue et faisait une double exécution (ascendante puis descendante).

Après une qui est récursive terminal. Il était fastidieux de tester le résultat de la fonction pour des nombres de joueurs trop élevé, j’ai utilisé la formule pour calculer la somme des entiers naturels en la modifiant un peu : m = (j * (j - 1) )/2

Bref, j’aimerai avoir des retours sur ce code et ce qu’il faut que j’évite. Merci pour vos temps en avance.

code :

#include <iostream>
#include <limits>
#include <cassert>

void securisee_entree (int & entier);
int nombre_matches_term (int j, int m);
void test_nombre_matches_term_return_value ();

// -----------------------------------------------------------------------------------------------------------------------------------------

int main ()
{
    int j { 0 };
    std::cout << "Donnez le nombre de joueur : ";
    securisee_entree (j);

    int const m { 0 };
    int resultat { nombre_matches_term (j, m) };

    std::cout << "pour j = " << j << ", m = " << resultat << std::endl;

    return 0;
}

// -----------------------------------------------------------------------------------------------------------------------------------------

void securisee_entree (int & entier)
{
    while (not (std::cin >> entier) or entier < 0)
    {
        if (std::cin.eof () )
        {
            std::cout << "Fin du flux." << std::endl;
            break;
        }

        else if (std::cin.fail () )
        {
            std::cout << "entre invalide, recommencez : ";
            std::cin.clear ();
            std::cin.ignore (std::numeric_limits<std::streamsize>::max (), '\n');
        }

        else
        {
            std::cout << "nombre negatif, recommencez : ";
        }
    }
}

// -----------------------------------------------------------------------------------------------------------------------------------------

/***
    + ROLE :
        > permet de calculer le nombre de matche pour un jeu a 2 joueurs.
   
    + PARAMETRE :
        > prend en argument le nombre de joueurs et le nombre de match qui
          vaut au depart 0.
          
    + RETURN-VALUE :
        > retourne une valeur de type integer.
    
        
    + AJOUT ULTERIEUR :
        > aucune idee
        
***/

int nombre_matches_term (int j, int m)
{
    assert (j >= 0 and "le nombre de joueur ne peut pas etre negatif.");
    assert (m >= 0 and "le nombre de matches ne peut pas etre negatif.");

    if (j == 0)
    {
        return m;
    }

    else
    {
        return nombre_matches_term (j - 1, (j - 1) + m);
    }
}

// test-unitaire de la fonction nombre_matches

void test_nombre_matches_term_return_value ()
{
    int j { 1 };
    int const m { 0 };
    int resultat { nombre_matches_term (j, m) };
    assert (resultat == 0 and "pour j = 1, m doit-etre egal a 0.");

    j = 2;
    resultat = nombre_matches_term (j, m);
    assert (resultat == 1 and "pour j = 2, m doit-etre egal a 1.");

    j = 3;
    resultat = nombre_matches_term (j, m);
    assert (resultat == 3 and "pour j = 3, m doit-etre egal a 3.");

    j = 4;
    resultat = nombre_matches_term (j, m);
    assert (resultat == 6 and "pour j = 4, m doit-etre egal a 6.");
}

Désolé, j’avais poster le sujet sur le mauvais forum au départ.

+0 -0

Salut,

assert (j >= 0 and "le nombre de joueur ne peut pas etre negatif.");

j’avais appris ceci sur le cours de ce site, mais a la place de and il y avait || && personnellement je préfère utiliser and car elle est plus lisible et expressive.

D’après ce que j’ai compris, on utilise les assertions pour gérer les erreurs du programmeurs. Dans ce bout de code ça permet de définir les préconditions de la fonctions qu’on doit respecter sinon elle nous renvoie des résultats erronés.

+0 -0

Oulah mais c’est pas du tout la même chose. || est un OU logique, pas un ET.

Et and et && ne sont pas non plus la même opération puisqu’elles n’agissent pas sur les mêmes types d’opérandes (et je crois qu’elles n’ont pas la même priorité).

oui c’est vrai, je me suis précipité, c’est un &&, erreur de ma part, je viens de le corriger. Ah d’accord, croyais qu’ils agissaient de la même manière. peux-tu m’expliciter ce point (quand vous dites qu’ils n’ont pas la même priorité et qu’ils n’agissent pas sur le même type d’opérandes). Merci sinon

+0 -0

Encore une fois, d’après ce que j’ai compris, une chaîne de caractères est toujours évaluée a true pour le compilateur. Une assertion fait planter le programme et nous signale le fichier, la ligne et la condition qui a fait planter le programme. La chaîne de caractères vient en supplément pour rendre le message afficher plus claire.

Je ne sais pas si je réponds bien a ta question car je suis moi-même débutant dans ce langage et il y a bcp de chose que je ne comprends pas a 100%. En tout le cours de ce site parle des assertions et l’expliques très bien.

+0 -0

il y a peu de temps, j’avais lu sur Wikipedia que lors de la compilation, la récursion terminale peut-être transformée en itération mais je ne sais pas si c’est vrai aussi pour C++.

En tout cas je me demande aussi cette question.

Je comprends qu’une fonction récursive terminale est plus efficace qu’une fonction récursive non-terminale mais je ne comprends pas en quoi une fonction itérative peut-être plus efficace qu’une fonction récursive terminale, vu que celle-ci utilise moins de concepts (pas de loop) seulement des appels récursifs qui n’ont pas besoin d’être empilés (chaque appel remplace simplement l’appel précédent) et qu’il y a qu’une seule phase de descente

+0 -0

Sinon, la récursion me semble bonne. Est-ce que le C++ optimise la récursion terminale, cela dit ?

otini

Ce n’est pas dans le scope du langage de définir comment optimiser. Dans la pratique, les compilateurs le fond, mais il faut activer les optimisations.

mais je ne comprends pas en quoi une fonction itérative peut-être plus efficace qu’une fonction récursive terminale, vu que celle-ci utilise moins de concepts (pas de loop) seulement des appels récursifs qui n’ont pas besoin d’être empilés (chaque appel remplace simplement l’appel précédent) et qu’il y a qu’une seule phase de descente

Il y a une boucle: l’appel de la fonction récursive. Une boucle ou une récursion terminale suivent exactement les mêmes principes:

  • condition d’arrêt
  • modification d’une valeur de retour
  • saut sur la condition (la boucle)

Ce qui est plus efficace n’est pas la fonction itérative sur la fonction récursive terminal, mais une version itérative sur une fonction récursive dû à l’absence de pile. Le fait qu’une fonction soit récursive terminale ne veut pas dire que la pile d’appel va disparaître, mais qu’il est possible de la faire disparaître. Et si la récursion disparaît, alors elle ne se distingue pas d’une version itérative dans le résultat final.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte