Exercice
Devinez le type des fonctions suivantes et vérifier avec OCaml :
let f x y = x**y
let g x y = x (y + 1)
let h x y z = x (3. *. (y z)) + 1
Puis tester ces fonctions sur des arguments de votre choix.
Solution
let f x y = x**y in
(* x et y doivent être de type float car ** ne fonctionne que sur des float *)
(* donc f est de type float -> float -> float *)
f 0.3 0.1;; (* test *)
let g x y = x (y + 1) in
(* y doit être entier car on utilise + dessus *)
(* x est une fonction car on l'applique sur un argument *)
(* de plus, l'argument de x est de type int car (y + 1) est un int *)
(* donc x : int -> 'a, car il n'y a pas de contrainte sur la valeur de retour de x *)
(* enfin la valeur de retour de g est la même que celle de x *)
(* donc g : (int -> 'a) -> int -> 'a *)
g (fun x -> x) 1;;
let h x y z = x (3. *. (y z)) + 1 in
(* x est une fonction qui prend un float car *. donne une float *)
(* x renvoie un int (car on utilise + sur son image )*)
(* donc x : float -> int *)
(* z n'a aucune contrainte de type : son type est donc 'a *)
(* y est une fonction qui prend un z et renvoie un float (car on utilise *. dessus) *)
(* donc y : 'a -> float *)
(* enfin la valeur de retour de h est de type int *)
(* donc h : (float -> int) -> ('a -> float) -> 'a *)
h (fun a -> 0) (fun x -> 0.5) 1;;
- : float = 0.88656815056521332
- : int = 2
- : int = 1
Tangente hyperbolique¶
Exercice
Définir en OCaml la fonction $\tanh : x \longmapsto \frac{e^x - e^{-x}}{e^x + e^{-x}}$ en utilisant un seul appel à la fonction exp : float -> float
de OCaml.
Solution
let tanh x =
let e = exp x in
(e -. 1./.e)/.(e +. 1./.e);;
tanh 1. (* test *)
val tanh : float -> float = <fun>
- : float = 0.761594155955764851
Géométrie¶
Le type float*float
désigne un couple de flottant, représentant un point p
dans $\mathbb{R}^2$. On peut récupérer les coordonnées de p
avec let x, y = p
. Par exemple :
let p = (2.1, 3.7) in (* exemple de point *)
let x, y = p in (* récupération des coordonnées *)
x;; (* affichage de x *)
- : float = 2.1
Exercice
Écrire une fonction aire_boule : float -> float
telle que aire_boule r
renvoie l'aire d'une boule (disque en dimension 3) de rayon r
, c'est à dire $\frac{4 \pi r^3}{3}$. On pourra utiliser Float.pi
.
Solution
let aire_boule r = 4.*.Float.pi*.r**3./.3. in
aire_boule 1.
- : float = 4.18879020478639053
Exercice
Écrire une fonction distance : float*float -> float*float -> float
telle que distance p1 p2
renvoie la distance euclidienne entre les points p1
et p2
.
Vérifier que la distance entre $(0, 0)$ et $(1, 1)$ est (approximativement) $\sqrt{2}$.
Solution
let distance (x1, y1) (x2, y2) = (* on peut aussi décomposer un couple dans l'argument *)
((x1 -. x2)**2. +. (y1 -. y2)**2.)**0.5;;
distance (0., 0.) (1., 1.)
val distance : float * float -> float * float -> float = <fun>
- : float = 1.41421356237309515
Exercice
Écrire une fonction polaire : float*float -> float*float
qui, étant donné les coordonnées polaires $(r, \theta)$ d'un point, renvoie ses coordonnées cartésiennes $(x, y)$. On rappelle que $x = r\cos(\theta)$ et $y = r\sin(\theta)$. On pourra utiliser cos
et sin
en OCaml.
Solution
let polaire (r, theta) = r*.cos theta, r*.sin theta in
polaire (1., Float.pi/.3.);;
3.**0.5/.2.
- : float * float = (0.500000000000000111, 0.866025403784438597)
- : float = 0.866025403784438597
Exercice
Écrire une fonction milieu : float*float -> float*float -> float*float
telle que milieu p1 p2
renvoie le milieu du segment d'extrémités p1
et p2
.
Solution
let milieu (x1, y1) (x2, y2) =
(x1 +. x2)/.2., (y1 +. y2)/.2. in (* attention : c'est un + et pas - *)
milieu (0., 0.) (1., 1.)
- : float * float = (0.5, 0.5)
Exercice
Écrire une fonction parallelogramme : float*float -> float*float -> float*float -> float*float -> bool
telle que parallelogramme p1 p2 p3 p4
renvoie true
si les points p1
, p2
, p3
, p4
forment un parallélogramme, c'est à dire si les côtés opposés sont (approximativement) de même longueur.
On fera attention à ne pas comparer 2 flottants avec =
, mais regarder à la place si la différence est petite (< 0.001 par exemple).
Solution
(* penser à réutiliser la fonction distance précédente *)
let parallelogramme p1 p2 p3 p4 =
abs_float ((distance p1 p2) -. (distance p3 p4)) < 0.001 &&
abs_float ((distance p1 p4) -. (distance p3 p2)) < 0.001;;
parallelogramme (0., 0.) (1., 0.) (2., 1.) (1., 1.);;
parallelogramme (0., 0.) (1., 0.) (2., 1.) (1.5, 1.);;
val parallelogramme : float * float -> float * float -> float * float -> float * float -> bool = <fun>
- : bool = true
- : bool = false
Congruence¶
Exercice
Le but de cet exercice est de calculer $1357^{2013}$ mod $5$ (mais la méthode utilisée s'applique dans un grande nombre d'exercices mathématiques).
On rappelle que les congruences sont compatibles avec la puissance :
$$a \equiv b [n] \Longrightarrow a^k \equiv b^k [n]$$
- Soit $k$ un entier positif. Que vaut $2^{4k}$ mod $5$? On pourra calculer des valeurs avec OCaml puis le démontrer mathématiquement.
- Que vaut $1357$ mod $5$? En déduire la valeur de $1357^{2013}$ mod $5$.
Solution
- Comme $2^4 = 16 \equiv 1 [5]$, $2^{4k} = (2^4)^k \equiv 1^k [5] \equiv 1 [5]$
- Comme $1357 = 2 [5]$ et $2013 = 503 \times 4 + 1$ (ce qu'on peut trouver avec
/
etmod
en OCaml): $$1357^{2013} \equiv 2^{2013} [5]$$ $$\equiv 2^{503 \times 4 + 1} [5]$$ $$\equiv 2^{503 \times 4} \times 2 [5]$$ $$\equiv 1 \times 2 [5]$$ $$\boxed{1357^{2013} \equiv 2 [5]}$$
Nombres aléatoires¶
Exercice
Random.int n
permet d'obtenir un entier uniformément au hasard entre 0 et n - 1
(chacun de ces entiers a la même probabilité $\frac{1}{n}$ d'être obtenu).
Dans cet exercice, on imagine que l'on possède comme seule source aléatoire un dé à 5 faces (c'est à dire que l'on a seulement le droit d'utiliser Random.int 5
).
- Calculer
(Random.int 5) + (Random.int 5)
plusieurs fois. Est-ce que le résultat vous semble être un entier uniformément au hasard entre 0 et 8? - Quelle est la probabilité d'obtenir
0
avec(Random.int 5) + (Random.int 5)
? D'obtenir1
? Quelle est l'entier qui a le plus de chances d'apparaître? - Comment générer uniformément un entier entre 0 et 24? (C'est à dire passer de 5 possibilités à 25)
- Comment générer uniformément un entier entre 0 et 6? Écrire une fonction pour le faire. On utilisera une méthode par rejection (rejection sampling) : générer dans un espace plus grand et regénérer si la valeur n'est pas dans l'intervalle souhaité.
Solution
- Non, on obtiens plus souvent certains entiers.
- Pour obtenir un 0, il faut faire un 0 sur chaque dé, ce qui a une probabilité $\frac{1}{5}\times \frac{1}{5} = \boxed{\frac{1}{25}}$ d'arriver.
Il y a 25 résultats possibles des 2 dés (mathématiquement le résultat des 2 dés est un élément de $\{0, 1, ..., 4\}\times\{0, 1, ..., 4\}$), et deux permettent d'obtenir la valeur 1 ($0 + 1$ ou $1 + 0$). La probabilité d'obtenir un 1 est donc $\boxed{\frac{2}{25}}$. - Après avoir lancé deux dés et obtenu les résultats $a$ et $b$, on génère l'entier $5\times a + b$ (c'est une écriture en base 5). Comme chaque entier entre 0 et 24 est obtenu de façon unique par un lancé de deux dés $a$, $b$, on a bien un générateur uniforme dans $\{0, 1, ..., 24\}$.
- On peut générer dans $\{0, 1, ..., 24\}$ et regénérer si le résultats n'est pas dans $\{0, ..., 6\}$ :
Solution
let rec rand6 () =
let r = 5*Random.int 5 + Random.int 5 in
if r < 7 then r (* on renvoie le résultat s'il est entre 0 et 6 *)
else rand6 ();; (* sinon on génère à nouveau *)
let count = Array.make 7 0 in
for _ = 0 to 10000 do
let r = rand6 () in
count.(r) <- count.(r) + 1
done;
count (* tire 10000 entiers au hasard pour vérifier que la répartition est uniforme *)
val rand6 : unit -> int = <fun>
- : int array = [|1436; 1418; 1389; 1482; 1452; 1430; 1394|]