IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Caml Discussion :

Arbres d'expressions arithmétiques


Sujet :

Caml

  1. #1
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut Arbres d'expressions arithmétiques
    Bonjour,

    Je cherche à programmer une fonction qui prenne en argument 2 ensembles, l'un étant des nombres (réels), l'autre des opérations (supposées binaires pour l'instant, par exemple +.,-.,*.), et qui rende l'ensemble des résultats différents qui peuvent être construits en utilisant une et une seule fois chaque nombre (dans l'ordre que l'on veut), et les opérations autant de fois que l'on veut.
    Pour ce faire, je me suis dit qu'un algorithme de base était de lister toutes les possibilités, pour ensuite les évaluer, enlever tous les doublons, bref, travailler sur l'ensemble trouvé. Appelons liste_des_arbres la fonction qui prend en argument l'ensemble E des scalaires et l'ensemble F des lois de compositions internes, et qui rend la liste de toutes les formules qu'il est possible de faire, avec les contraintes énoncées plus haut.
    La représentation que j'ai choisi pour les formules est celle d'arbre binaire (les noeuds seront les lois de composition, et les feuilles seront les scalaires).
    La première étape est donc de lister toutes les possibilités.

    (Remarque : j'avais déjà posé cette question il y a quelque temps, mais mon algorithme et ma programmation étaient incorrects, je préfère donc tout reprendre depuis le début, et suis ouvert à toute idée).

    Il y a encore une autre contrainte, mais on pourra y réfléchir plus tard (faisons le peu à peu, sinon ça risque de devenir compliqué) : on a le droit de passer par des réels, mais le résultat final doit être entier.

    Merci de m'aider à faire liste_des_arbres, dans un premier temps.

  2. #2
    Membre expérimenté Avatar de Steki-kun
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    222
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 222
    Par défaut
    Donc si je comprends bien, c'est le problème des chiffres dans "les Chiffres et les Lettres" mais avec des réels et pour faire un résultat entier c'est bien ça ? :-)

    Edit: (question subsidiaire) qu'appelles-tu des "doublons" exactement ? Est-ce que c'est n'importe quelle paire d'arbres qui donne le même résultat ? Ou est-ce que ce sont les arbres qui représentent le "même calcul" ? Par exemple "3*(2*4)" et "(3*2)*4".

  3. #3
    LLB
    LLB est déconnecté
    Membre émérite
    Inscrit en
    Mars 2002
    Messages
    968
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 968

  4. #4
    Expert confirmé
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Par défaut
    Citation Envoyé par elishac Voir le message
    Il y a encore une autre contrainte, mais on pourra y réfléchir plus tard (faisons le peu à peu, sinon ça risque de devenir compliqué) : on a le droit de passer par des réels, mais le résultat final doit être entier.
    Plutôt que des réels, tu veux des rationnels, ainsi tes calculs seront exact, tu ne manqueras pas de solutions à cause d'une imprécision dans les calculs et tu n'auras pas à te casser la tête avec des epsilons. En fait tu peux faire tout les calculs avec des rationnels, ça sera le plus simple.

    --
    Jedaï

  5. #5
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Oui ce sont des rationnels si l'on veut (mais je ne sais pas si c'est le plus simple à représenter en CaML, alors que les flottants ça marche bien).


    Steki-kun et LLB, ça ressemble un peu à des chiffres et des lettres, mais comme Steki-kun l'a dit, on peut passer par des nombres non entiers.
    De plus, contrairement à des chiffres et des lettres, on doit utiliser au moins une fois (et au plus une fois) chaque chiffre (en revanche, on utilise les opérations que l'on souhaite).
    La question subsidiaire : Le but est le résultat, même si l'on passe par des arbres. Par conséquent, si deux arbres donnent le même résultat, on ne gardera que l'un des deux (mais bon, ça on le fera dans un deuxième temps, la première étape est déjà de lister tous les abres possibles, sans doute en faisant une permutation des feuilles et en décorant de toutes les manières possibles les noeuds, de manière récursive. Par ailleurs, supprimer les arbres qui donnent le même résultat n'est pas difficile : on crée très facilement de manière récursive une fonction eval qui rend l'évaluation de l'arbre (c'est ici qu'interviendront les réels ou les rationnels), et on ajoute le résultat de l'évaluation à une liste liste_resultat, pour peu que liste_resultat ne contienne pas encore le résultat qu'on s'apprête à ajouter).
    En conclusion 3*(2*4) et (3*2)*4 n'est compté qu'une seule fois, si c'est le résultat final (par contre, il me semble très difficile de supprimer ce genre de doublons en plein milieu de l'arbre).
    En revanche, dans un second temps, on pourra essayer d'améliorer l'algorithme en faisant un test sur l'opérateur : s'il est commutatif, on ne fera les calculs que sur un sous-arbre (par exemple le sous-arbre gauche), puisque par symétrie le sous-arbre droit sera fait aussi.

    Jedai, je ne suis pas sûr que les réels soient durs à traiter en CaML (on utilise des flottants). Mais bon, si tu veux on pourra faire des rationnels.
    De toute façon, on ne réfléchira à ça que plus tard, pour la fonction eval que j'ai cité plus haut.

    La première étape est de lister tous les arbres possibles avec les contraintes énoncées dans le premier message.

  6. #6
    LLB
    LLB est déconnecté
    Membre émérite
    Inscrit en
    Mars 2002
    Messages
    968
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 968
    Par défaut
    Citation Envoyé par elishac Voir le message
    Oui ce sont des rationnels si l'on veut (mais je ne sais pas si c'est le plus simple à représenter en CaML, alors que les flottants ça marche bien).
    Utilise le type Num. Il gère parfaitement les rationnels (en précision arbitraire).

    Citation Envoyé par elishac Voir le message
    Steki-kun et LLB, ça ressemble un peu à des chiffres et des lettres, mais comme Steki-kun l'a dit, on peut passer par des nombres non entiers. De plus, contrairement à des chiffres et des lettres, on doit utiliser au moins une fois (et au plus une fois) chaque chiffre (en revanche, on utilise les opérations que l'on souhaite).
    La logique reste la même. C'est un exercice classique, regarde sur le net. Il y a plein d'explications.

  7. #7
    Membre expérimenté Avatar de Steki-kun
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    222
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 222
    Par défaut
    Hmm, deux choses : déjà si tu veux juste calculer les résultats possibles tu n'es pas obligé de passer par la génération de tous les arbres (est-ce que pour chaque résultat, tu veux vraiment un calcul permettant de l'obtenir ou pas?).

    Aussi, pour enlever les doublons, tu ferais bien de pas garder ça dans une liste mais plutôt dans une Hashtbl, parce que quand tu as n réels différents au départ et k opérations, le nombre d'arbres différents va grossir assez vite (il y a C(n-1) différents arbres binaires à n feuilles, où C(n-1) est le (n-1)ème nombre de Catalan ; il y a n! façons de mettre tes n nombres dans tes n feuilles, et il y a k^(n-1) manière de mettre des opérations sur les noeuds internes de chaque arbre.... => ça fait k^(n-1)*(2n-2)!/(n-1)! arbres différents sauf erreur de ma part, et ça grossit en (4k(n-1)/e)^(n-1) --- fin de la parenthèse mathématique ).

  8. #8
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    (ouverture de la parenthèse mathématique : je suis d'accord pour ton décompte du nombre d'arbres, et pour ta simplification en k^(n-1)*(2n-2)!/(n-1)! . En revanche, je ne comprends pas ton équivalent (notamment je ne comprends pas où est passée la racine carrée de la formule de Stirling).
    fin de la parenthèse mathématique)

    Je ne sais pas ce qu'est le type Num, ni Hashtbl, mais je veux bien vous croire qu'ils sont efficaces.
    Le type Num n'a pas besoin de m'être expliqué pour l'instant, car il ne servira que dans un deuxième temps (lorsqu'on cherchera à évaluer les arbres, la première étape étant de les former).
    Par contre le hashtbl est peut-être important dès maintenant, merci de m'expliquer en quoi ça consiste.
    De toute facon, on n'est pas obligé de garder tous les arbres dans une liste.
    On les évalue en cours de route (on modifiera dans un second temps le programme pour qu'il fasse cela en cours d'éxecution et pas à la fin), et on ajoute le résultat de l'arbre seulement si le résultat n'est pas déjà présent dans la liste (pour répondre à ta première question, Steki-kun, on peut donc si on veut rendre une liste de couples, où le premier élément est l'entier obtenu et le deuxième est l'arbre (ou plus précisément, l'un des arbres) qui a permis de l'obtenir (ce n'est pas ça qui augmentera beaucoup le temps d'éxecution de l'algorithme, et sa pourrait être utile) (évidemment, on ne va pas chercher à écrire tous les arbres qui ont permis d'avoir le même nombre, ne serait-ce que parce que beaucoup d'arbres seront identiques d'après nos simplifications arithmétiques usuelles (commutativité, associativité...). Par ailleurs, on pourra à la fin chercher à améliorer la complexité en considérant ce genre de choses).

    Peut-on à présent commencer à s'intéresser à l'écriture en elle-même de l'algorithme ? (je pense que tout le monde à compris la question à présent).

  9. #9
    Membre expérimenté Avatar de Steki-kun
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    222
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 222
    Par défaut
    En effet ce qui m'intéressait était un grand O (ou plutôt un grand Theta) mais pas un équivalent, j''ai donc jarreté le coef multiplicateur du terme dominant, qui devait être 4^(-3/4) ou qque chose comme ça.

    En ce qui concerne Hashtbl, c'est le nom du module qui implémente les tables de hachage en OCaml. Si tu ne sais pas ce que c'est, disons pour faire simple que c'est un moyen plus efficace de réaliser une table d'association que de le faire avec une liste de couples (accès en temps constant si tt se passe bien, contre accès en O(taille de la liste)). Je rédige en ce moment un tutoriel sur ces modules mais en attendant je t'encourage à regarder http://fr.wikipedia.org/wiki/Tableau_associatif.

    J'ai bien compris que tu ne vas garder tous les arbres, mais seulement tous les résultats différents, et qu'il n'y en aura pas autant que mon dénombrement pourrait laisser croire, mais je fais 2 constats :
    1/ s'il y a bcp d'arbres, il y aura quand même un nombre certain de résultats différents (même 10% de tout ça, ça fait beaucoup), d'autant que comparé au problème de la télé, ici toutes les divisions sont correctes (sur les entiers, elles ne le sont que rarement)
    2/ c'est surtout que quoi qu'il advienne, le nombre d'arbres c'est aussi le nombre de fois que tu vas faire ta petite recherche dans ton tableau associatif pour savoir s'il faut ajouter ce résultat ou pas, donc ça fera une différence notable in fine si cette recherche se fait en temps constant ou en O(nombre de résultats déjà trouvés)

    Pour l'algorithme, que dire de plus que ce qui est dit dans les pages renvoyées par la recherche de LLB ?

  10. #10
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Je pense qu'on peut faire à peu près 80% des entiers entre 0 et le plus grand nombre faisable possible (évidemment, ce n'est qu'une moyenne, car il y a énormément de paramètres : le nombre d'opérations, mais aussi si les nombres sont grands ou pas, etc.). Par exemple, avec {+,-,*,/} et {1,3,4,6}, le plus grand nombre faisable est 6*4*(3+1)=96. En gros on peut donc faire 80 nombres (en fait, c'est surtout cet exemple là qui m'intéresse dans ma programmation, mais tant qu'à faire, autant généraliser).
    80 n'est pas si énorme (s'il y a des problèmes d'affichages, on peut toujours créer d'autres fonctions qui gèrent cela, entre autre une fonction qui donne le nombre d'éléments de la liste, à savoir combien de nombre différents il est possible de former).

    De toute façon, concrètement, on n'appliquera pas cette fonction à beaucoup de nombres et d'opérateurs.
    Une autre application pourra ensuite être de chercher juste si un nombre peut être construit avec les données initiales, et de s'arrêter en rendant le résultat dès que c'est fait (et éventuellement, par exemple, garder les résultats des nombres qui lui sont à une distance inférieure à d, d étant donné. Exemple : avec {+,-,*,/} et {1,3,4,6}, on cherche jusqu'à trouver 24. Si on le trouve, on s'arrête et on rend l'arbre, sinon, on continue. Si on tombe sur des nombres entre 20 et 28 (d=4), on ajoute les résultats à une liste, mais on continue de chercher).

    Pour l'algorithme, je préférerais qu'on m'aide à le faire, qu'on élabore un algorithme et une réflexion mathématique et informatique ensemble, plutôt que de lire un résultat tout fait, ce qui n'a aucun intérêt.
    Le but est ici de découvrir comment fonctionnent les arbres binaires (je débute en informatique, et en caml).

  11. #11
    Membre Expert
    Avatar de InOCamlWeTrust
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    1 036
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 036
    Par défaut
    Si tu te contentes de tous les arbres possibles au sens large, sans compter ceux qui sont "pareils" à la permutation près, ça ne devrait pas être trop compliqué.

    Voici un bout de raisonnement : il te permettra d'acquérir la façon dont on raisonne en fonctionnel.

    Supposons que tu aies un certains ensemble d'arbres (une liste notée l), qui contienne tous les arbres réalisés jusqu'à alors, de hauteur au plus n. Tu pourras créer tous les arbres faisables d'au plus hauteur (n + 1) en parcourant l : pour chaque arbre a et b de l, tu construis l'arbre a + a, a * a et a - a, a + b, b + a, b - a, etc... Bien-sûr, il ne faudra pas oublier d'ajouter le nouvel ensemble d'arbres à la liste initiale l pour obtenir tous ceux de hauteur au plus (n + 1). Je te laisse trouver la liste initiale...

  12. #12
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Je ne comprends pas ton algorithme.
    N'oublie pas qu'on ne peut utiliser chaque nombre qu'une seule fois.
    Ainsi, il n'est pas toujours possible de faire a+a, je crois...

  13. #13
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Peut-être aurez-vous plus de facilités à répondre à la question suivante, qui est typique, mais sur laquelle je bloque un peu :
    comment construire les C(n-1) arbres binaires à n noeuds, ayant l'étiquette de n noeuds dans une liste l ?

  14. #14
    Membre expérimenté Avatar de Steki-kun
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    222
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 222
    Par défaut
    @ IOCWT : elishac ne s'intéresse pas aux arbres d'une certaine hauteur, mais aux arbres d'une certaine taille, la méthode que tu lui suggères ne le ménera pas très loin.

    @elishac : Il y a C(n-1) arbres binaires à n feuilles et non à n noeuds. Ensuite, je vois pas bien l'intérêt de ne pas garder les étiquettes des noeuds sur les noeuds, mais dans une liste à part ? Enfin les algos pour générer tous ces arbres binaires sont connus (cf en particulier 7.2.1.6 dans The Art of Computer Programming) mais ici ça me paraît une très mauvaise idée : le dénombrement effectué plus haut montre qu'il y a un "triple" problème combinatoire ici 1/ générer les C(n-1) arbres binaires à n-1 noeuds 2/ décorer ces noeuds de toutes les façons possibles 3/ faire toutes les permutations possibles des entiers de départ dans les feuilles.

    Alors oui, tu peux générer tous les arbres, les décorer de toutes les manières, générer toutes les permutations de manière séparée (en particulier tu trouveras tous ces types d'algos dans la référence sus-citée) mais c'est vraiment très bourrin vu la nature du problème. Tu as meilleur temps, étant donné un ensemble d'entiers donné, de :
    • si l'ensemble est un singleton, on a terminé
    • créer toutes les paires possibles avec ces nombres
    • pour chacune de ces paires, réaliser toutes les opérations possibles ayant du sens
    • pour chacune de ces opérations, construire un nouvel ensemble d'entiers qui contient le résultat de l'opération à la place des opérandes
    • pour chaque nouveau problème ainsi obtenu, recommencer récursivement à l'étape 1

    Au passage, pour chaque nombre, tu peux garder avec lui la manière dont il a été obtenu, ainsi quand tu as utilisé tous les nombres, le seul qui reste contient "l'arbre de calcul" qui permet de l'obtenir.

    Si tu veux prendre en compte la commutativité, tu remplaces "paires" par "sous-ensembles de taille 2" dans l'étape 3 et tu traites spécialement la division et la soustraction, et en plus c'est plus rapide de générer ça que de générer les paires.

    Si avec ça tu t'en sors pas..

  15. #15
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Ta méthode me semble bien, je vais essayer de résoudre le problème ainsi.
    Je me permets quand même de préciser quelque chose que j'ai compris, mais que peut-être d'autres n'ont pas compris : ce que tu appelles "paire", en maths c'est en fait un couple ((a,b)<>(b,a)), tandis qu'un "sous-ensemble de taille 2" est en fait une paire (c'est le contraire de ce que tu as dit, c'est pour cela que je me permets de préciser).

    Création de la fonction couples qui a une liste associe la liste de tous les couples faisables avec les éléments de cette liste (il y en a n(n-1)).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #let rec couples=function
    	|[]->[]
    	|a::q->
    		let rec creer_couple a=function 
    			|[]->[]
    			|b::q->(a,b)::(b,a)::(creer_couple a q)
    		in
    		(creer_couple a q)@(couples q);;
    couples : 'a list -> ('a * 'a) list = <fun>
    #couples [1;2;3];;
    - : (int * int) list = [ (1, 2) ; (2, 1) ; (1, 3) ; (3, 1) ; (2, 3) ; (3, 2) ]
    
    Explications sur la fonction creer_couple :
    #creer_couple : 'a -> 'a list -> ('a * 'a) list = <fun>
    #creer_couple 1 [2;3;4];;
    - : (int * int) list = [  (1, 2) ; (2, 1) ; (1, 3) ; (3, 1) ; (1, 4) ; (4, 1)  ]
    La fonction couples est alors simplement la fonction creer_couple à 
    appliquer sur chaque élément de la liste donnée en argument.
    De même, la fonction paires, qui renvoie toutes les paires faisables (pour l'étude de la commutativité) (n(n-1)/2 paires):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    let rec paires=function
    	|[]->[]
    	|a::q->
    		let rec creer_paire a=function 
    			|[]->[]
    			|b::q->(a,b)::(creer_paire a q)
    		in
    		(creer_paire a q)@(paires q);;

  16. #16
    Membre expérimenté Avatar de Steki-kun
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    222
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 222
    Par défaut
    Oui, oui tu as raison pour paire/couple, c'est qu'à force d'utiliser des anglicismes toute la journée on en perd son latin (dans ma vie de computer scientist de ts les jours, "pair" désigne un tuple de taille 2, donc est ordonné).

    Sinon, moi j'écrirais ta fonction creer_paires comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      List.rev_map (fun b -> (a,b)) q
    ou pareil pour couples, en adaptant avec un fold_left :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      List.fold_left (fun acc b -> (a,b)::(b,a)::acc) [] q
    vu que tu te moques bien de savoir dans quel ordre relatif elles sont construites, et que comme ça tes fonctions sont tail-recursives.

    Pareil pour la fonction paires/couples d'ailleurs (en inlinant creer_couples ici) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    let couples =
      let rec couples acc = function
        [] -> acc
       | a::q -> couples (List.fold_left (fun acc b -> (a,b)::(b,a)::acc) acc q) q
       in couples []

  17. #17
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    C'est vrai que c'est beaucoup plus concis, mais je ne connais pas tout ce verbiage (je ne connais que le caml light, avec les fonctions vraiment de base (map est deja presque trop evolué pour moi), car je presente des concours en fin d'année, et dans ce cadre on ne peut utiliser des fonctions trop évoluées).
    Je préfererais donc que vous m'aidiez avec ce que je connais, et ce qui est à mon programme (ceci étant dit, si vous pouviez expliquer en 2 mots ce que vous avez fait, ce n'est pas de refus).

  18. #18
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Je me lance dans la création de la fonction qui fait ce que je souhaite :

    il est probable que ce ne soit pas parfait, merci de vous donner la peine de le lire quand même. C'est un premier jet, et la fonction pourra de toute façon être améliorée suivant nos remarques précédentes.

    Algorithme pour l'obtention de ce qu'on désire :
    On considère un ensemble E de flottants, et on suppose pour l'instant que les 4 opérations sont +,-,*,/.

    Avant de commencer, on crée une fonction à part, (operandes (a,b)), qui prend en argument un couple et renvoie la liste de toutes les operations possibles ayant du sens entre a et b (cette fonction dépendra donc des opérations choisies, il faudra la modifier en conséquence pour le cas général).

    Voici alors l'algorithme :
    1. si l'ensemble est vide, on a terminé
    2. sinon on crée la liste de tous les couples de E, avec la fonction (couples E).
    3. Pour chacun de ces couples (i.e. on considere le premier couple de la liste couples E, de maniere recursive), on commence par créer une liste tmp contenant E moins 2 éléments du couple considéré, à l'aide de la fonction (sup_de_la_liste E (a,b) ). Il faudra créer cette fonction.
    4. On crée alors la liste de toutes les opérations possibles entre a et b, à l'aide de la fonction operandes (a,b).
    5. Pour chacune de ces opérations, on ajoute le résultat à la liste tmp, et on est ainsi ramené à un problème de taille n-1 (retour à l'étape 1).


    Création des fonctions auxiliaires :
    -création de la fonction sup_de_la_liste e (a,b).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    let rec sup_de_la_liste e (a,b)= match e with
    	|[]->[]
    	|c::q-> if a=c or b=c then sup_de_la_liste q (a,b) 
                             else c::(sup_de_la_liste q (a,b));;
    -création de la fonction operandes (a,b).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    let operandes (a,b)=
    	if a=0. then [a;b;-.b] 
    	else if b=0. then [b;a;-.a] 
    	else [a+.b;a-.b;b-.a;a*.b;a/.b;b/.a];;

    Création de la procédure générale, appelée solution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    let rec solution e=match (couples e) with
    	|[]->[]
    	|(a,b)::q->
    		let tmp=sup_de_la_liste e (a,b) in
    			f  operandes (a,b) where rec f=function
    				|[]->[]
    				|c::q->(solution (c::tmp))@(f q);;
    f existe sans doute dans CaML sous un autre nom que je ne connais pas.
    Par ailleurs, il y a une petite erreur d'initialisation car elle rend toujours la liste vide.

  19. #19
    Membre expérimenté Avatar de Steki-kun
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    222
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 222
    Par défaut
    Salut je t'expliquerai mes fonctions précédentes d'ici ce we (là j'ai un peu de taf quand même), elles sont tout à fait faisables en caml light moyennant changement de nom, mais pour l'instant qques remarques sur les fonctions que tu proposes ici :

    Citation Envoyé par elishac Voir le message
    Création des fonctions auxiliaires :
    -création de la fonction sup_de_la_liste e (a,b).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    let rec sup_de_la_liste e (a,b)= match e with
    	|[]->[]
    	|c::q-> if a=c or b=c then sup_de_la_liste q (a,b) 
                             else c::(sup_de_la_liste q (a,b));;
    Cette fonction ne fait pas ce que tu veux. Elle supprime toutes les occurrences de a et b de la liste, alors que tu ne veux supprimer qu'une seule fois a et une seule fois b (ce qui peut vouloir dire deux occurrences du même flottant si a = b). Tu peux soit faire une fonction remove "standard" et faire remove a (remove b l), soit changer ta fonction pour supprimer a et b en une seule passe mais je suis pas sûr que ça en vaille la peine.

    Citation Envoyé par elishac Voir le message
    -création de la fonction operandes (a,b).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    let operandes (a,b)=
    	if a=0. then [a;b;-.b] 
    	else if b=0. then [b;a;-.a] 
    	else [a+.b;a-.b;b-.a;a*.b;a/.b;b/.a];;
    Tu t'autorises les réels négatifs etc etc ? Sinon tu voudrais probablement tester si a = b et ne pas faire certaines opérations. Ou encore si a = 1 ou b = 1 auquel cas les multiplications et divisions sont inutiles.

    Dans tous les cas, il serait bien que tu gardes en plus du calcul une "trace" de ce calcul, comme expliqué plus haut.

    Citation Envoyé par elishac Voir le message
    Création de la procédure générale, appelée solution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    let rec solution e=match (couples e) with
    	|[]->[]
    	|(a,b)::q->
    		let tmp=sup_de_la_liste e (a,b) in
    			f  operandes (a,b) where rec f=function
    				|[]->[]
    				|c::q->(solution (c::tmp))@(f q);;
    f existe sans doute dans CaML sous un autre nom que je ne connais pas.
    Par ailleurs, il y a une petite erreur d'initialisation car elle rend toujours la liste vide.
    Dans caml light, ta fonction f correspond à une utilisatino de flat_map je pense
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (flat_map (fun c -> solution (c::tmp)) (operandes (a,b)))
    . Evite le "... where ..." aussi, ça fait des trucs illisibles De plus tu ne te rappelles jamais sur la suite de la liste de couples dans la fonction solution, tu veux probablement concaténer un solution q à la fin.

  20. #20
    Inactif
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 136
    Par défaut
    Tu as raison pour la fonction sup_de_la_liste, je n'avais pas pensé au fait qu'il puisse y avoir plusieurs fois le meme nombre.
    Je modifierai bientot cette fonction, mais je prefere ne pas faire remove b(remove a l), car, bien que la liste ne soit pas très longue (sinon l'algorithme ne rend pas de résultat avant des années), cela la parcourt quand même inutilement 2 fois.

    La fonction operandes est loin d'être definitive.
    Comme résultat final, j'aimerais bien avoir en argument, outre les nombres qui doivent être utilisés, une liste ou un vecteur du genre [Addition;Soustraction;Multiplication;puissance;raccarree;division;concatenation;factorielle...] qui donne à la procédure les opérations qui peuvent être utilisées.
    Mais on n'en est pas encore là, on améliorera après, pour l'instant je veux déjà un truc qui marche avec les 4 opérations usuelles.
    Cependant, j'améliorerai quand meme les cas à traiter pour le cas des 4 opérations usuelles.
    Le résultat final doit etre un entier (positif), et il est vrai que passer par les negatifs n'apporte pas davantage de solutions (en effet, on ne peut obtenir de nombres negatifs a partir de nombres positifs qu'en faisant des soustractions, et le nombre obtenu est alors le meme en valeur absolue, que ce soit a-b ou b-a ; avoir des nombres negatifs n'apporte donc rien de plus ; mon argumentation n'est sans doute pas claire, mais je ne veux pas en écrire des pages, et c'est clair dans ma tete ; en tout cas je pense que j'ai raison).

    Pour l'instant le plus important est que la fonction telle quelle ne marche pas, elle rend toujours la liste vide, il y a une erreur à un moment donné.

    PS: je n'ai pas compris ta derniere phrase.

Discussions similaires

  1. Transformer une expression arithmétique bien parenthésée en un arbre binaire
    Par mohsenuss91 dans le forum Algorithmes et structures de données
    Réponses: 5
    Dernier message: 04/02/2012, 10h06
  2. Expression arithmétique dans un arbre binaire
    Par lixtoon dans le forum Pascal
    Réponses: 2
    Dernier message: 07/02/2011, 17h01
  3. [Free Pascal] Expressions arithmétiques sous forme d'arbre binaire
    Par Leikka dans le forum Free Pascal
    Réponses: 2
    Dernier message: 17/05/2010, 10h55
  4. Réponses: 1
    Dernier message: 03/01/2007, 15h07
  5. Réponses: 1
    Dernier message: 09/12/2006, 10h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo