Retour sur le retour sur trace et récursivité

Une suite à mon article, une deuxième méthode pour trouver le nombre de solutions de l'énigme. Après la solution itérative, voici la récursive.

Le code

Voici le programme pour comparer les temps de résolutions des deux solutions. On peut remarquer que le code récursif est plus court et nécessite une fonction de moins (la recherche du lieu précédent).

"""Recherche du nombre de solutions pour une énigme"""

import datetime

# Les déplacements
#
#       2
#   3   .   1
#     . . .
# 4 . . x . . 0
#     . . .
#   5   .   7
#       6

a = datetime.datetime.now()

def nv_lieu(lieu, dplt):
    """Retourne le lieu après déplacement"""
    x, y = lieu
    match dplt:
        case 0:
            x += 3
        case 1:
            x += 2
            y -= 2
        case 2:
            y -= 3
        case 3:
            x -= 2
            y -= 2
        case 4:
            x -= 3
        case 5:
            x -= 2
            y += 2
        case 6:
            y += 3
        case 7:
            x += 2
            y += 2

    return (x, y)


def ac_lieu(lieu, dplt):
    """Retourne le lieu avant déplacement"""
    x, y = lieu
    match dplt:
        case 0:
            x -= 3
        case 1:
            x -= 2
            y += 2
        case 2:
            y += 3
        case 3:
            x += 2
            y += 2
        case 4:
            x += 3
        case 5:
            x += 2
            y -= 2
        case 6:
            y -= 3
        case 7:
            x -= 2
            y -= 2

    return (x, y)


def test_lieu_dplt(lieu, dplt, grille):
    """Vérifie la validité du chemin
    à partir du lieu (x,y),
    du déplacement 0..7
    et de la grille cases disponibles
    """
    x, y = nv_lieu(lieu, dplt)

    # En dehors de la grille
    if not (0 <= x <= 4 and 0 <= y <= 4):
        return False
    # Case pas disponible
    if (x, y) not in grille:
        return False
    # C'est valide
    return True


def chercher_solutions_it(lieu):
    """Cherche les solution pour un lieu de départ
    et les retourne sous forme de liste"""
    solutions = []
    grille = {(x, y) for x in range(5) for y in range(5)}
    grille.remove(lieu)
    chemin = []
    etape = 0
    dplt = 0

    def cul_de_sac(grille, lieu, chemin, dplt, etape):
        grille.add(lieu)
        lieu = ac_lieu(lieu, chemin[-1])
        dplt = chemin[-1] + 1
        chemin = chemin[:-1]
        etape -= 1

        return grille, lieu, chemin, dplt, etape

    while True:
        if etape == 24:
            solutions.append(chemin)
            grille, lieu, chemin, dplt, etape = cul_de_sac(
                grille, lieu, chemin, dplt, etape
            )
        if etape == len(chemin):
            if test_lieu_dplt(lieu, dplt, grille):
                chemin.append(dplt)
                etape += 1
                lieu = nv_lieu(lieu, dplt)
                grille.remove(lieu)
                dplt = 0
            elif dplt < 7:
                dplt += 1
            elif chemin != []:
                grille, lieu, chemin, dplt, etape = cul_de_sac(
                    grille, lieu, chemin, dplt, etape
                )
            else:
                return solutions


def iterative():
    nbr_total = 0
    nbr_solutions_total = 0

    for x in range(5):
        for y in range(5):
            solutions = chercher_solutions_it((x, y))


def chercher_solutions_re(lieu, grille, chemin):

    global solutions

    nv_grille = grille[:]
    nv_grille.remove(lieu)

    if len(nv_grille) == 0:
        solutions[chemin[0]].append(chemin)
    else:
        for dplt in range(8):
            if test_lieu_dplt(lieu, dplt, nv_grille):
                chercher_solutions_re(
                    nv_lieu(lieu, dplt),
                    nv_grille,
                    chemin +(dplt, )
                )


def recursive():

    global solutions

    solutions = {(x, y):[] for x in range(5) for y in range(5)}

    grille = [(x, y) for x in range(5) for y in range(5)]

    for x in range(5):
        for y in range(5):
            lieu = (x, y)
            chercher_solutions_re(lieu, grille, (lieu, ))


b = datetime.datetime.now()

iterative()

c = datetime.datetime.now()

recursive()

d = datetime.datetime.now()

print(b-a)
print(c-b)
print(d-c)

Bilan

Les temps obtenus sur deux ordinateurs sont résumés dans le tableau suivant.

Définitions Itératif Récursif Différence


0.000046 74.324188 65.233239 -12,23% 0.000046 74.959233 65.506206 -12,61% 0.000036 29.403527 26.463715 -10,00% 0.000022 29.656857 26.841345 -09,49% 0.000022 29.409830 26.901892 -08,53% 0.000022 29.311514 26.807893 -08,54% 0.000022 29.418347 26.951236 -08,39%


Le temps pour trouver le nombre de solutions est inférieur de 10% avec le récursif qu'avec l'itératif.