Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

RÉCURSIVITÉ: PLACER HUIT REINES SUR UN ÉCHIQUIER


Information sur la source

Description

Cet exemple illustre la programmation récursive.
La méthode récursive est une méthode "bourrin", - c'est vrai, - puisqu'elle va explorer toutes les possibilités jusqu'à trouver une solution, mais c'est parfois la seule méthode qui tienne la route, (rien n'empêche d'optimiser la recherche si c'est possible).
Mais sa principale qualité est qu'elle permet une programmation élégante, je dirais même limpide. Pour autant, les premières fois que l'on tente de la mettre en oeuvre, on peut se faire des noeuds à la tête !
Elle s'applique lorsque l'on est confronté à un problème que l'on ne sait pas résoudre, mais que l'on peut décomposer en problèmes élémentaires de même nature.
La solution du problème de complexité C est alors une fonction simple de la solution du problème de complexité C-1.

L'exemple bateau est le calcul de la fonction factorielle: Factorielle(e) = e x (e-1) x e(-2) x ... x 3 x 2 x 1,
(e étant un entier positif; si e est nul, Factorielle(e)=1).
Première solution: faire une boucle, (dans ce cas c'est la meilleure méthode).
Deuxième solution: l'algorithme récursif qui peut se programmer ainsi:
Private Function Factorielle(ByVal e As Long) As Long
If e <= 1 Then
Factorielle = 1
Else
Factorielle = e * Factorielle(e - 1)
End If
End Function
Remarques: e est supposé positif et pas trop grand pour éviter les overflow.
Il faudrait ajouter les contrôles d'erreur.

Quelques exemples de problèmes pouvant être résolus par récursivité:
- les tours de Hanoï;
- trouver le chemin pour qu'un cavalier parcourre toutes les cases d'un échiquier, une et une seule fois;
- placer huit reines sur un échiquier sans qu'elles ne se mettent en échec (voir l'exemple);
- recherche d'itinéraire.

 

Source

  • 'Remarque: il y a une et une seule reine par colonne,
  • ' le numéro de reine est donc confondu avec son numéro de colonne
  • 'Résumé: on cherche une ligne pour placer la reine numéro iReine,
  • ' si on y parvient
  • ' si c'est la dernière, on a terminé
  • ' sinon, on cherche à placer la suivante
  • ' sinon, il faut déplacer la reine précédente
  • Private Function PlacerReine(ByVal iReine As Integer) As Boolean
  • Dim iLigne As Integer
  • Dim bRetour As Boolean
  • For iLigne = 0 To 7
  • If Not bLigneOccupee(iLigne) _
  • And Not bDiagonaleMontanteOccupee(iLigne + iReine) _
  • And Not bDiagonaleDescendanteOccupee(7 + iReine - iLigne) Then
  • bLigneOccupee(iLigne) = True
  • bDiagonaleMontanteOccupee(iLigne + iReine) = True
  • bDiagonaleDescendanteOccupee(7 + iReine - iLigne) = True
  • Animation iLigne, iReine, True
  • If iReine = 7 Then
  • bRetour = True
  • Else
  • bRetour = PlacerReine(iReine + 1)
  • End If
  • If bRetour Then Exit For
  • bLigneOccupee(iLigne) = False
  • bDiagonaleMontanteOccupee(iLigne + iReine) = False
  • bDiagonaleDescendanteOccupee(7 + iReine - iLigne) = False
  • Animation iLigne, iReine, False
  • End If
  • Next iLigne
  • PlacerReine = bRetour
  • End Function
'Remarque: il y a une et une seule reine par colonne,
'          le numéro de reine est donc confondu avec son numéro de colonne
'Résumé:   on cherche une ligne pour placer la reine numéro iReine,
'          si on y parvient
'               si c'est la dernière, on a terminé
'               sinon, on cherche à placer la suivante
'          sinon, il faut déplacer la reine précédente
Private Function PlacerReine(ByVal iReine As Integer) As Boolean
Dim iLigne                                  As Integer
Dim bRetour                                 As Boolean
    For iLigne = 0 To 7
        If Not bLigneOccupee(iLigne) _
        And Not bDiagonaleMontanteOccupee(iLigne + iReine) _
        And Not bDiagonaleDescendanteOccupee(7 + iReine - iLigne) Then
            bLigneOccupee(iLigne) = True
            bDiagonaleMontanteOccupee(iLigne + iReine) = True
            bDiagonaleDescendanteOccupee(7 + iReine - iLigne) = True
            Animation iLigne, iReine, True
            If iReine = 7 Then
                bRetour = True
            Else
                bRetour = PlacerReine(iReine + 1)
            End If
            If bRetour Then Exit For
            bLigneOccupee(iLigne) = False
            bDiagonaleMontanteOccupee(iLigne + iReine) = False
            bDiagonaleDescendanteOccupee(7 + iReine - iLigne) = False
            Animation iLigne, iReine, False
        End If
    Next iLigne
    PlacerReine = bRetour
End Function

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Commentaires et avis

signaler à un administrateur
Commentaire de BruNews le 20/02/2008 19:36:24 administrateur CS

L' "élégance" est à confronter à:
- Risque d'explosion de la stack.
- Perte de perfs due à l'empilage et dépilage des params.
Voilà qui rend la récursivité nettement moins élégante.

Pour résumer, en prod une fonction ne reste en version récursive que tant qu'on a pas réussi à la rendre itérative.

signaler à un administrateur
Commentaire de lucqum le 20/02/2008 22:52:29

A Brunews,

Merci de votre commentaire, mais permettez-moi de le tempérer un peu.
Certes, rien n'est parfait en ce bas monde, mais je ne suis pas d'accord pour dire que la récursivité n'est qu'un pis aller en attendant mieux.
C'est vrai, le risque de débordement de pile est tout à fait réel, j'ai pu en rencontrer au temps où j'utilisais le C7.0. En effet, mettre en oeuvre la récursivité demande quelques précautions: comprendre le "mécanisme logiciel" mis en jeu, estimer le niveau maximum de réentrance, minimiser les paramètres passés ainsi que les variables locales. Pour autant, ce n'est pas une méthode à délaisser.
Pour ce qui est des performances, cela ne peut se juger qu'au cas par cas. Par ailleurs, aux performances du programme, il faut aussi confronter le coût de développement.
Le tournevis et le marteau sont deux très bons outils, mais suivant mes besoins, j'utiliserai l'un, l'autre, ou les deux, à moins que ce ne soit un troisième.

signaler à un administrateur
Commentaire de jupiter le 23/02/2008 09:21:08

Bonjour,
Dans la fonction « PlacerReine », on peut remplacer « bRetour = True » par Stop pour obtenir les différentes combinaisons possibles avec leur variantes.
Les variantes sont les mêmes combinaisons obtenues en tournant l'échiquier d'un quart ou d'un demi-tour.
Dans cet exemple, l'inconvénient de la programmation récursive est qu'il est très difficile (voir impossible) de modifier le programme de façon à obtenir uniquement les différentes combinaisons sans les variantes.

signaler à un administrateur
Commentaire de mstarsup5 le 26/02/2008 11:01:38

Salut, ayant moi même fait des programmes en récursif, et ayant cherché à voir leur fonctionnement en itératif, je ne peux qu'aller dans le sens de la remarque de BruNews: d'un point de vue performance, les procédés itératifs sont bien meilleurs. (J'arrivais parfois à avoir des gains en vitesse d'exécution de l'ordre de 400%... ce qui, comme tu peux le constater, n'est pas négligeable.)

Cela dit, je reconnais qu'un programme est souvent plus simple à faire en récursif, et il est parfois très difficile de l'adapter en récursif.(ayant moi même aussi programmé les programmes dont tu parles dans la description (tours de hanoi, trajet du cavalier pour se déplacer sur toutes les cases (et suivant la configuration, se retrouver sur son point de départ à la fin ou non), et le placement des dames sur l'échiquier), ainsi que pas mal de programmes de jeu avec intelligence artificielle.

signaler à un administrateur
Commentaire de neamar le 16/03/2008 13:29:28

Salutations,
La récursivité est certes plus longue que la méthode itérative...et encore, seulement si on utilise des procédures qui ne sont pas tail-rec (récursion terminale, voir plus bas..mais bon, c'est seulement pour la curiosité : VB ne gère pas le tail rec...)

Je me permets cependant de citer un excellent tutorial sur la récursivité :
"
D'une part, le tail-rec se justifie pour des questions de performances. Mais il faut savoir que les performances ne sont pas le seul but que doit viser le programmeur. Dans certain cas, elles ne sont même pas vraiment importantes (par exemple, quand on interagit avec l'utilisateur, qui est mille fois plus lent à choisir un bouton à la souris que n'importe quelle factorielle récursive codée avec les pieds), d'ailleurs il suffit de voir le nombre de gens qui codent dans des langages "lents", comme PHP, Python ou Ruby par exemple.
Bref, un autre aspect à ne pas négliger du code est la lisibilité. Là, l'utilisation de fonctions tail-rec devient plus controversée. Il y a deux cas : soit la fonction est naturellement tail-récursive (un compte à rebours par exemple) et ça ne pose aucun problème, soit la fonction demande une transformation, et alors vous devez peser le pour et le contre avec soin : la transformation pose-t-elle des problèmes de lisibilité ? Si vous n'utilisez qu'une seule fois la fonction dans votre programme, privilégiez plutôt la lisibilité, et laissez la "non tail-rec". Si elle est souvent utilisée et constitue une part non négligeable du temps utilisé par votre programme (il existe des outils pour mesurer ça), choisissez la version tail-rec. De toute façon, de nombreux programmeurs sont habitués à reconnaître le motif "accumulateur de fonction tail-rec" (choisissez un nom clair pour l'argument accumulateur supplémentaire), et cela ne leur posera donc aucun problème.

D'autre part, certaines fonctions ne peuvent pas devenir tail-récursives. Comme nous l'avons vu, une fonction est tail-récursive quand l'appel récursif est la dernière chose effectuée par la fonction. Qu'en est-il des fonctions qui font plusieurs appels récursifs (notre exemple max_pommes par exemple) ? Et bien c'est simple, ces fonctions ne peuvent tout simplement pas être rendues tail-récursives : seul le dernier appel pourrait être converti en appel terminal, et tous les autres appels (dans la boucle for de notre exemple) augmenteront la pile d'appels.
Cela pose-t-il un problème fondamental ? La réponse est non. En effet, la justification de l'optimisation tail-rec des fonctions est d'obtenir les mêmes performances que la version itérative. Pour ce genre de fonctions (récursivité à appels multiples), il n'existe pas de version itérative équivalente qui soit aussi simple. La version récursive est donc la seule manière simple de formuler le problème, et toutes les versions itératives basées sur cet algorithme devront trouver une manière de remplacer la pile d'appels (qui stocke des informations qui nous arrangent), et leurs performances ne seront donc pas meilleures.
"
(source : http://www.siteduzero.com/tuto-3-23774-1-la-recursivite.html )
Et pour conclure, je souligne la dernière phrase de la citation : certes, la récursivité est plus lente que l'itératif...MAIS si on utilise la méthode récursive, c'est souvent parce que la méthode itérative est inapplicable ou encore beaucoup plus lourde. Par exemple, dans cette source (http://www.vbfrance.com/codes/VRAIE-CALCULATRICE-ECRITURE-2D-ON-MARQUE-LIGNE-ENTIERE_46070.aspx ), l'utilisation de la récursivité pour le calcul est obligatoire : programmer avec un mode itératif serait 100 fois plus complexe et moins lisible.

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

[.NET] Methode Pushed sur les bouton [ par niluje ] Est-il possible de disposer de la même méthode 'pushed' que sur les 'Toolbar' pour les boutons ?par exempleje voudrait placer 3 boutons pour sélection >>METHODE POST AVEC WINSOCK<< [ par nullspace ] Je connais la méthode GET pour recevoir la une page web mais lorsqu'il s'agit d'utiliser un CGI avec la méthode POST, je suis perdu.Quelqu'un peut me Methode de Newton Raphson [ par fouf ] Je travaille actuellement sur un TP pour lequel je dois évaluer les performances de la méthode de Newton et je dois trouver une méthode d'arret du pro enregistrement d'image dans un fichier [ par cri99 ] Bonjour à tousQuelqu'un aurait-il la méthode pour enregistrer dans un fichier (bmp) le contenu d'un picturebox, contenu créé à partir de la méthode pa Connexions DAO [ par fredo54 ] Bonjour,J'utilise encore la méthode DAO pour me connecter à une base de donnée Access...Il paraît que ADo est plus adéquate certe !!!J'aimerais savoir algorithme de huffman [ par ObiWan2003 ] je voudrait savoir comment ça marche huffman et je voudrait avoir aussi le code source en CObiWan Kenobi Méthode Print de l'objet Printer non disponible [ par pydupuis ] Bonjour,Tentant pour la première fois de faire une impression avec l'objet Printer, je ne dispose pas de la méthode "Print" associée. Peut-être me man Créer une clé d'identification [ par Docck ] Salut,Je cherche à créer une clé d'identification de logiciel pour protéger mes propres logiciels, ceci grâce à un algorithme.Je pensais en fait recup méthode openrecordset [ par pedrolane ] j'ai un petit problème pour utiliser cette méthodevoici mon code:Dim mabase As DatabaseDim janvier As RecordsetSet mabase = opendatabase("contactrh", Algorithme Wavelet - Ondelette ? [ par YoYoDev20 ] Bonjour,Je vais peut-être un peu loin dans ma question mais quelqu'un connaitrait il un algorithme de calcul de Wavelet (ou Ondelette en français !) ?


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,702 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.