C++ fonction récursive plante si appelée 32436 fois

Fermé
neo781 Messages postés 2 Date d'inscription mardi 1 novembre 2016 Statut Membre Dernière intervention 2 novembre 2016 - Modifié par crapoulou le 1/11/2016 à 22:07
 Utilisateur anonyme - 3 nov. 2016 à 19:51
Bonjour,

J'ai fait un sudoku solveur avec fonction récursive en backtraking. Elle plante pour les grilles difficiles où elle doit s'appeler plus de 32436 fois. Quelqu'un peut-il me dire pourquoi ? merci.

#include <iostream>
#include <cstdio>

using namespace std;

const int avance = 1;
const int recule = 7;
int compteur = 0;

void affiche (int t[9][9]);
bool teste (int t[9][9], int nb, int x, int y);
int remplit (int tab[9][9], int t1 [81]);
bool calcule (int tab [9][9], int t1[81], int c, int nbcat, int val, int av_rec);
bool fin (int tab [9][9], int t1[81], int nbcat);

int main()
{


// facile
  int tab[9][9]= {{0,1,0,0,2,4,0,0,9},{0,3,0,0,0,8,0,0,0},{2,9,4,6,7,3,0,0,8},{0,0,2,0,1,0,9,6,5},{0,0,8,0,4,0,2,0,0},
                    {9,7,1,0,6,0,3,0,0},{4,0,0,9,5,2,6,7,1},{0,0,0,7,0,0,0,4,0},{6,0,0,4,8,0,0,9,0}};



//int tab[9][9]= {{0,0,0,0,0,0,0,1,2},{0,0,0,0,0,0,0,0,3},{0,0,2,3,0,0,4,0,0},{0,0,1,8,0,0,0,0,5},{0,6,0,0,7,0,8,0,0},
//        {0,0,0,0,0,9,0,0,0},{0,0,8,5,0,0,0,0,0},{9,0,0,0,4,0,5,0,0},{4,7,0,0,0,6,0,0,0}};

 //int tab[9][9]= {{0,3,2,0,0,1,0,0,6},{0,0,8,0,2,5,0,3,4},{7,0,0,4,0,0,8,1,0},{0,0,0,2,0,0,1,6,0},{0,0,1,3,0,7,4,0,0},
  //                  {0,7,9,0,0,6,0,0,0},{0,4,3,0,0,2,0,0,8},{8,1,0,6,3,0,5,0,0},{2,0,0,7,0,0,3,4,0}};



    // le plus difficile du monde ??
 //int tab[9][9] = {{1,0,0,0,0,7,0,9,0},{0,3,0,0,2,0,0,0,8},{0,0,9,6,0,0,5,0,0},{0,0,5,3,0,0,9,0,0},{0,1,0,0,8,0,0,0,2},
   //                 {6,0,0,0,0,4,0,0,0},{3,0,0,0,0,0,0,1,0},{0,4,0,0,0,0,0,0,7},{0,0,7,0,0,0,3,0,0}};

// difficile
 // int tab[9][9] =  {{0,1,0,0,0,4,0,9,7},{0,0,7,0,0,0,0,4,8},{0,8,9,0,5,0,2,0,3},{5,0,0,6,0,2,0,0,0},{0,0,0,0,0,0,0,0,0},
 //                   {0,0,0,5,0,3,0,0,8},{1,0,2,0,8,0,6,5,0},{0,6,0,0,0,0,1,0,0},{3,0,0,4,0,0,0,7,0}};

int t81 [81]; int r;
r=remplit (tab,t81);

affiche (tab);
calcule (tab, t81, 0, r, 1, avance);
affiche (tab);

return 0;
}

/*** Teste la valeur nb en x,y dans t
renvoie true si valeur possible, false autrement ***/

bool teste (int t[9][9], int nb, int x, int y)
{
    int i,j;
    if (nb > 9)                return false;
    for (i=0;i<9;i++)
    {
    if (t[i][y] == nb)         return false;
    if (t[x][i] == nb)         return false;
    }

    int _x = x - (x % 3), _y = y - (y % 3);

        for (i = _x ; i < _x + 3 ; i++)
        for (j = _y ; j < _y + 3 ; j++)
        {
            if (t[i][j] == nb) return false;
        }

    return true;
}

