(* cf polycopié *)
let tri_shell a =
let h = ref 1 in
while !h <= vect_length a - 1 do
h := 3 * !h + 1
done;
while !h > 1 do
h := !h / 3;
for i = !h to vect_length a - 1 do
if a.(i) < a.(i - !h) then begin
let v = a.(i) in
let j = ref i in
while !j >= !h && a.(!j - !h) > v do
a.(!j) <- a.(!j - !h);
j := !j - !h
done;
a.(!j) <- v
end
done
done;;
Nombre moyen et nombre pire de comparaisons? On peut montrer qu'il en fait moins que O(N^{3/2}). Pour les séquences 2^p3^q, Vaughan Pratt a montré que N (log N)^2 suffisent.
Mais on ne connait pas de borne inférieure. C'est un pb ouvert!
Pour trouver des bornes sup sur le coût, il suffit d'exhiber un algorithme. Pour trouver des bornes inf, il faut un argument principalement combinatoire. Pour trouver les coûts moyens, on passe souvent par l'analyse complexe, fonctions génératrices, etc (cf Analysis of Algorithms, Sedgewick et Flajolet; ou cours de DEA).
Il y a bien d'autres tris (cf Knuth, vol 3; Searching and Sorting). Si on connait la distribution des valeurs et qu'elles ne soient pas très grandes, on peut arriver à trier en temps linéaire.
(cf. le TD1 de Bruno Salvy en
http://www.polytechnique.fr/poly/~salvy)
Tri distribution, tri des mécanographes (Straight Radix Sort). Enfin, si toutes les données ne tiennent pas dans la mémoire vive d'un ordinateur (c'est de moins en moins le cas), il y a des méthodes sophistiquées de tri externe.
Tri distribution:
let tri_distribution a =
let count = make_vect max_value 0
and b = make_vect (vect_length a) 0 in
begin
for j = 0 to max_value -1 do count.(j) <- 0 done;
for i = 0 to vect_length a - 1 do
count.(a.(i)) <- count.(a.(i)) + 1
done;
for j = 1 to max_value -1 do
count.(j) <- count.(j-1) + count.(j)
done;
for i = vect_length a - 1 downto 0 do
count.(a.(i)) <- count.(a.(i)) - 1;
b.(count.(a.(i))) <- a.(i)
done;
for i = 0 to vect_length a - 1 do a.(i) <- b.(i) done;
end ;;
let f x = x ;; let g (x, y) = x ;; let nth (a, n) = a.(n) ;;donnent
f : 'a -> 'a =g : 'a * 'b -> 'a = nth : 'a vect * int -> 'a =
'a (lire "alpha") est une variable de type. On peut donc
utiliser f dans tout contexte où alpha peut être intancié.
Par exemple,
f 2 ;; - : int = 2ou
f "aaaa" ;; - : string = "aaaa"
Pb: quel est le type de := ??
prefix := ;;donne la réponse...
let ... in ...
La construction let est en fait plus générale que déjà vu. On
peut mettre un motif à gauche de =. Les 3 expressions suivantes
qui sont équivalentes
let pat_1 = e_1 and ... pat_n = e_n in e let (pat_1, ... pat_n) = (e_1, ... e_n) in e (function (pat_1, ... pat_n) -> e) (e_1, ... e_n)comportent des motifs patterns (linéaires), c'est à dire des expressions incomplètes avec des variables (de filtrage). Dans un motif, toute variable est considérée comme une variable de filtrage. Un motif est une expression de la forme suivante:
constante variable (pat_1 | pat_2 ... pat_n) (pat_1, pat_2 ... pat_n)Il est interdit d'avoir une variable dans un motif de la 2ème sorte (dont nous verrons l'utilité dans un
match). Une
variable ne peut intervenir qu'une seule fois (linéarité).
Les variables des motifs sont des variables locales avec les mêmes lois de portée que dans le cas (déjà vu) du motif trivial:
let x_1 = e_1 and ... x_n = e_n in e
Exemples:
let x = 3;; let (x, y, z) = (1, 2, 3) in x+y+z;;
match ... with ...
On passe de la conjonction à la disjonction de filtrages avec
match
match e with pat_1 -> e_1 | ... pat_n -> e_n1- On évalue
e qui donne une valeur v. On filtre v par
pat_1. pat_1
avec les valeurs filtrées dans v, et on évalue e_1 dans ce
nouvel environnement. La valeur v_1 de e_1 sera le résultat de
toute l'expression. pat_2, etc, jusqu'à pat_n. Match_failure.
De la même manière, on peut définir une fonction à l'aide d'un filtrage disjonctif
function pat_1 -> e_1 | ... pat_n -> e_ndont le sens est défini par l'équivalence des 2 instructions suivantes
match e with pat_1 -> e_1 | ... pat_n -> e_n (function pat_1 -> e_1 | ... pat_n -> e_n) eLe système signale un filtrage non exhaustif, qui a oublié un cas. C'est un simple avertissement. La constante
_ permet
d'avoir un cas par défaut. Si plusieurs cas se recouvrent, c'est le
premier dans l'ordre d'écriture du programme qui est pris.
let rec définit une variable récursive, ie qui utilise
la (les) valeur(s) en cours de définition. Exemples:
let rec f (x) = if x <= 1 then 1 else f(x-1) + f(x-2) ;; let rec g (x) = if x = 0 then 1 else x * g (x-1) ;; let rec h (x, y) = if x = 0 then 1 else h (x-1, h(x,y)) ;; let rec m (x) = if x > 100 then x - 10 else m (m (x+11)) ;;On peut imprimer leur valeurs pour qques valeurs
begin for i = 1 to 15 do printf "%d " (f (i)) done; printf "\n" end ;;f, g sont les fonctions de Fibonacci et factorielles. C'est la même notation que pour les suites récurrentes. Que valent h et m?
A nouveau, la valeur des arguments est calculée avant de calculer le corps de la fonction appel par valeur. C'est le seul mécanisme d'appel des fonctions. Si on veut modifier la valeur d'un argument, on passe par les références (comme en C):
Exemples:
let incr x = x := !x + 1 ;;
La récursion ne coute pas cher. Il faut l'utiliser, car souvent élégante. La récursion terminale est particulièrement optimisée. C'est lorsque le résultat d'une fonction est un appel récursif. (Caml transforme alors le programme en programme itératif). Exemple:
let f (x) = if even x then
x / 2
else
f (3*x + 1) ;;
dont on conjecture que le résultat vaut toujours 1. Essayez de
calculer qques valeurs!
La récursivité est très puissante. Invention de Kleene
(1935). Théorie des fonctions récursives, comme moyen général de
calcul. On peut montrer qu'il existe toujours des fonctions
récursives partielles (qui ne terminent pas pour tte valeur de leurs
arguments), si on veut un modèle général. La sous théorie des
fonctions récursives primitives correspond aux programmes sans
boucles, sans appels récursifs, et l'instruction for. Ces
programmes terminent toujours. (cf Introduction to Metamathematics,
S. Kleene; Recursive Functions Theory and Logic, A. Yasuhara).
Church et les autres logiciens des années 30 ont montré que
systèmes de Post, les fonctions récursives de Kleene, les machines
de Turing, sont tous équivalentes. Il a émis la thèse suivante:
tous les modèles de la calculabilité sont équivalents.
(cf majeure math/info, cours de Stern pour avoir tous les détails)
Transporter N rondelles de la pile i à la pile j en ne mettant jamais une petite sous une plus grosse, avec (i,j dans {1, 2, 3}).
Si N = 0, ne rien faire. Si N > 0, alors on résoud le pb avec N-1 rondelles de i à la troisième pile k=6-i-j. On prend la grosse rondelle en bas de i et on la met sur j (qui est alors vide). On met alors les N-1 rondelles de k sur i.
Ecrire donc le programme correspondant (en moins de 5 lignes). On réalise alors que la récursion est un mécanisme très général.
Par le Web, on trouve la description complète
ici.
Sans le Web, on peut lire le fichier
d'interface graphics.mli (ML interface)
du module graphique de votre système.
value open_graph: string -> unit value close_graph: unit -> unit value clear_graph : unit -> unit value size_x : unit -> int value size_y : unit -> int value rgb: int -> int -> int -> color value set_color : color -> unit value black : c value magenta : color value plot : int -> int -> unit value point_color : int -> int -> color value moveto : int -> int -> unit value current_point : unit -> int * int value lineto : int -> int -> unit value draw_arc : int -> int -> int -> int -> int -> int -> unit value draw_ellipse : int -> int -> int -> int -> unit value draw_circle : int -> int -> int -> unit value set_line_width : int -> unit value draw_char : char -> unit value draw_string : string -> unit value set_font : string -> unit value set_text_size : int -> unit value text_size : string -> int * int value fill_rect : int -> int -> int -> int -> unitAu début du programme, on déclare l'utilisation de la librairie graphique par la directive:
#open "graphics" ;;
Gaston Julia et Benoît Mandelbrot (x56?, IBM) en sont les pères fondateurs.
Le flocon de von Koch
[1]
La courbe du dragon
[1,
2]
La courbe de Hilbert
[1]
La courbe de Sierpinsky
[1
2
3
4]
Les fougères de Barnsley
[1]
Les systèmes de Lindenmayer (arbres)
[1]
Exemple (le dragon)
let rec dragon n x y z t = if n = 1 then begin moveto x y; lineto z t end else begin let u = (x + z + t - y) / 2 and v = (y + t - z + x) / 2 in dragon (n - 1) x y u v; dragon_bis (n - 1) u v z t end and dragon_bis n x y z t = if n = 1 then begin moveto x y; lineto z t end else begin let u = (x + z - t + y) / 2 and v = (y + t + z - x) / 2 in dragon (n - 1) x y u v; dragon_bis (n - 1) u v z t end;;Attention aux arrondis, entiers contre flottants.