Page 1 of 1

Aide pour un algo récursif

Unread postPosted: 16 Feb 2014, 23:03
by Bisam
Bonsoir,

Je suis en galère avec un algorithme récursif destiné à la résolution d'un problème de Project Euler.
Je ne veux pas vraiment que vous me donniez une solution mais que vous me disiez si vous voyez ce qui ne va pas...

Voici le code Python (pour ceux qui ne le maîtrisent pas, ça se lit presque comme du pseudo-code).
Les """ servent à encadrer des commentaires

Le but est de trouver le nombre d'ensembles constitués de nombres premiers dont les chiffres sont les chiffres de 1 à 9 écrits une et une seule fois chacun.
Code: Select all
def pb118()

    """On calcule une liste de True et False, servant d'oracle pour savoir si un entier est premier."""

    isprime = eratho(10 ** 8)

    """Avec cet oracle, on fait une liste des nombres premiers qui ne contiennent pas le chiffre 0.
    On les enregistre sous forme de chaînes de caractères."""

    primes = [str(i) for i in range(10 ** 8) if isprime[i] and '0' not in str(i)]

    """Ensuite, on fait une liste des ensembles des chiffres composant les nombres premiers
    qui ne contiennent aucun chiffre répété. C'est de cette liste qu'on se servira dans la fonction récursive."""

    primedigits = [{int(j) for j in a} for a in primes if all(u not in a[k + 1:] for k, u in enumerate(a))]

    """res contiendra le résultat cherché. On utilise une liste car cela évite d'avoir à la déclarer globale..."""

    res = [0]

    def search(currentdigits, nextindex):
        """Cherche récursivement des nombres premiers à rajouter au panier et fait le compte
        des paniers trouvés."""
        l = len(currentdigits)
        if l == 9:
        """Dans ce cas, on a forcément : currentdigits = {1, 2, 3, 4, 5, 6, 7, 8, 9}"""
            res[0] += 1
        else:
            for i, newDigits in enumerate(primedigits[nextindex:]):
                if len(newDigits) + l > 9:
        """Inutile de poursuivre si les nombres premiers suivants ont trop de chiffres."""
                    break
                if currentdigits.isdisjoint(newDigits):
        """On rajoute les nouveaux chiffres et on continue à chercher récursivement à partir
        du rang suivant dans la liste des premiers."""
                    search(currentdigits | newDigits, i + 1)

    """On commence à chercher à partir d'un ensemble vide, et à partir de l'indice 0."""
    search(set(), 0)

    return(res[0])


Ce programme ne renvoie malheureusement pas le bon résultat... mais je ne sais pas pourquoi.
Si vous comprenez ce code et que vous voyez pourquoi ça ne marche pas... merci de me le dire !

PS : Pour ceux qui voudraient essayer l'algorithme chez eux, c'est du Python 3.
Pour info, le calcul de "primedigits" au départ prend 540Mo de RAM environ et un peu moins de 4 minutes.
Ensuite, la recherche récursive ne prend que 3 minutes environ.

Re: Aide pour un algo récursif

Unread postPosted: 19 Feb 2014, 19:28
by Lionel Debroux
Tiens, je ne l'avais jamais fait, cet algorithme-là du projet Euler. En même temps, je n'en avais fait qu'exactement 50 ^^

{1, 2, 3, 4, 5, 6, 7, 8, 9} n'est pas constitué exclusivement d'éléments premiers ?
Il ne doit pas y avoir beaucoup d'ensembles d'éléments appartenant au problème qui ont 6 éléments ou plus.
Parmi les éléments permettant l'élagage des possibilités, toutes les entrées où 1, 4, 6, 8 et 9 sont présents ne répondent pas au critère.

Re: Aide pour un algo récursif

Unread postPosted: 20 Feb 2014, 22:06
by Bisam
J'étais sûr que tu répondrais, Lionel...

À vrai dire, même si gagner en rapidité est toujours bon à prendre, cet algorithme est déjà suffisamment performant à mon goût.
Il est certain qu'on pourrait élaguer pas mal (même si je n'ai pas bien compris ton idée) mais ce n'était pas vraiment le but de ma question.

Mais c'est déjà gentil de t'être penché sur la question...

Pour ceux qui voudraient tester, je fournis également le code de mon crible d'Érathostène, qui est utilisé au début du programme.
Show/Hide spoilerAfficher/Masquer le spoiler
Code: Select all
def eratho(N):
    """Renvoie une liste de True ou False, indiquant si un entier entre 0 et N-1 est premier par le crible d'Érathosthène"""
    t = [i % 2 == 1 for i in range(N)] # on initialise tous les pairs à False
    t[1], t[2] = False, True
    r = int(N ** .5)
    i = 3
    while i <= r:
        if t[i]:
            for j in range(i ** 2, N, i):
                t[j] = False
        i += 2
    return(t)

Re: Aide pour un algo récursif

Unread postPosted: 23 Feb 2014, 15:57
by Excale
Sur le principe de l'algo, je ne vois pas de problème. Dans le détail, j’espère que ça n'est pas un problème de Python qui fait que ça ne marche pas.

Code: Select all
for i, newDigits in enumerate(primedigits[nextindex:]):

i commence bien à nextindex et finit à len(primedigits)?
(j'y connais rien en serpents)

Et puis j'ai rien compris à enumerate, mais c'est sans doute de ma faute ça :P.

Re: Aide pour un algo récursif

Unread postPosted: 23 Feb 2014, 19:11
by Bisam
En fait, "enumerate" permet de nommer à la fois l'indice (ici "i") et l'élément de la liste (ici "newDigits") et le "primedigits[nextindex:]", c'est la liste "primedigits" mais commencée à l'indice "nextindex"...

Mais ça me fait penser qu'il y a bien un truc qui foire dans ce code !! En effet, "enumerate" compte à partir de 0, par défaut... et le fait que je modifie la liste n'y change rien ! Par conséquent, je ne renvoie pas le bon indice quand je fais ma récursion, puisque je renvoie l'indice à partir du rang "nextindex"... et non à partir du début.

Bon, j'aurais tendance à dire que, du coup, j'en fais plus que nécessaire, et non pas moins... mais peut-être bien que je fais des doublons.
En tout cas une erreur est une erreur, donc, je vais déjà corriger ça et après on verra !

Merci pour avoir involontairement relevé ce problème !

Re: Aide pour un algo récursif

Unread postPosted: 23 Feb 2014, 19:54
by Bisam
Et YESS ! Merci Excale....

Re: Aide pour un algo récursif

Unread postPosted: 23 Feb 2014, 21:00
by Excale
Bisam wrote:Mais ça me fait penser qu'il y a bien un truc qui foire dans ce code !! En effet, "enumerate" compte à partir de 0, par défaut... et le fait que je modifie la liste n'y change rien ! Par conséquent, je ne renvoie pas le bon indice quand je fais ma récursion, puisque je renvoie l'indice à partir du rang "nextindex"... et non à partir du début.

Ben voilà pourquoi ça correspondait pas avec la doc et que je ne comprenais rien!

Bisam wrote:Et YESS ! Merci Excale....

;)

Comme quoi, c'est parfois vicieux le Python. T'as plus qu'à donner ça à tes élèves en TD :D.