/*** Remplit le tableau t81 avec toutes les références des cases à compléter
                                    forme : i = c / 9; j = c % 9;
                                    avec respectivement i ordonnée et j abscisse de la case sous la forme tab [i][j] ***/

int remplit (int tab[9][9], int t1 [81])
{
 int c,i,j,k;

    for (c=0;c<81;c++) t1[c] = 0;

    k = 0;
    for (c=0;c<81;c++)
    {
    i = c / 9; j = c % 9;
        if ((tab [i][j]) == 0)
        {
        t1[k] = c ;
        k++;
        }
    }
return k; // k : nombre de cases à trouver
}

/*** Affiche la grille tab [9][9] ***/

void affiche (int t[9][9])
{
    int i,j,l;

    cout << endl;
    cout << " **********************************************" <<endl;

    for (i=0;i<9;i++){
    for (j=0;j<9;j++){
        if (j == 0) cout << " *  ";

        if (t[i][j] != 0)
            {cout <<t[i][j] <<"   ";}
        else
            {cout <<"    ";}

        if ((j+1)%3 == 0) cout << "*  ";
    }
    cout << endl;
    if ((i+1)%3 == 0) cout << " **********************************************" <<endl;
    }
}

/*** Fonction principale récursive (vers l'avant et vers l'arrière) ***/

bool calcule (int tab [9][9], int t81[81], int c, int nbcat, int val, int av_rec )
{

 int i,j,k;
 k = t81 [c];
 i = k / 9; j = k % 9;
 compteur ++;
 cout << "compteur : " << compteur << " c : " << c << " val : "<< val << " " << endl;

/***Avance et remplit tab [9][9]***/

  if ((av_rec) == avance)
  {
    if (val > 9) {tab [i][j] = 0; calcule (tab, t81, c - 1, nbcat, val, recule);}
        // S'il n'est pas possible d'avancer, c'est qu'il faut reculer.
    else
    {
        if (teste (tab, val, i, j) == false) calcule (tab, t81, c, nbcat, val + 1, avance);
        else
        {
            tab [i][j] = val;
            if (fin (tab, t81, nbcat) == true) return true;
            calcule (tab, t81, c + 1, nbcat, 1, avance);
        }
    }
  }
  if ((av_rec) == recule)
  {
    val = tab [i][j];
        if (val == 9) {tab [i][j] = 0; calcule (tab, t81, c - 1, nbcat, val, recule);}
        else calcule (tab, t81, c, nbcat, val + 1, avance);
  }
}

/*** Renvoie true si la dernière case est remplie ***/


bool fin (int tab [9][9], int t81[81], int nbcat)
{
int i,j,c;
    c = t81[nbcat-1];
    i = c / 9; j = c % 9;
    if (tab [i][j] == 0) return false;
return true;
}

2 réponses

Utilisateur anonyme
2 nov. 2016 à 20:43
Salut,

Les fonctions récursives ne peuvent aller au délà d'une certaine profondeur pour une histoire de taille de pile (heap size).

Chaque instruction que tu appelles est stockée dans une pile et ensuite les instructions sont traitées. Cependant, les fonctions récursives font appel à elles-même jusqu'à une certaine profondeur. Malheureusement, la pile ne peut stocker indéfiniment les instructions à résoudre. C'est un peu comme un vase que tu remplis trop sans le vider à chaque fois.

Plus d'information : https://franckh.developpez.com/tutoriels/c-ansi/recursivite/
0
neo781 Messages postés 2 Date d'inscription mardi 1 novembre 2016 Statut Membre Dernière intervention 2 novembre 2016
2 nov. 2016 à 21:39
Merci Jason.
Du coup j'ai réécrit mon programme en remplaçant la fonction récursive
par une boucle while. Et là ça a marché tout de suite.

Je donne la version de mon sudoku solver avec la boucle while si ça peut intéresser quelqu'un.

#include <iostream>
#include <cstdio>

using namespace std;

const int avance = 1;
const int recule = 0;
int compteur = 0;

