Bonsoir,
Un billet à propos d’OCaml, encore. Je me suis amusé à implémenter la construction de l’Ensemble de Cantor (version anglaise un peu plus complète). Vous voulez en savoir plus ?

Première étape, il me fallait une fonction qui à partir d’une liste et d’un couple (a,b) de nombre réels, représentant l’intervalle [a,b], rajoute les deux intervalles qui en sont générés par une itéraction de la construction de l’ensemble de Cantor, c’est à dire les intervalles [a, (2a+b)/3] et [(a+2b)/3, b], à la liste. Voici la fonction en question, appelée cantor_step ici :
let cantor_step l (a,b) = (a, (a +. a +. b) /. 3.) :: ((a +. b +. b) /. 3., b) :: l |
Ensuite, il fallait une fonction qui permettre d’appliquer cette fonction sur une liste de couples (a,b) pour passer d’une liste d’intervalles à une autre, c’est à dire passer à l’étape suivante de la génération de l’Ensemble de Cantor. J’ai par la même occasion profité pour rajouté un paramètre n permettant de réaliser cela n fois. J’ai appelé cette fonction map_n.
let map_n f l n = match l with | [] -> [] | l -> aux l n where rec aux l = function | 0 -> l | k -> aux (List.fold_left f [] l) (k-1) |
List.fold_left est ce que l’on appelle un « pliage » (fold en Anglais). Pour vous familiariser avec ces derniers, une recherche sur « functional programming fold » devrait vous apporter le nécessaire
). Il permet de parcourir la liste d’une certaine façon en appliquant une certaine fonction sur l’élément courant et le résultat de ce qui a été fait jusque là de la même façon, et ainsi de suite.
Enfin, définissons une fonction nth_cantor qui permet d’obtenir la liste des intervalles qui constituent la n-ème itéraction de la construction de l’Ensemble de Cantor.
let nth_cantor = function | 0 -> [(0.,1.)] | k -> map_n cantor_step [(0.,1.)] k |
On voit donc que l’on part de l’intervalle [0,1], et que l’on va répéter k fois l’itéraction.
Deux fonctions utilitaires, dont une qui utilise non pas le module List standard mais le module List de OCaml Batteries Included, pour la fonction print. (en fait, List.fold_left ci-dessus est également celui de OCaml Batteries Included, étant donné que le programme sera compilé avec OCaml Batteries Included).
let couple_printer output (a,b) = Printf.printf "(%f,%f)\n" a b let list_printer l = (* List.print couple_printer stdout l ;*) Printf.printf "\n%d intervals in list\n" (List.length l) |
La première permet d’afficher un couple de réels depuis … un couple de réels.
La seconde permet d’afficher une liste en précisant quelle fonction utiliser pour afficher chaque élément. Elle affiche ensuite la taille. Ici, le passage d’affichage des éléments est commenté (entre (* … *)) car ayant testé mon code avec notamment 25 itérations, on se retrouve avec beaucoup trop de choses à afficher pour que ça soit supportable pour un être humain normal.
Enfin, le code qui est exécuté :
let main () = try let n = int_of_string Sys.argv.(1) in let cn = nth_cantor n in list_printer cn with _ -> print_endline "Use : ./cantor n, where n is an integer" ; exit 0 let _ = main () |
Pour terminer, la compilation :
$ ocamlfind batteries/ocamlopt -o cantor cantor.ml |
(pas d’optimisation ici, si vous les voulez, rajouter -cc -On après ocamlopt)
Et on exécute :
$ time ./cantor 25
33554432 intervals in listreal 0m25.553s
user 0m23.481s
sys 0m2.060s
En activant l’affichage des éléments, sur un petit nombre d’itérations :
$ time ./cantor 4
[(0.740741,0.753086)
; (0.765432,0.777778)
; (0.666667,0.679012)
; (0.691358,0.703704)
; (0.962963,0.975309)
; (0.987654,1.000000)
; (0.888889,0.901235)
; (0.913580,0.925926)
; (0.074074,0.086420)
; (0.098765,0.111111)
; (0.000000,0.012346)
; (0.024691,0.037037)
; (0.296296,0.308642)
; (0.320988,0.333333)
; (0.222222,0.234568)
; (0.246914,0.259259)
]
16 intervals in listreal 0m0.013s
user 0m0.000s
sys 0m0.016s
Vous avez surement remarqué que cette méthode ne préserve pas l’ordre des intervalles… Ce n’a pas vraiment été mon soucis premier, et ce serait arrangé avec la méthode décrite ci-dessous.
Au passage, si j’utilisais des arbres binaires pour toutes les étapes intermédiaires et que je produisais une liste depuis cet arbre, je gagnerais pas mal en efficacité, disons que je vous le laisse à titre d’exercice
Enjoy !
PS : la capture d’écran ci-dessus provient d’un programme que j’avais réalisé il y a quelques mois et qui faisait une construction itérative représentée graphiquement, mais la méthode de génération était moins efficace, et j’ai la flemme de refaire la même chose maintenant…


Commentaires récents