Obtenir une somme fixée

Bonjour,

je suis en difficulté pour écrire un algorithme qui résolve le problème suivant :

J'ai une liste de dix entiers naturels $A = [a_0, ..., a_9]$ et une somme décimale $S$.
Je voudrais énumérer toutes les manières d'obtenir l'égalité :
$$\sum_{k=0}^9 n_k 10^{-p_k} a_k = S$$
où :
- les $p_k$ appartiennent à $\{0,1,2,..., P\}$ (en première approximation $P=4$),
- les $n_k$ sont des entiers naturels dont la somme est raisonnable (disons $\sum_k n_k\leq N$, avec $N$ qui vaut par exemple 5, 6 ou 7, je pense que ça devrait suffire).

Est-ce que ça peut s'écrire en général ?
Par exemple une fonction Python sommeFixe(A,S, P, N)
qui me renvoie la liste des [p_k, n_k] satisfaisants.

A défaut est-ce que ça peut s'écrire pour des cas particuliers (N=7, P=4 par exemple)....?

Je séche pour l'écriture, même pour le cas particulier...

Merci pour toute aide !

PS :
Je sais juste qu'on peut éviter les flottants, travailler partout avec des entiers pour éviter des problèmes !
On multilplie tout par $10^P$, et plus de problème d'arrondi.

Réponses

  • Edit !
    Je viens de m'apercevoir que mon énoncé est incomplet ! (td)

    En effet les $a_k$ peuvent apparaître plusieurs fois, avec des coefficients $10^{-p_k}$ distincts !
  • Tu peux utiliser la classe decimal pour utiliser des nombres décimaux.
    >>> from decimal import Decimal
    >>> Decimal("1.2")
    Decimal('1.2')
    >>> Decimal(1.2)
    Decimal('1.1999999999999999555910790149937383830547332763671875')
    >>> Decimal(1.2+1.4)
    Decimal('2.5999999999999996447286321199499070644378662109375')
    >>> Decimal("1.2+1.4")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]
    >>> Decimal("1.2")+Decimal("1.4")
    Decimal('2.6')
    
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Bonjour

    D'abord, évaluons le nombre de cas.
    • Pour chaque répartition des $n_k$, on a la quantité du point suivant.
      En attendant, on a, ici, $\Gamma_N^{10}=C_{N+9}^{10}$ répartitions. Par exemple, pour N=5, il y a $\Gamma_5^{10} = C_{14}^{10} = C_{14}^{4} = \frac{14*13*12*11}{4*3*2}=1001$ répartitions. Quantité abordable.
    • Pour chaque tirage de valeurs $P_k$, on a la quantité du point suivant.
      En attendant, on a, ici, $(P+1)^{10}$ équipes de valeurs. Par exemple, P=4, on a 5^10=9765625 cas possibles. Ça commence à chauffer.
    • Les $a_k$ sont fixes. Donc un seul cas en tout.

    En conclusion, on a $C_{N+9}^{10}(P+1)^{10}$ sommes possibles. Il suffit de les énumérer comme on les a dénombrées.
    Dans l'exemple, 9775390625 itérations. Presque dix milliards de tours de boucle quand même.
    Ce site est fatigant. Les gens modifient sans cesse leurs messages passés, et on ne comprend plus rien à la discussion. Je suis nostalgique du temps où, si on postait une bêtise, on devait l'assumer. Et si on cite le passage pour l'ancrer, l'administrateur supprime en disant qu'on n'a pas besoin de recopier le message passé.
  • Est-ce que tu as 3 ou 4 jeux de données à proposer ?
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • @lourran

    Euh, non, faudrait construire ça !
    Mon approche est algorithmique (et théorique). Y a-t-il un truc vraiment simplificateur que je rate ?

    @PetitLutin

    et par conséquent, ton approche de la complexité est très intéressante pour moi !
    Ton premier point, avec le coef binomial, m'avait échappé ! Pourtant je connaissais ce dénombrement (avec 10 sortes de crottes en chocolats, combien de ballotins possibles, le ballotin contenant N crottes, c'est bien ça ?)

    Tu proposes l'algoritme exhaustif, je vais essayer de l'écrire,
    même s'il ne peut pas tourner sur de grosses valeurs...
    Je voit mieux à quoi il pourrait ressembler... je crois.

    Merci à tous les deux !

    Non, je cherchais la grosse astuce qui .... lol !

    Ps :
    D'ailleurs, et compte tenu de ma remarque (mon second message)
    je poserais la question différemment maintenant, en indexant sur N et non plus sur 10...
    Je ne sais pas si je suis clair, là...
  • Por moi, l'astuce serait de commencer par les exposants $p_k$ les plus élevés, ou par les plus bas ; je n'arrive pas à me décider !

    Si tu dois reconstiter le nombre 1,2345 à partir d'entiers, avec $p_k$ limité à 4 , il n'y a pas des tonnes de façons d'obtenir le 5 final ; Pour chacune de ces solutions, on va ensuite chercher à obtenir le 4 en avant dernière position etc

    Donc même si l'arbre qu'il faut parcourir est énorme, dans les faits, il y a des tas de branches qui s'avèrent tout de suite inutiles.
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • Oui.
    Donc algorithmiquement parlant, ça ressemble plutôt à de la récursivité ?
    J'ai un peu réfléchi à ça, mais je dois être rouillé sur le sujet (ou je ne l'ai jamais vraiment compris)...
  • Sauf erreur de ma part, il doit être possible de travailler uniquement avec des entiers en multipliant $S$ par une puissance de 10. (Ah, bah, je viens de voir que tu l'avais dit dès le post initial).

    Ensuite, clairement, la récursivité convient bien, et puisque on somme uniquement des entiers positifs, il y a moyen d'élaguer pas mal de branches.
    On doit pouvoir aussi procéder par programmation dynamique, ou simplement par mémoïsation.
  • Indexer sur N et pas sur 10, j'allais te le suggérer.
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • JP nl a écrit:
    (avec 10 sortes de crottes en chocolats, combien de ballotins possibles, le ballotin contenant N crottes, c'est bien ça ?)

    Euh c'est plutôt 10 boîtes et 5 crottes. 3 exemples de répartitions possibles :
    (0 0 0 0 1 0 0 0 2 2)
    (0 1 0 1 0 1 0 1 0 1)
    (5 0 0 0 0 0 0 0 0 0)

    À la relecture, je m'aperçois que ce n'est pas $\Gamma_{N}^{10}$, mais $\displaystyle \sum_{i=0}^{N} \Gamma_{i}^{10}$.
    Ce site est fatigant. Les gens modifient sans cesse leurs messages passés, et on ne comprend plus rien à la discussion. Je suis nostalgique du temps où, si on postait une bêtise, on devait l'assumer. Et si on cite le passage pour l'ancrer, l'administrateur supprime en disant qu'on n'a pas besoin de recopier le message passé.
  • Merci à tous.

    @Bisam
    En commençant avec les grandes puissances :
    Si la somme est trop grande, on élague. Ok

    En partant avec les petites puissances :
    Si la dernière décimale n'est pas la bonne, on élague. Ok.

    Je suis incapable d'imaginer l'écriture récursive. Une bonne occasion de réviser le concept ?
    Quant à la mémoïsation, première fois que je vois ce mot !
    (quand j'ai le temps, je vais regarder sur le net, promis !)

    @PetitLutin
    J'ai dû mal m'exprimer.
    Chez un artisan chocolatier, on va s'acheter un joli petit balotin de N crottes.
    Il propose 10 sortes de chocolat.
    On choisit librement (en dénombrement, répétitions possibles, ordre sans importance) la composition du balotin.
    $\binom{9+N}{N}$ possibilités.
    Exemple :
    CCC I I I I CC | C | | | C I
    modélise le choix de 7 chocolats parmi 10 sortes (numérotées ici de 1 à 10) :
    3 de la sorte 1
    2 de la sorte 5
    1 de la sorte 6
    1 de la sorte 9
  • Nous sommes d'accord ;-).
    Ce site est fatigant. Les gens modifient sans cesse leurs messages passés, et on ne comprend plus rien à la discussion. Je suis nostalgique du temps où, si on postait une bêtise, on devait l'assumer. Et si on cite le passage pour l'ancrer, l'administrateur supprime en disant qu'on n'a pas besoin de recopier le message passé.
  • Voici ce que j'avais en tête :
    def somme_fixe(A: list[int], S: int, P: int, N: int) -> dict:
        """Cherche s'il existe une liste (p_1, ..., p_q) et une liste (n_1,...,n_q)
        d'entiers tels que sum(n_k * a_k * 10^(P-p_k), k=1..q) = S avec les contraintes
        n_1 + ... + n_q <= N et (p_1, ..., p_q) <= P.
        Renvoie un dictionnaire dont les clés sont les tuples (n_1, ..., n_q)
        et les valeurs correspondantes sont (p_1, ..., p_q)."""
    
        if S < 0 or S >= 10**(P+1):
            return {}
        
        if len(A) == 0:
            return {} if S !=0 else {(): ()}
    
        a0 = A[0]
        sols = {}
        for p in range(P+1):
            for n in range(N+1):
                sol_prev = somme_fixe(A[1:], S - n * a0 * 10**(P-p), P, N-n)
                for n_list in sol_prev:
                    sols[(n, *n_list)] = (p, *sol_prev[n_list])
        return sols
    

    Par exemple,
    somme_fixe([3,1,4,1], 3141, 3, 8)
    
    renvoie
    {(1, 1, 1, 1): (0, 3, 2, 1), (1, 2, 3, 1): (0, 2, 2, 3), (1, 1, 3, 2): (0, 3, 2, 2), (2, 3, 2, 1): (2, 0, 2, 3), (2, 1, 2, 3): (2, 3, 2, 0)}
    
  • Merci beaucoup bisam !

    Je vais examiner ça dans le détail.

    C'est tellement court que c'est inquiétant, :)olol !

    Je manque un peu d'énergie et de temps maintenant,
    mais je reviens sous quelques jours !
  • J'ai écrit l'entête pour du Python 3.9 (les spécifications de typage évoluent très rapidement, en ce moment).

    Bien sûr, j'ai modifié un peu la requête initiale en remplaçant la somme S à chercher par S * 10**P.

    J'explique un tout petit peu mon algorithme :
    • Si la somme S n'est pas entre 0 et 10**(P+1), il n'y a pas de solution.
    • Si la liste A est vide, il n'y a pas de solution si S est non nulle. En revanche si S est nulle, le tuple vide () d'entiers n_i et le tuple vide () d'entiers p_i forment l'unique solution.
    • Si la liste A n'est pas vide, on prend son premier élément, un entier n entre 0 et N et un entier p entre 0 et P et pour chaque solution (qui est un couple n_list, p_list) atteignant la somme S - n*A[0]*10**(P-p) à partir de la liste A privée de son premier terme et pour les entiers P et N-n fixés, on fabrique une nouvelle solution en ajoutant n à au début de la liste des n_i et p au début de la liste des p_i.

    Il y avait quelques petites erreurs à la fin que j'ai corrigées ci-dessus :
    • j'avais écrit 10**p au lieu de 10**(P-p), ce qui ne correspondait donc pas à la description
    • j'avais pris n dans range(N) au lieu de range(N+1), donc je ratais des solutions

    Cela devrait être mieux maintenant.
  • En fait, en corrigeant mon précédent message, je me suis rendu compte que mon algorithme ne répondait pas à la demande d'exhaustion des cas car pour une même liste de n_i, il peut y avoir plusieurs listes de p_i (et réciproquement, d'ailleurs).

    Pour y remédier, le moyen le plus simple est de changer la nature de la réponse en une liste de couples de tuples...
    Voici donc la deuxième version :
    def somme_fixe2(A: list[int], S: int, P: int, N: int) -> dict:
        """Cherche s'il existe une liste (p_1, ..., p_q) et une liste (n_1,...,n_q)
        d'entiers tels que sum(n_k * a_k * 10^(P-p_k), k=1..q) = S avec les contraintes
        n_1 + ... + n_q <= N et (p_1, ..., p_q) <= P.
        Renvoie une liste de couples formés des tuples (n_1, ..., n_q)
        et (p_1, ..., p_q)."""
    
        if S < 0 or S >= 10**(P+1):
            return []
    
        if len(A) == 0:
            return [] if S !=0 else [((), ())]
    
        a0 = A[0]
        sols = []
        for p in range(P+1):
            for n in range(N+1):
                sol_prev = somme_fixe2(A[1:], S - n * a0 * 10**(P-p), P, N-n)
                for n_list, p_list in sol_prev:
                    sols.append(((n, *n_list), (p, *p_list)))
        return sols
    

    Ainsi, cette fois-ci :
    somme_fixe2([3,1,4,1], 3141, 3, 8)
    
    renvoie
    [((1, 1, 1, 1), (0, 1, 2, 3)), ((1, 2, 3, 1), (0, 2, 2, 3)), ((1, 1, 1, 1), (0, 3, 2, 1)), ((1, 1, 3, 2), (0, 3, 2, 2)), ((2, 3, 2, 1), (2, 0, 2, 3)), ((2, 1, 2, 3), (2, 3, 2, 0))]
    

    On voit en particulier que le tuple (1,1,1,1) de n_i est associé à deux couples de p_i : (0,1,2,3) et (0,3,2,1), ce qui est logique puisque les a_i correspondants sont égaux.

    La première méthode a l'avantage d'éviter les multiplicités en les écrasant au fur et à mesure, mais elle peut rater des cas.
  • Je pense qu'en fait, la première difficulté, c'est de très bien formuler la question.

    Avec une liste $a_i$ qui serait [3,1,4], est-ce que la solution 3+1/10+4/100+ 1/1000 est acceptée ou pas ?

    Dans la 1ère formulation de l'exercice, on a envie de conclure que non. Chaque $a_i$ doit être utilisé une seule fois, avec un certain coefficient, et une puissance de 10.
    Dans la 2ème piste (le PS de ce message), on a l'impression que finalement, on peut utiliser chaque nombre plusieurs fois , en respectant bien sur la contrainte sur N.

    C'est un peu pour ça que je demandais des exemples.
    Avec le jeu de données [3,1,4] , p=3, N=8, et S=3.141, est que que la solution 3+1/10+4/100+ 1/1000 est acceptée ou pas ?
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • 1) somme_fixe2([111, 3, 14], 111*200 + 3*10 + 14*2, 3, 5)
    2) somme_fixe2([111, 3], 111*101 + 3*10, 3, 4)


    Bonjour et merci à tous les deux.

    Et désolé pour la cafouillage initial, ma formulation en deux étapes jette le trouble.
    la version rectifiée dans mon second post étant celle qui m'intéresse le plus.

    Avec 1), je m'attends à obtenir des solutions avec ma première question.

    Avec 2), je m'attends à obtenir des solutions avec ma seconde question.

    (j'espère ne pas me tromper dans ces exemples).

    Plus de détails sur ces deux exemples si besoin, mais je pense que vous avez bien compris ma recherche.

    Désolé aussi, je manque d'énergie pour aller plus loin ce soir,
    mais je me demande si ton algo, Bisam, ne part du principe que les éléments de la liste A sont < 10. Ce qui n'est pas comme le montre les exemples.
    J'ai essayé de modifier le booléen S >= 10**(P+1) en S >= 10**(P+1) * max(A+[0]) * N, sans effet bénéfique.
    J'y pense en écrivant : sans doute parce que dans un algo récursif, les valeurs de A, N P changent...
  • Effectivement, j'avais majoré au départ la valeur de S... mais ce n'est pas du tout utile, vu la méthode que j'utilise.
    Il suffit d'enlever la condition
    or S >= 10**(P+1)
    
    pour obtenir ce que tu veux pour le premier cas.

    En revanche, pour le second cas, il faut revoir entièrement l'algorithme. En effet, celui-ci prévoit que le nombre d'éléments de la liste A est fixé et la récursion le fait strictement diminuer.
    Si on peut changer le nombre d'éléments de la liste, l'algorithme est caduc.

    On peut peut-être y arriver quand même, mais cela devient plutôt un problème d'arithmétique.
  • Ça se complique sévèrement. À l'heure actuelle, j'opterais pour un algorithme dont le fil rouge serait la baisse du premier chiffre (en partant de S). Si on arrive à zéro en N étapes, c'est gagné.

    Exemple : atteindre 22258 avec [111; 3; 14] demande l'étude de 3 sous-cas : 11158, 19258, et 8258. Et on recommence. Etc.
    Notez que baisser de 14 descend plus vite que baisser de 111. :-)
    Ce site est fatigant. Les gens modifient sans cesse leurs messages passés, et on ne comprend plus rien à la discussion. Je suis nostalgique du temps où, si on postait une bêtise, on devait l'assumer. Et si on cite le passage pour l'ancrer, l'administrateur supprime en disant qu'on n'a pas besoin de recopier le message passé.
  • Maintenant que j'ai une idée générale de l'algorithme
    (en particulier la manière dont on gère la récursivité, ce qui me posait le plus problème),
    je crois que je vais essayer d'être autonome.

    Effectivement, je vais réécrire les choses en essayant, comme le suggère PetitLutin,
    non pas de diminuer la liste A, mais en diminuant N à chaque étape.

    Ce que j'ignore complètement, c'est la capacité en termes de nombre de "branches de récursivité" de Python (ou d'un autre logiciel),
    mais je testerai en diminuant longueur de liste et N.
    Si déjà ça peut marcher comme ça, je serai assez content !!

    Je vous envoie des nouvelles quand je me suis penché là-dessus.
    Encore merci !
  • Par défaut, les versions de Python classiques ont une profondeur de récursion assez élevée.
    Je viens de faire le test avec un script tout bête et j'ai obtenu 989.
    def recur(n):
        try:
            return recur(n+1)
        except:
            return n
    

    Tu lances le test avec "recur(0)" et le nombre renvoyé est la profondeur de récursion.

    Puisque tu voulais te limiter à de petites valeurs de N, et si ton algorithme diminue strictement N à chaque étape, tu n'auras aucun problème... mais si je ne m'abuse, N ne diminue pas strictement...
  • @bisam

    N'as-tu pas plutôt obtenu $1000 - 2 = 998$, par hasard ? Because:
    >>> import sys
    >>> print(sys.getrecursionlimit())
    1000
    
    et ce lien vers SO.
  • Effectivement, la limite de récursion de Python est fixée par défaut à 1000, mais au moment d'un test, elle dépend de l'état de la pile d'exécution au moment du lancement du script. Visiblement, Pyzo ajoute une couche d'exécution d'une dizaine d'étages de profondeur...

    J'ai réussi à m'en sortir tout de même :
    def somme_fixe3(A: list[int], S: int, P: int, N: int) -> dict:
        """Cherche s'il existe une liste (p_1, ..., p_q) et une liste (n_1,...,n_q)
        d'entiers tels que sum(n_k * a_k * 10^(P-p_k), k=1..q) = S avec les contraintes
        n_1 + ... + n_q <= N et (p_1, ..., p_q) <= P,
        en répétant éventuellement les éléments de la liste A.
        Renvoie un ensemble de tuples con tenant des couples qui à chaque valeur de A 
        associent la somme des coefficients n_k*10**(P-p_k) correspondants."""
    
        if S < 0:
            return set()
    
        if N == 0:
            if S !=0:
                return set()
            return {tuple((a, 0) for a in A)}
    
        sols = set()
        for n in range(1, N+1):
            for p in range(P+1):
                multi = n * 10**(P-p)
                for a in [0] + A:
                    sols_prev = somme_fixe3(A, S - a * multi, P, N-n)
                    for sol in sols_prev:
                        d = dict(sol)
                        if a in d:
                            d[a] += multi
                        elif a!= 0:
                            d[a] = multi
                        sols.add(tuple((k, d[k]) for k in d))
        return sols
    

    Cette fois, on renvoie directement un ensemble de solutions (pour éviter de répéter plusieurs fois la même), chacune des solutions étant sous la forme d'un tuple de couples. J'aurais préféré renvoyer un ensemble de dictionnaires, mais les dictionnaires ne sont pas hashables.

    Ainsi, les trois essais
    somme_fixe3([111, 3, 14], 111*200 + 3*10 + 14*2, 3, 5)
    
    somme_fixe3([111, 3], 111*101 + 3*10, 3, 4)
    
    somme_fixe3([11, 3, 14], 473, 3, 7)
    
    renvoient respectivement les 3 résultats
    {((111, 200), (3, 10), (14, 2))}
    
    {((111, 101), (3, 10))}
    
    {((11, 13), (3, 110), (14, 0)), ((11, 10), (3, 121), (14, 0)), ((11, 0), (3, 111), (14, 10)), ((11, 40), (3, 11), (14, 0)), ((11, 43), (3, 0), (14, 0)), ((11, 1), (3, 0), (14, 33)), ((11, 30), (3, 1), (14, 10)), ((11, 3), (3, 100), (14, 10))}
    
    .

    On doit pouvoir faire encore plus efficace avec des résolutions d'équations diophantiennes... mais je n'ai pas encore cherché dans cette voie, car si elle est sûrement plus efficace sur de grandes valeurs de N, elle est bien plus pénible à écrire !
  • Bonsoir.
    J'ai essayé, une dizaine de jours après lecture du code proposé par Bisam,
    de réécrire un code pour vérifier si j'avais compris.

    Je signale que j'essaie de n'utiliser que des outils plus rudimentaire que Bisam (dans son code, les "set", les "tuple" m'échappent).
    Donc je veux construire une liste affichée en cas de succès,
    par exemple pour mon exemple ci-dessous, à savoir : sommeCoef([3, 111, 15], 4, 2, 11100+165)
    j'attends quelque chose comme [ [2, 110], [1, 15], [0, 15] ] car 11100+165 = 111 * 10^2 + 15 * 10 + 15

    Donc voilà ma tentative. Elle me semble "raisonnablement correcte" (j'espère),
    mais ... je sais bien que ma liste n'est initialisée nulle part !
    Donc ceci est un nouvel appel à l'aide :
    - est-ce que je peux m'en sortir avec quelque chose comme ça ?
    - si oui, où initialiser la liste ?
    def sommeCoef(A, N, P, score) :
        if score < 0 :
            return False
        if N >= 0 :
            if score == 0 :
                print(liste)
                return True
            else :
                for elem in A :
                    for k in range(P+1) :
                        test = sommeCoef(A, N-1, k, score - elem * 10**k)
                        if test :
                            liste.append([k, A])
                        return test
    
    sommeCoef([3, 111, 15], 4, 2, 11100+165) 
    
    Merci pour tout coup de main !
  • Voici le code corrigé :
    def sommeCoef(A, N, P, score, liste=None):
        if liste is None:
            liste = []
        if score < 0 :
            return False
        if N >= 0 :
            if score == 0 :
                print(liste)
                return True
            else :
                for elem in A :
                    for k in range(P+1) :
                        test = sommeCoef(A, N-1, k, score - elem * 10**k, liste + [[k,elem]])
                        if test :
                            liste.append([k, elem])
    

    On ajoute un argument "liste" à la fonction qui sera modifié par effet de bord à l'exécution de la fonction.

    Pour ne pas avoir besoin de le spécifier dans l'appel au départ., on donne une valeur par défaut à "liste"... mais une subtilité veut qu'on ne peut pas l'initialiser avec une liste vide car ce serait la même liste utilisée à tous les appels de la fonction puisque l'instanciation de la valeur par défaut se fait au moment de la création de la fonction et non à son exécution. L'astuce consiste à donner une valeur fixe (et impossible à obtenir autrement) par défaut et à tester à l'exécution si la valeur de la liste est cette valeur par défaut. Cela donne les deux premières lignes.

    Ensuite, tu avait une erreur en renvoyant "test" à la fin de ta boucle : je l'ai supprimé. Une autre erreur était que tu mettais "A" au lieu de "elem" dans tes listes.

    Pour finir, il suffit de faire l'appel récursif en adjoignant à la liste le couple [k, elem].


    Le problème de cette méthode c'est que tu fais afficher les différentes solutions mais tu ne peux pas les réutiliser dans une autre fonction car elles ne sont pas renvoyées.
  • Merci beaucoup, Bisam. Et merci aux autres qui m'ont aidé.

    J'ai compris les boulettes,
    le return, le A à remplacer par elem.

    Et je suis heureux de connaître l'astuce ! Je voyais le problème d'initialisation, mais je ne savais pas le résoudre.
    C'est j'imagine une astuce "classique" pour les algorithmes récursifs.

    Pour le print, j'ai bien conscience du problème que tu soulèves.
    Bon, je verrai si je trouve un moyen d'y remédier...
  • Pour comprendre mes messages précédents, petite explication sur les types de base de Python :
    • les 'set' représentent des ensembles, au sens mathématique. On les entoure avec des accolades, comme en maths. Ils ne contiennent que des objets non mutables et par définition ne peuvent contenir qu'un seul exemplaire de chaque objet. Ils possèdent les méthodes usuelles mathématiques sur les ensembles (union, intersection, différence, etc.)
    • les 'tuple' sont des listes non mutables. On les entoure avec des parenthèses au lieu des crochets. Ils peuvent néanmoins contenir des objets mutables.
    • les 'dict' sont des dictionnaires qui à des clés (non mutables) associent des valeurs (mutables ou non). C'est une sorte d'intermédiaire entre une liste (dont les clés seraient les indices) et une fonction (dont les valeurs sont calculées plutôt que stockées).

    Pour en savoir plus, évidemment, suivre un cours basique de Python devrait aider...
  • Puisque tu compares les 'dict' à des fonctions, précisons un peu : un 'dict' peut être vu comme le graphe (ensemble de couples) d'une fonction au sens mathématique.
Connectez-vous ou Inscrivez-vous pour répondre.