void affiche (int t[9][9]);
bool teste (int t[9][9], int nb, int x, int y);
int remplit (int tab[9][9], int t1 [81]);
bool calcule (int tab [9][9], int t1[81], int c, int nbcat, int val, int av_rec);
bool fin (int tab [9][9], int t1[81], int nbcat);

int main()
{


// facile
//int tab[9][9]= {{0,1,0,0,2,4,0,0,9},{0,3,0,0,0,8,0,0,0},{2,9,4,6,7,3,0,0,8},{0,0,2,0,1,0,9,6,5},{0,0,8,0,4,0,2,0,0},
// {9,7,1,0,6,0,3,0,0},{4,0,0,9,5,2,6,7,1},{0,0,0,7,0,0,0,4,0},{6,0,0,4,8,0,0,9,0}};



//int tab[9][9]= {{0,0,0,0,0,0,0,1,2},{0,0,0,0,0,0,0,0,3},{0,0,2,3,0,0,4,0,0},{0,0,1,8,0,0,0,0,5},{0,6,0,0,7,0,8,0,0},
// {0,0,0,0,0,9,0,0,0},{0,0,8,5,0,0,0,0,0},{9,0,0,0,4,0,5,0,0},{4,7,0,0,0,6,0,0,0}};

//int tab[9][9]= {{0,3,2,0,0,1,0,0,6},{0,0,8,0,2,5,0,3,4},{7,0,0,4,0,0,8,1,0},{0,0,0,2,0,0,1,6,0},{0,0,1,3,0,7,4,0,0},
// {0,7,9,0,0,6,0,0,0},{0,4,3,0,0,2,0,0,8},{8,1,0,6,3,0,5,0,0},{2,0,0,7,0,0,3,4,0}};



// le plus difficile du monde ??
int tab[9][9] = {{1,0,0,0,0,7,0,9,0},{0,3,0,0,2,0,0,0,8},{0,0,9,6,0,0,5,0,0},{0,0,5,3,0,0,9,0,0},{0,1,0,0,8,0,0,0,2},
{6,0,0,0,0,4,0,0,0},{3,0,0,0,0,0,0,1,0},{0,4,0,0,0,0,0,0,7},{0,0,7,0,0,0,3,0,0}};

// difficile
// int tab[9][9] = {{0,1,0,0,0,4,0,9,7},{0,0,7,0,0,0,0,4,8},{0,8,9,0,5,0,2,0,3},{5,0,0,6,0,2,0,0,0},{0,0,0,0,0,0,0,0,0},
// {0,0,0,5,0,3,0,0,8},{1,0,2,0,8,0,6,5,0},{0,6,0,0,0,0,1,0,0},{3,0,0,4,0,0,0,7,0}};

int t81 [81]; int r;
r=remplit (tab,t81);

affiche (tab);
calcule (tab, t81, 0, r, 1, avance);
affiche (tab);
return 0;
}

