-
-
Notifications
You must be signed in to change notification settings - Fork 45
/
public2012_D3 (OCaml).ml
508 lines (336 loc) · 19.4 KB
/
public2012_D3 (OCaml).ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
(*
This OCaml script was exported from a Jupyter notebook
using an open-source software (under the MIT License) written by @Naereen
from https://github.com/Naereen/Jupyter-Notebook-OCaml
This software is still in development, please notify me of a bug at
https://github.com/Naereen/Jupyter-Notebook-OCaml/issues/new if you find one
*)
(* # Table des Matières
* [1. Agrégation externe de mathématiques, texte d’exercice diffusé en 2012](#1.-Agrégation-externe-de-mathématiques,-texte-d’exercice-diffusé-en-2012)
* [1.1 Épreuve de modélisation, option informatique](#1.1-Épreuve-de-modélisation,-option-informatique)
* [1.2 *Proposition* d'implémentation, en [OCaml](https://ocaml.org/)](#1.2-*Proposition*-d'implémentation,-en-[OCaml]%28https://ocaml.org/%29)
* [1.2.1 Pour [l'option informatique (D)](http://www.dit.ens-rennes.fr/agregation-option-d/programme-de-l-option-informatique-de-l-agregation-de-mathematiques-48358.kjsp) de l'[agrégation de mathématiques](http://agreg.org/) (en France).](#1.2.1-Pour-[l'option-informatique-%28D%29]%28http://www.dit.ens-rennes.fr/agregation-option-d/programme-de-l-option-informatique-de-l-agregation-de-mathematiques-48358.kjsp%29-de-l'[agrégation-de-mathématiques]%28http://agreg.org/%29-%28en-France%29.)
* [1.3 Jeux de Nim](#1.3-Jeux-de-Nim)
* [1.3.1 Représentation des configurations](#1.3.1-Représentation-des-configurations)
* [1.3.2 Fonction de Sprague-Grundy pour le jeu de Nim](#1.3.2-Fonction-de-Sprague-Grundy-pour-le-jeu-de-Nim)
* [1.3.3 Déterminer un coup à jouer selon une stratégie gagnante (s'il y en a une)](#1.3.3-Déterminer-un-coup-à-jouer-selon-une-stratégie-gagnante-%28s'il-y-en-a-une%29)
* [1.3.3.1 Stratégie optimale](#1.3.3.1-Stratégie-optimale)
* [1.3.4 Stratégie stupide](#1.3.4-Stratégie-stupide)
* [1.4 Deux exemples, manuellement](#1.4-Deux-exemples,-manuellement)
* [1.4.1 Premier exemple](#1.4.1-Premier-exemple)
* [1.4.2 Second exemple](#1.4.2-Second-exemple)
* [1.5 Un bonus : *simulation du jeu*](#1.5-Un-bonus-:-*simulation-du-jeu*)
* [1.5.1 Simulation de plusieurs étapes](#1.5.1-Simulation-de-plusieurs-étapes)
* [1.5.2 Exemple :](#1.5.2-Exemple-:)
* [1.5.3 Configuration aléatoire](#1.5.3-Configuration-aléatoire)
* [Le joueur *optimal* gagne souvent...](#Le-joueur-*optimal*-gagne-souvent...)
* [Le joueur *stupide* peut quand-même gagner ?](#Le-joueur-*stupide*-peut-quand-même-gagner-?)
* [1.5 Conclusion](#1.5-Conclusion)
* [1.6 Attention](#1.6-Attention-:)
*)
(* # 1. Agrégation externe de mathématiques, texte d’exercice diffusé en 2012 *)
(* ## 1.1 Épreuve de modélisation, option informatique *)
(* > - Ce [notebook Jupyter](http://jupyter.org/), utilisant [OCaml](https://ocaml.org/) (via le [kernel IOcaml](https://github.com/andrewray/iocaml/#installation)), est une correction [non officielle](https://github.com/Naereen/notebooks/tree/master/agreg) d'un texte de modélisation pour l'option informatique de l'agrégation externe de mathématiques.
> - Il s'agit du texte [public2012-D3](http://agreg.org/Textes/public2012-D3.pdf).
> - Cette tentative de correction partielle a été rédigée par [Lilian Besson](http://perso.crans.org/besson/) ([sur GitHub ?](https://github.com/Naereen/), [sur Bitbucket ?](https://bitbucket.org/lbesson)), et [est open-source](https://github.com/Naereen/notebooks/blob/master/agreg/public2012_D3%20%28OCaml%29.ipynb).
> - Une autre version, en [Python 3](https://docs.python.org/3/), [est disponible (aussi sur GitHub)](https://github.com/Naereen/notebooks/blob/master/agreg/public2012_D3.ipynb).
> #### Feedbacks?
> - Vous avez trouvé un bug ? → [Signalez-le moi svp !](https://github.com/Naereen/notebooks/issues/new), merci d'avance.
> - Vous avez une question ? → [Posez la svp !](https://github.com/Naereen/ama.fr) [![Demandez moi n'importe quoi !](https://img.shields.io/badge/Demandez%20moi-n'%20importe%20quoi-1abc9c.svg)](https://GitHub.com/Naereen/ama.fr)
---- *)
(* ## 1.2 *Proposition* d'implémentation, en [OCaml](https://ocaml.org/) *)
(* ### 1.2.1 Pour [l'option informatique (D)](http://www.dit.ens-rennes.fr/agregation-option-d/programme-de-l-option-informatique-de-l-agregation-de-mathematiques-48358.kjsp) de l'[agrégation de mathématiques](http://agreg.org/) (en France). *)
(* **Attention** : ce document ne prétend pas être LA correction du texte, mais **un exemple de solution**.
---- *)
(* Initialisation du [générateur de nombres aléatoires](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Random.html#VALinit) et d'une [fonction généralisée](https://ocaml.org/learn/tutorials/format.html) d'affichage. *)
(* In[1]: *)
Random.self_init ();;
let print = Format.printf;;
(* C'est un très bon réflexe d'utiliser `print = Format.printf` ainsi défini, elle permet d'afficher facilement du texte contenant des paramètres. Pour plus de détails, voir [la documentation de Printf.printf](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Printf.html) et [des explications sur `Format`](https://ocaml.org/learn/tutorials/format.html#Printingtostdoutusingprintf).
Il suffit de retenir que `printf` s'utilise comme ça : *)
(* In[2]: *)
print "Chaine de caractere\n";;
(* In[3]: *)
print "Chaine avec variables : il faut retenir %%i, %%f, %%s et %%c...\n" ;;
print "\n - %%i pour un entier : %i, " 1 ;;
print "\n - %%f pour un flottant : %f, " 3.1415 ;;
print "\n - %%s pour une string : %s, " "OK?" ;;
print "\n - %%c pour un caractère : %c\n" 'c' ;;
(* ## 1.3 Jeux de Nim *)
(* ### 1.3.1 Représentation des configurations *)
(* Pas besoin d'un type de données trop complexe : *)
(* In[4]: *)
type nim = int array;;
(* On va d'abord écrire une fonction toute simple qui affiche une configuration, en mode texte. *)
(* In[5]: *)
let print_nim =
Array.iteri (fun case total -> begin
print "\n%i: " case;
for j = 1 to total do
print "! ";
done;
end);;
(* On peut définir et afficher deux exemples de configuration d'un jeu de Nim, venant de la figure 1. *)
(* In[6]: *)
let a = [| 1; 3; 5 |] ;;
print "\n Configuration (a) de la Figure 1 :";
print_nim a;;
(* In[7]: *)
let b = [| 1; 3; 2 |] ;;
print "\n Configuration (b) de la Figure 1 :";
print_nim b;;
(* ### 1.3.2 Fonction de Sprague-Grundy pour le jeu de Nim *)
(* Elle est donnée par le corollaire 1. en page 6/7 du texte.
On a besoin du **xor** (*"ou exclusif"*, cf [cette page](https://fr.wikipedia.org/wiki/Fonction_OU_exclusif)) **bit à bit**, obtenu en OCaml avec l'opérateur [``lxor``](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#VAL(lxor)) :
$$ \gamma(\mathrm{Nim}(x_1, \dots, x_k)) := \bigoplus_{i=1}^{k} x_i = x_1 \oplus \dots \oplus x_k. $$ *)
(* Petit rappel sur cette fonction **xor** : *)
(* | Entrée | | Entrée | Sortie |
| ----- |:--------:| ----- | ------: |
| False | $\oplus$ | False | = False |
| False | $\oplus$ | True | = True |
| True | $\oplus$ | False | = True |
| True | $\oplus$ | True | = False | *)
(* On peut la définir avec l'opérateur infixe : *)
(* In[8]: *)
let somme_nim = ( lxor ) ;;
(* Ou bien plus simplement : *)
(* In[9]: *)
let somme_nim x y = x lxor y ;;
(* Quelques exemples, juste pour vérifier.
- D'abord, les valeurs pour les deux entiers `0` et `1` : *)
(* In[10]: *)
somme_nim 0 0;; (** = 0 *)
somme_nim 0 1;; (** = 1 *)
somme_nim 1 0;; (** = 1 *)
somme_nim 1 1;; (** = 0 *)
(* - Ensuite, d'autres valeurs, juste pour tester : *)
(* In[39]: *)
somme_nim 3 5;; (** 3 xor 5 = 011_2 xor 101_2 = 111_2 = 6 *)
somme_nim 5 9;; (** 5 xor 9 = 0101_2 xor 1001_2 = 1100_2 = 12 *)
somme_nim 12 1;; (** 12 xor 1 = 1100_2 xor 0001_2 = 1101_2 = 13 *)
somme_nim 12 2;; (** 12 xor 2 = 1100_2 xor 0010_2 = 1110_2 = 14 *)
(* D'après le corollaire 1., il suffit d'appliquer un **xor** bit à bit à chaque valeur du tableau pour calculer $\gamma$.
En OCaml, on peut faire ça avec une boucle, une fonction récursive, ou plus concisement avec `Array.fold_left`. *)
(* In[12]: *)
Array.fold_left ;;
(* *Rappel :* `fold_left` prend une fonction `f(x,y)` et `x0` calcule
`f( ... f( f( x0, a[0] ), a[1] ), a[n-1] )`.
Ici, pour `f = somme_nim`, il faut donner `x0 = 0` pour que le premier calcul `f(0, a[0]) = a[0]`. *)
(* In[13]: *)
let gamma = Array.fold_left somme_nim 0;;
(* In[14]: *)
print "\nGamma(a) = %i" (gamma(a));;
print "\nGamma(b) = %i" (gamma(b));;
(* ### 1.3.3 Déterminer un coup à jouer selon une stratégie gagnante (s'il y en a une) *)
(* On suit l'algorithme proposé par le texte, qui utilise la fonction $\gamma$ sur la configuration pour savoir s'il y a une stratégie ou non (d'après la proposition 5.), et ensuite si elle existe on doit trouver un coup qui ammene $\gamma$ à 0.
On a d'abord besoin d'une exception pour signaler s'il n'y a pas de stratégie gagnante. *)
(* In[15]: *)
exception Pas_de_Strat_Gagnante;;
(* #### 1.3.3.1 Stratégie optimale
L'algorithme va être assez naïf, depuis une configuration actuelle $c$ :
- si $\gamma(c)$ est $0$, on échoue (`Pas_de_Strat_Gagnante`),
- sinon, on explore toutes les configurations $c'$ atteignables en un coup depuis $c$, et on choisis la première qui amène à $\gamma(c') = 0$. *)
(* In[40]: *)
let next ?(id=0) config =
let g = gamma config in
(** La prop. 5 donne directement : si g(s_0) = 0 alors échec. *)
if g = 0 then raise Pas_de_Strat_Gagnante;
(** Sinon, on calcul les gamma des fils, et on pointe vers le fils avec un gamma nul. *)
print "\n\nIl y a une stratégie gagnante :\n";
(** En pratique, on aurait pas besoin de calculer chaque fils,
comme on sait que g != 0, et on doit obtenir g' = 0,
on peut s'arreter au premier coup qui donne g' = 0.
*)
let config' = Array.copy config in
let colonne = ref 0 and
nb = ref 1 in
(** Il suffit d'explorer toutes les configurations accessibles depuis l'état actuel : *)
for j = 0 to (Array.length config') - 1 do
for i = 1 to config'.(j) do
config'.(j) <- config'.(j) - i; (** On essaie d'appliquer ce coup. *)
if (gamma config') = 0 then begin
(** On a trouvé un coup gagnant, on le stocke. *)
colonne := j;
nb := i;
end;
config'.(j) <- config'.(j) + i; (** On annule ce coup. *)
done;
done;
(** On applique le coup retenu, dernier coup à donner gamma(c') = 0. *)
print "\nLe joueur courant (numéro %i) doit enlever %i allumette à la %i-ième rangée." id !nb !colonne;
config'.(!colonne) <- config.(!colonne) - !nb; (** On retire nb allumette sur cette colonne. *)
(* assert ((gamma config') = 0); (** Si on veut vérifier *) *)
config'
;;
(* > *Note :* cette fonction utilise la notation `?(id=0)` dans sa définition pour définir un *argument optionnel avec valeur par défaut*.
> La fonction `next` peut être appelée de deux façons :
>
> - `next a` : sans spécifier `id`, qui vaudra donc `0`,
> - `next ~id:1 a` : en spécifiant `id=1`, qui vaut ici `1`.
>
> Cette astuce n'est pas requise, et c'est un peu de la "frime", inutile de la retenir et de se forcer à s'en servir. Néanmoins, ça peut être utile dans certaines situations. *)
(* ----
On peut tester cette fonction sur nos deux configuration a et b : *)
(* In[41]: *)
try
print_nim (next ~id:0 a);
with _ -> print "\n\nBlocage durant la simulation 1 (sur a).";;
(* In[42]: *)
try
print_nim (next ~id:1 b);
with _ -> print "\n\nBlocage durant la simulation 2 (sur b).";;
(* ### 1.3.4. Une stratégie "stupide" : complètement aléatoire *)
(* On a d'abord besoin de deux fonctions utilitaires :
- `array_filter`, pour filtrer un tableau (comme [`List.filter`](http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html#VALfilter) mais pour un [`array`](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Array.html)). On fait ça simplement en transformant le tableau en liste, on filtre la liste, puis la liste en tableau. Attention, la taille du tableau n'est *pas* préservée.
- et `choose` pour choisir un élément aléatoire (uniforme) dans un tableau. En Python, on aurait [`random.choice`](https://docs.python.org/3/library/random.html#random.choice). On fait ça simplement en tirant un entier `i` aléatoire uniformément parmi `0, ..., n-1` pour `n = Array.length a`, puis en renvoyant `a.(i)`.
> (Et oui, la libraire standard de OCaml est [assez limitée](http://sucre.syntaxique.fr/doku.php?id=ocaml#bibliotheque_standard))... *)
(* In[19]: *)
let array_filter pred a =
Array.of_list (List.filter pred (Array.to_list a));;
let choose a
= a.(Random.int (Array.length a));;
(* Dans le but de comparer cette fonction `next`, qui implémente une stratégie optimale, on implémente aussi une stratégie complétement aléatoire ("Dummy player").
La stratégie aléatoire fonctionne en trois étapes :
1. trouve les rangées qui ont encore des allumettes (en calculant leurs indices, via `array_filter`, dans le tableau `indices`),
2. choisi une rangée aléatoirement `i` (uniformément),
3. enlève un nombre aléatoire (uniforme) d'allumette entre `1` et `xi`, pour `xi` le nombre d'allumette dans la rangée `i` (i.e., `xi = config.(i)`). *)
(* In[20]: *)
(** Un adversaire stupide qui joue aléatoirement. *)
let random ?(id=1) config =
print "\nLe joueur courant (numéro %i) joue aléatoirement.\n" id;
let indices = array_filter ( fun i -> (config.(i) > 0) ) (Array.init (Array.length config) (fun i -> i)) in
let i = choose indices in
print "Le joueur (%i) choisi de regarder la %i-ième rangée.\n" id i;
let nbAEnlever = 1 + Random.int config.(i) in
print "Le joueur (%i) choisi d'enlever %i allumettes parmi les %i disponibles.\n" id nbAEnlever config.(i);
let config' = Array.copy config in
config'.(i) <- config.(i) - nbAEnlever;
config';
;;
(* ## 1.4 Deux exemples, manuellement *)
(* ### 1.4.1 Premier exemple *)
(* On peut ainsi faire un exemple de début de partie entre deux joueurs "stupides" : *)
(* In[43]: *)
let a0 = a ;; (* Debut du jeu *)
print_nim a0 ;;
let a1 = random ~id:0 a0 ;;
print_nim a1 ;;
let a2 = random ~id:1 a1 ;;
print_nim a2 ;;
let a3 = random ~id:0 a2 ;;
print_nim a3 ;;
(* ... etc *)
(* ### 1.4.2 Second exemple *)
(* On peut aussi faire le même exemple de début de partie entre un joueur "optimal" et un joueur "stupide" : *)
(* In[44]: *)
let c = [| 2; 3; 2 |] ;;
let a0 = c ;; (* Debut du jeu *)
print_nim a0 ;;
let a1 = next ~id:0 a0 ;;
print_nim a1 ;;
let a2 = random ~id:1 a1 ;;
print_nim a2 ;;
let a3 = next ~id:0 a2 ;;
print_nim a3 ;;
(* ... etc *)
(* ## 1.5 Un bonus : *simulation du jeu* *)
(* Maintenant qu'on dispose d'un joueur stupide et d'un joueur optimal, on peut rapidement coder une petite fonction qui les fera s'affronter (même si c'est un peu cruel envers le pauvre joueur "stupide" purement aléatoire !). *)
(* In[23]: *)
exception Perdu of int;;
(* ### 1.5.1 Simulation de plusieurs étapes *)
(* La fonction ``kpas`` va jouer la partie, en partant de la configuration donnée, en commençant par le joueur ``id`` et pour un certain nombre de coups joués (``nbPas``).
Si on ne donne pas ce nombre de coups, le nombre total d'allumette est utilisé (sachant qu'une partie se termine souvent par une exception ``Pas_de_Strat_Gagnante`` lorsque le joueur optimal ne peut plus gagner). *)
(* In[45]: *)
let kpas config id nbPas =
let id = ref id in
let config' = ref (Array.copy config) in
for i = 1 to nbPas do
print "\nTour numéro %i." i;
print_nim !config';
print "\n";
if (Array.fold_left ( + ) 0 !config') = 0 then raise (Perdu !id);
begin
if !id = 0 then (** Le joueur 0 joue bien. *)
config' := next ~id:(!id) !config'
else (** Mais le joueur 1 joue aléatoirement. *)
config' := random ~id:(!id) !config'
end;
id := 1 - !id; (** Juste pour alterner entre 0 et 1 : 0 -> 1, 1 -> 0 *)
done;
!config';
;;
(* On peut finalement implementer une jolie fonction qui simule en partant du joueur ``0`` (comme le vrai jeu de Nim) et interprète l'exception renvoyée pour afficher l'issue du jeu : *)
(* In[46]: *)
let simulation config =
(** On compte le nombre total d'allumettes. *)
let n = Array.fold_left (fun i j -> i + j + 1) 0 config in
(** Puis on lance le joueur 0 avec au plus [n] pas. *)
kpas config 0 n;
;;
(** Dernière fonction, [nim], qui lance [simulation] et rattrape les exceptions. *)
let nim a =
let resultat =
(* try ignore (simulation a) with *)
try ignore(simulation a); None with
| Perdu i -> (Some i)
| Pas_de_Strat_Gagnante -> None
in
begin
match resultat with
| None -> print "Blocage à cause de la stratégie optimisée.\n\n"
| Some i -> print "\n ==> Le joueur %i a perdu !\n\n" i
end;
resultat
;;
(* ### 1.5.2 Exemple : *)
(* In[47]: *)
nim a;;
(* In[48]: *)
nim b;;
(* > Pas très utile jusqu'ici... *)
(* In[49]: *)
nim c;;
(* ### 1.5.3 Générer des configurations aléatoires *)
(* On peut écrire une fonction qui génére une configuration aléatoire, et ensuite lancer notre simulation ``nim()`` dessus, pour voir ce que ça donne sur une configuration plus grande.
La fonction `randomstart(k, p)` va générer une configuration aléatoire :
- avec un nombre de lignes uniforme dans `{1, ..., k}`,
- et un nombre d'allumettes uniforme dans `{1, ..., p}` pour chaque ligne. *)
(* In[29]: *)
let randomstart k p () = Array.init (1 + Random.int k) (fun i -> 1 + Random.int p);;
(* Par exemple : *)
(* In[30]: *)
let c = randomstart 5 8 ();;
print_nim c;;
(* #### Le joueur *optimal* (d'indice `0`) gagne toujours *)
(* In[31]: *)
let c = randomstart 5 8 () ;;
print "Configuration random c :" ;;
print_nim c;;
nim c;;
(* #### Le joueur *stupide* peut-il quand-même gagner ? *)
(* In[37]: *)
c = randomstart 5 8 () ;;
print "Configuration random c :" ;;
print_nim c ;;
nim c ;;
(* C'est effectivement possible que le joueur `0` (celui qui commence) perde, même en suivant sa stratégie gagnante.
Si le joueur aléatoire `1` a de la chance... *)
(* ---- *)
(* ## 1.5 Conclusion *)
(* C'est tout ce que j'avais eu le temps d'implémenter durant les 4h de préparation (c'est un des textes que j'avais préparé en juin 2014, dans les "vraies" conditions en oraux blanc, à l'ENS Cachan, et le code initial était en OCaml mais je n'ai rien changé à part l'écriture du notebook Jupyter).
Quelques remarques :
- durant l'épreuve de modélisation, vous êtes libres de faire ce que vous voulez, la seule partie requise est dans le paragraphe **Exercice de programmation** (ici, il s'agissait d'implémenter une fonction similaire à ``optimal()`` (cf. plus haut).
- les quelques développements supplémentaires traites ci-dessus (stratégie stupide, configuration aléatoire, simulation de jeu), ne sont qu'une suggestion de ce qui pouvait être fait sur ce texte,
- d'autres suggestions sont possibles, si vous avez des idées, [envoyez-moi vos notebooks !](https://github.com/Naereen/notebooks/pulls).
> *Edit :* j'avais une erreur dans mon calcul de `next`, corrigée le 11/01/17. *)
(* ## 1.6 Attention : *)
(* Les 35/40 minutes de passage au tableau ne doivent PAS être uniquement consacrée à la présentation de vos expériences sur l'ordinateur !
Il faut aussi :
- faire une introduction générale (citer des mots clés),
- présenter le plan de votre présentation,
- introduire les notations, les objectifs et les résultats donnés par le texte,
- prouver ou exposer des développements **theoriques** personnels (à choisir parmi la liste proposée, mais pas seulement),
- etc. *)
(* ----
> *C'est tout pour aujourd'hui les amis !*
> [Allez voir d'autres notebooks](https://github.com/Naereen/notebooks/tree/master/agreg) si vous voulez. *)