/************************************************************************************************************************
Teste la valeur nb en x,y dans t
renvoie true si valeur possible, false autrement
                                                                                                                                                                                                                                                    • */bool teste (int t[9][9], int nb, int x, int y){ int i,j; if (nb > 9) return false; for (i=0;i<9;i++) { if (t[i][y] == nb) return false; if (t[x][i] == nb) return false; } int _x = x - (x % 3), _y = y - (y % 3); for (i = _x ; i < _x + 3 ; i++) for (j = _y ; j < _y + 3 ; j++) { if (t[i][j] == nb) return false; } return true;}/************************************************************************************************************************ Remplit le tableau t81 avec toutes les références des cases à compléter forme : i = c / 9; j = c % 9; avec respectivement i ordonnée et j abscisse de la case sous la forme tab [i][j]************************************************************************************************************************** */int remplit (int tab[9][9], int t1 [81]){ int c,i,j,k,l; for (c=0;c<81;c++) t1[c] = 0; k = 0; for (c=0;c<81;c++) { i = c / 9; j = c % 9; if ((tab [i][j]) == 0) { t1[k] = c ; k++; } }/*int t2 [k]; for (i=0;i<k;i++) t2[i] = 0; // création et initialisation à zéro de t2for (l=0;l<k;l++)for (c=1;c<10;c++){ i = l / 9; j = l % 9; if (teste (tab,c,i,j) == true) t2 [l] ++;}for (j=0;j<k;j++) cout << t1[j] << " ";cout << endl;for (j=0;j<k;j++) cout << t2[j] << " ";for (j=0;j<k-1;j++)for (i=0;i<k-1;i++){ if (t2[i] < t2[i+1]) { c = t2[i+1]; t2[i+1] = t2[i]; t2[i] = c; l = t1[i+1]; t1[i+1] = t1[i]; t1[i] = l; //c = t2[i]; t2[i] = t2[i+1]; t2[i+1] = c; //l = t1[i]; t1[i] = t1[i+1]; t1[i+1] = l; }}cout << endl;for (j=0;j<k;j++) cout << t2[j] << " ";*/return k; // k : nombre de cases à trouver}/************************************************************************************************************************ Affiche la grille tab [9][9]************************************************************************************************************************** */void affiche (int t[9][9]){ int i,j,l; cout << endl; cout << " **********************************************" <<endl; for (i=0;i<9;i++){ for (j=0;j<9;j++){ if (j == 0) cout << " * "; if (t[i][j] != 0) {cout <<t[i][j] <<" ";} else {cout <<" ";} if ((j+1)%3 == 0) cout << "* "; } cout << endl; if ((i+1)%3 == 0) cout << " **********************************************" <<endl; }}/************************************************************************************************************************ Fonction principale (vers l'avant et vers l'arrière)************************************************************************************************************************** */bool calcule (int tab [9][9], int t81[81], int c, int nbcat, int val, int av_rec ){ int i,j,k; while (true) { k = t81 [c]; i = k / 9; j = k % 9; compteur ++;if ((av_rec) == avance) { if (val > 9) {tab [i][j] = 0; c-- ; av_rec = recule;} // S'il n'est pas possible d'avancer puisqu'aucune valeur de 1 à 9 ne fonctionne, c'est qu'il faut reculer d'une case. else { if (teste (tab, val, i, j) == false) {val++ ; av_rec = avance;} // si une valeur quelconque entre 1 et 9 ne fonctionne pas, on passe à la suivante. else { tab [i][j] = val; if (fin (tab, t81, nbcat) == true) return true; // si une valeur fonctionne, on la met dans le tableau et on passe à la case suivante en testant val = 1. val = 1; c++; av_rec = avance; } } }else // if ((av_rec) == recule) { val = tab [i][j]; if (val == 9) {tab [i][j] = 0; c-- ; av_rec = recule;} // si je dois reculer d'une case, je mets la valeur de la case que je viens de quitter à zéro else {val++, av_rec = avance;} // si je me rend compte qu'ayant reculé d'une case je peux ajouter un à cette case, je le fais. }}}/*bool calcule (int tab [9][9], int t81[81], int c, int nbcat, int val, int av_rec ){ int i,j,k; k = t81 [c]; i = k / 9; j = k % 9; compteur ++; cout << "compteur : " << compteur << " c : " << c << " val : "<< val << " " << endl;/********************************************************************************************************************* Avance et remplit tab [9][9]***********************************************************************************************************************//* if ((av_rec) == avance) { if (val > 9) {tab [i][j] = 0; calcule (tab, t81, c - 1, nbcat, val, recule);} // S'il n'est pas possible d'avancer, c'est qu'il faut reculer. else { if (teste (tab, val, i, j) == false) calcule (tab, t81, c, nbcat, val + 1, avance); else { tab [i][j] = val; if (fin (tab, t81, nbcat) == true) return true; calcule (tab, t81, c + 1, nbcat, 1, avance); } } } if ((av_rec) == recule) { val = tab [i][j]; if (val == 9) {tab [i][j] = 0; calcule (tab, t81, c - 1, nbcat, val, recule);} else calcule (tab, t81, c, nbcat, val + 1, avance); }}*//********************************************************************************************************************* Renvoie true si la dernière case est remplie.***********************************************************************************************************************/bool fin (int tab [9][9], int t81[81], int nbcat){int i,j,c; c = t81[nbcat-1]; i = c / 9; j = c % 9; if (tab [i][j] == 0) return false;return true;}
0
Utilisateur anonyme
3 nov. 2016 à 19:51
Il y a un problème avec l'insertion de ton code. Pense à mettre le sujet en résolu !
0