niveau d'indentation 0 |
En Coffeescript, l'affectation est notée par un "=".
Par exemple,
x = 2
x = x+1
Une variable peut être affectée par un nombre mais aussi par du texte, une liste ou même une fonction.
On peut affecter simultanément a par 1 et b par 2 avec
[a,b] = [1,2]
Pour augmenter x de 1, on peut faire indifféremment
x = x+1 x += 1 x++
Pour faire entrer un nombre x par l'utilisateur, il faut affecter x (donc écrire un signe "égal") et afficher un message d'invite rappelant ce que représente le nombre. Par exemple
x = entre "Quelle valeur donner à x ?"
Tant qu'on ne demande pas à CoffeeScript d'afficher le résultat d'un
calcul, on ne peut pas connaître celui-ci. Il y a deux moyens de faire
connaître le contenu de x
:
alerte x
x
dans le cadre prévu à cet effet à droite
de la page avec
affiche x
Pour créer une fraction, il faut lui attribuer un numérateur et un dénominateur. Par exemple, pour créer la fraction "2 tiers", on entre
nouvelle Fraction 2, 3L'affectation de fractions est pratique pour effectuer des calculs.
Pour afficher une fraction, on met le mot "affiche" devant son nom:
a = nouvelle Fraction 2, 3 affiche aOn peut aussi afficher une valeur approchée de la fraction avec "toFloat()":
a = nouvelle Fraction 2, 3 affiche a.toFloat()
Pour additionner deux fractions, on met ".plus" entre les deux:
a = nouvelle Fraction 2, 3 b = nouvelle Fraction 3, 4 affiche a.plus b
Pour soustraire deux fractions, on met ".moins" entre les deux:
a = nouvelle Fraction 2, 3 b = nouvelle Fraction 3, 4 affiche a.moins b
Pour multiplier deux fractions, on met ".fois" entre les deux:
a = nouvelle Fraction 2, 3 b = nouvelle Fraction 3, 4 affiche a.fois b
Pour diviser deux fractions, on met ".sur" entre les deux:
a = nouvelle Fraction 2, 3 b = nouvelle Fraction 3, 4 affiche a.sur b
On peut calculer l'opposé d'une fraction (qu'elle soit négative ou positive):
a = nouvelle Fraction 2, -3 affiche a affiche a.opposé()
En général, l'inverse d'une fraction n'est pas égal à son opposé:
a = nouvelle Fraction 2, -3 affiche a affiche a.inverse()
Pour connaître la décomposition d'une fraction supérieure à 1 sous la forme d'une somme comportant
a = nouvelle Fraction 355, 113 affiche egypt ace qui apprend par exemple que 355/113 = 3+1/8+1/61+1/5014+1/27649202+1/1911195900442808.
Pour lancer un dé à 6 faces
affiche dé 6
Le nombre de faces doit être en entier, sans limite.
Pour vérifier, on peut lancer 10 fois le dé:
(dé 6 pour n dans [1..10])
Pour jouer à pile ou face, on lance un dé à deux faces.
affiche alea()
donne un nombre aléatoire entre 0 et 1
On peut alors simuler une variable aléatoire Z normale centrée (d'espérance 0) et réduite (d'écart-type 1) avec
Z = -6 pour n dans [0...12] Z += alea() affiche Z
Essayer par exemple
gauss = () -> Z = -6 Z += alea() pour n dans [0...12] Z grosTableau = (gauss() pour n dans [1..1000]) histogramme grosTableau, -3, 3, 15, 200
Pour tester l'égalité entre deux objets, il y a deux manières:
2+2 == 4
2+2 is 4
affiche 2+2 est égal à 5
affiche false
(ben oui!)
Pour obtenir le contraire d'une proposition, la précéder par un point d'exclamation
affiche not 2+2==5 affiche 2+2 est différent de 5
Le symbole "≠" se note donc isnt
ou !=
ou "est différent de", au choix.
Pour dire que x est à la fois positif et pair, on peut écrire
x>0 et x%2 est égal à 0
Pour dire que x est, ou bien positif, ou bien pair (ou les deux), on peut écrire
x > 0 ou x%2 est égal à 0
Une alternative est le double trait vertical.
Pour taper le symbole "trait vertical", appuyer avec la main droite sur et, sans lâcher ce bouton, actionner le en haut du clavier, de la main gauche.
Un test est une instruction conditionnelle; Pour n'effectuer quelque chose que lorsqu'une condition est vraie (par exemple seulement si le dé est tombé sur 6), faire
si dé(6) est égal à 6 affiche "gagné"
On remarque l'indentation qui précise que la partie
affiche "gagné"
ne doit s'effectuer que si le dé vaut 6.
affiche "gagné" si dé(6) est égal à 6
a le même effet.
Que faire quand le dé ne tombe pas sur 6 ?
Si on ne s'intéresse pas au gain mais seulement à la défaite, on est pessimiste et on écrit
affiche "perdu" à moins que dé(6) == 6
Mais les deux cas peuvent être traités ensemble:
si dé(6) est égal à 6 affiche "gagné" sinon affiche "perdu"
On peut aussi faire un test dans un intervalle:
(x pour x dans [1..12] quand x%2==0)
En CoffeeScript, les intervalles sont composés uniquement d'entiers.
L'intervalle fermé [a;b] se note [a..b] avec deux points;
L'intervalle ouvert à droite [a;b[ se note [a...b] avec trois points;
L'appartenance se note in
;
Pour vérifier que 2 est compris entre 0 et 5:
affiche 2 dans [0..5] affiche 0 <= 2 <= 5
L'appartenance est aussi utilisée pour appliquer une fonction à un intervalle:
affiche (x*x pour x dans [0..5])
Pour ajouter un élement x à un ensemble A:
A.empile x
Pour enlever le nombre 5 d'un ensemble A:
A = (x pour x dans A quand x est différent de 5)
L'intersection de deux ensembles A et B est formée des x de A qui sont aussi dans B:
I = (x pour x dans A quand x dans B)
Une légère différence mais qui change tout:
(x for x in A when x not in B)
est le complémentaire de B dans A.
En probabilités, cette notion formalise le contraire d'un évènement.
La réunion de deux intervalles n'est pas nécessairement un intervalle:
A = (x pour x dans [0..100] quand 2 < x < 15 ou 80 <= x < 90) affiche A
À la différence d'un ensemble, un multiensemble (ou sac) peut contenir plusieurs occurences d'un élément. On crée un sac à partir d'un tableau, on ajoute un élément dans un sac comme dans un ensemble, mais on peut aussi ajouter plusieurs fois un élement d'un coup. Ainsi, pour créer une urne contenant 7 boules rouges et 3 boules bleues, on peut faire, ou bien
urne = nouveau Sac ['rouge','rouge','rouge','rouge','rouge','rouge','rouge','bleu','bleu','bleu']
ou bien
urne = nouveau Sac [] urne.ajoute 'rouge' pour n dans [1..7] urne.ajoute 'bleu' pour n dans [1..3]
ou enfin
urne = nouveau Sac [] urne.ajouteFois 7, 'rouge' urne.ajouteFois 3, 'bleu'
On peut calculer les intersection et réunion de deux multiensembles comme pour les ensembles,
oter un élément d'un multiensemble avec .ote élément
ou extraire un élément au hasard d'un multiensemble;
comme pour un ensemble, le cardinal d'un multiensemble est le nombre d'éléments (comptés
avec leur multiplicité) qu'il contient. sac.contient x
teste si l'élément x
se trouve ou non dans le sac.
Si on divise 0 par 0 on obtient NaN
(not a number) qui signifie que la division n'a pas de sens.
Mais si on divise 1 (ou tout autre nombre non nul) par 0,
on obtient Infinity
, ce qui veut dire que lorsqu'une variable x
tend vers 0, son inverse 1/x tend vers l'infini;
De même,
1/Infinity
donne 0: Si une variable x tend vers l'infini, son inverse 1/x tend vers 0.
affiche Infinity+Infinity
La somme de deux fonctions qui tendent vers +∞ tend elle-même vers +∞
affiche 3+Infinity
La somme d'une fonction qui tend vers 3 et d'une fonction qui tend vers +∞ tend elle-même vers +∞
affiche Infinity-Infinity
On ne peut rien conclure sur la somme d'une fonction qui tend vers +∞ et d'une fonction qui tend vers -∞: Il s'agit d'une forme indéterminée.
affiche Infinity*Infinity
Le produit de deux fonctions qui tendent vers +∞ tend lui-même vers +∞
affiche 3*Infinity
Le produit d'une fonction qui tend vers 3 et d'une fonction qui tend vers +∞ tend lui-même vers +∞
affiche 0*Infinity
On ne peut pas conclure sur le produit d'une fonction qui tend vers ∞ et d'une fonction qui tend vers 0: Il s'agit d'une forme indéterminée.
affiche leLogarithmeDe 0 affiche leLogarithmeDe Infinity
La limte de ln en 0 est -∞ et la limite de ln en +∞ est +∞
affiche exp -Infinity affiche exp Infinity
La limite de ex est 0 lorsque x tend vers -∞ et +∞ lorsque x tend vers +∞.
Une fonction associe à une variable d'entrée (ou plusieurs) appelée antécédent, une (ou plusieurs) variable de sortie appelée image; l'image de x par la fonction f est notée f(x); Aussi, en CoffeeScript, une fonction est-elle désignée par une flèche -> séparant l'antécédent, entre parenthèses, de l'image, qui est une expression. Après la flèche on peut insérer un algorithme destiné à calculer la fonction par algorithme.
La dernière ligne écrite dans la fonction est la valeur retournée.
Une fonction peut ne pas avoir d'antécédent, dans ce cas c'est une procédure.
(x) -> 3
est une fonction constante.
(x) -> x/2+1
est une fonction affine.
On peut affecter une variable par une fonction comme dans
f = (x) -> x*x-2*x-1
Ensuite on peut entrer f(3)
ou f 3
pour avoir l'image de 3 par f.
Parmi les fonctions prédéfinies, on trouve
sinus
et cosinus
en degrés (pour l
es radians, utiliser sin
et cos
)laRacineDe
pour la racine carrée; leCarréDe
,
leCubeDe
pour le carré et le cube d'un nombre;
lInverseDe
pour l'inverse d'un nombre (1 divisé par ce nombre)leLogarithmeDe
pour le logarithme népérien, et
lExponentielleDe
pour ex.Pour avoir un tableau de valeurs d'une fonction, on peut construire un tableau associatif dans lequel on associe à chaque valeur de x choisie, son image par la fonction. Ensuite on peut afficher ce tableau associatif dans le tableau ci-dessus ("sorties graphiques")
tableauAssoc = {} pour x dans [1,2,3,5,10] tableauAssoc[x] = leCarréDe x trierDansTableau tableauAssoc
On peut aussi dessiner le diagramme sagittal avec cette variante :
tableauAssoc = {} pour x dans [1,2,3,5,10] tableauAssoc[x] = leCarréDe x sagittal tableauAssoc
qui donne ce diagramme :
Plus court: On peut afficher les x et leurs images dans l'affichage:
pour x dans [1,2,3,5,10] affiche "#{x}, #{leCarréDe x}"
Ensuite, on sélectionne à la souris les nombres, et on les copie-colle dans un fichier portant l'extension "csv" (par exemple carré.csv); il suffit ensuite d'ouvrir ce fichier avec un tableur pour avoir non seulement le tableau, mais aussi la représentation graphique.
Pour répéter 10 fois une action, on a besoin d'une variable appelée indice de la boucle. Ici on notera i cet indice, qui ira de 1 à 10.
Par exemple, pour lancer un dé 10 fois, on peut entrer
pour i dans [1..10] affiche dé 6
Un bon moyen de suivre l'indice d'une boucle est d'afficher celui-ci dans la boucle:
pour i dans [1..10] affiche i
affiche i pour i dans [1..10]
a le même effet: Afficher i pour i entre 1 et 10. Alors que si on ajoute des parenthèses, il n'y a plus qu'un affichage, les parenthèses produisant une liste:
affiche (i pour i dans [1..10])
Les nombres 1, 2, 3 etc parcourus forment une suite arithmétique de raison 1.
Pour faire descendre l'indice (compte à rebours) on peut faire
affiche i pour i dans [10..1]
Pour aller de 3 en 3 on peut faire
affiche i pour i dans [1..10] quand i%3 est 1
ou, mieux:
affiche i pour i dans [1..10] par pas de 3
Pour boucler sur les nombres premiers inférieurs à 20:
pour i dans [2,3,5,7,11,13]
Pour lancer un dé jusqu'à ce qu'on ait un 6 (et compter les lancers dans un indice i), on fait
i=0 i++ jusqu'à ce que dé(6) == 6
Il y a plusieurs manières de faire ça; par exemple
i=1 i++ jusqu'à ce que dé(6) == 6
On peut aussi faire comme ceci:
i=1 tant que dé(6) est différent de 6 i=i+1
Un dé icosaédrique, comme son nom l'indique, a 20 faces numérotées de 1 à 20. L'univers de probabilité est alors Ω={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; On peut le calculer en fabricant un ensemble à partir de la liste des nombres allant de 1 à 20 :
univers = nouvel Ensemble [1..20] affiche univers
L'évènement «le résultat est pair» s'écrit A={2,4,6,8,10,12,14,16,18,20}; il peut se définir en compréhension par
A = nouvel Ensemble (x pour x dans univers.support quand x%2 == 0)
L'évènement «le résultat s'écrit avec un seul chiffre» peut se définir de façon analogue mais aussi en faisant comme pour l'univers; on obtient B={1,2,3,4,5,6,7,8,9}
B = nouvel Ensemble [1..9]
On peut dessiner les évènements A et B avec ce script:
patates A, B
ou cette variante :
patates3D A, B
qui affiche ceci :
La figure est "partitionnée" en trois parties :
Le contraire de A est formé des résultats qui sont dans l'univers mais pas dans A; on l'obtient par
A.complémentDans univers
L'évènement «le résultat est pair, et en plus il s'écrit avec un seul chiffre», noté A ∩ B, s'obtient par
A.inter B
L'évènement «le résultat est pair, ou alors il s'écrit avec un seul chiffre (à moins que ce soit les deux en même temps)», noté A ∪ B, s'obtient par
A.union B
Pour chercher les nombres premiers inférieurs ou égaux à 200, on peut faire ainsi :
crible = nouvel Ensemble [1..200] pour m dans [2..200] poubelle = nouvel Ensemble (x pour x dans crible.support quand x>m et x%m == 0) crible = poubelle.complémentDans crible affiche crible
En fait, on peut aussi implémenter cet algorithme avec des tableaux, ce qui permet alors d'afficher l'histogramme des nombres premiers :
N = 200 crible = [2..N] pour m dans [2..N] poubelle = (x pour x dans crible quand x>m et x%m est égal à 0) crible = (x pour x dans crible quand x n'est pas dans poubelle) histogramme crible, 0, 200, 10, 10
Le nombre d'éléments que contient un ensemble est son cardinal; pour savoir combien d'éventualités se trouvent dans A, on fait
A.cardinal()
Si son cardinal est nul, A est l'évènement impossible (le contraire de l'univers); pour savoir si un ensemble est vide, on fait
A.estVide()
La probabilité d'un évènement A est définie comme le quotient de son cardinal par celui de l'univers; elle dépend donc du choix de l'univers, et se note PΩ(A), ou, lorsqu'il n'y a pas d'ambigüité sur Ω, simplement P(A):
univers = nouvel Ensemble [1..20] proba = (E) -> E.probaSachantQue univers B = nouvel Ensemble [1..9] affiche proba(B)
Pour créer un point, on demande un nouveau Point (avec un P majuscule) et on lui donne des coordonnées :
A = nouveau Point 30, 20
Les coordonnées de A s'affichent avec affiche A
. Pour dessiner A dans un repère
centré sur le graphique et d'unité 1 pixel, il faut faire
effaceDessin() A = nouveau Point 30, 20 dessineCercle 320+A.x, 240-A.y, 2, 'blue'En effet A.x et A.y sont les coordonnées de A.
La distance AB se calcule avec :
A = nouveau Point 30, 20 B = nouveau Point -50, 20 affiche A.distance B
Pour calculer les coordonnées du milieu de [AB] :
A = nouveau Point 30, 20 B = nouveau Point -50, 20 affiche A.milieu B
Pour dessiner le segment [AB] en vert :
A = nouveau Point 30, 20 B = nouveau Point -50, 20 dessineSegment 320+A.x, 240-A.y, 320+B.x, 240-B.y, 'green'
Les coordonnées du vecteur d'origine A et d'extrémité B se calculent avec
A = nouveau Point 30, 20 B = nouveau Point -50, 20 affiche A.vecteur B
Pour créer un nouveau vecteur, on doit juste entrer ses coordonnées :
u = nouveau Vecteur 3, 2 affiche uOn voit que l'affichage des vecteurs se fait juste en demandant d'afficher. On constate aussi l'utilité de nommer les vecteurs, pour référence ultérieure.
La norme (ou longueur) d'un vecteur est une méthode de celui-ci :
u = nouveau Vecteur 3, 2 affiche u.norme()
Pour tripler un vecteur :
u = nouveau Vecteur 3, 2 affiche u.fois 3
Pour additionner deux vecteurs, on utilise plus
:
u = nouveau Vecteur 3, 2 v = nouveau Vecteur -1, 5 affiche u.plus v
Pour multiplier (scalairement) un vecteur par un autre, on utilise scalaire
:
u = nouveau Vecteur 3, 2 v = nouveau Vecteur -1, 5 affiche u.scalaire v
Cela sert à savoir si des vecteurs sont perpendiculaires :
u = nouveau Vecteur 3, 2 v = nouveau Vecteur -1, 5 affiche u.orthogonal v
Le déterminant d'un vecteur avec un autre vecteur est un nombre :
u = nouveau Vecteur 3, 2 v = nouveau Vecteur -1, 5 affiche u.déterminant v
Cela sert à savoir si deux vecteurs sont colinéaires :
u = nouveau Vecteur 3, 2 v = nouveau Vecteur -1, 5 affiche u.colinéaireAvec v
Pour aller plus loin, voir l'aide sur les matrices et les exemples de géométrie dans l'espace.
La sortie graphique mesure 640 pixels de large et 480 pixels de haut. Les abscisses vont donc de 0 (tout à gauche) jusqu'à 640 (tout à droite); mais les ordonnées sont ordonnées de haut en bas: 0 pour un élement graphique tout en haut, et 480 pour un élément graphique tout en bas.
Pour effacer le dessin, on entre
effaceDessin()
On dessine un point comme un cercle de rayon petit, par exemple
dessineCercle 240, 100, 2, 'red'
pour dessiner le point de coordonnées (240,100) (représenté comme un cercle de rayon 2 pixels).
Pour dessiner le segment joignant les points de coordonnées respectives (100, 200) et (400, 300) :
dessineSegment 100, 200, 400, 300, 'blue'
Pour dessiner le rectangle dont le coin supérieur gauche a pour coordonnées (200,50) et de largeur 40 pixels, et de hauteur 150 pixels:
dessineRectangle 200, 50, 40, 150, 'cyan'
Pour écrire sur le graphique (aux coordonnées (150,100) ici), on fait
dessineTexte "alcoffeethmique Mac", 150, 100, 'grey'
On peut choisir la couleur que l'on veut pour chacune de ces primitives graphiques. Le format est le suivant: Une chaîne de caractères commençant par "#" suivi de 6 lettres (ou chiffres hexadécimaux) allant de 0 à f, les deux premier codant la quantité de rouge (de 00 à ff), les deux suivants codant la quantité de vert, et les deux derniers codant la quantité de bleu. Par exemple, pour avoir du jaune, on peut faire "#ffff00" ou "yellow".
Pour dessiner un polygone, il faut lui fournir la liste des coordonnées de ses sommets
(chacun des éléments de cette liste est lui-même une liste de deux nombres), ainsi que la couleur
du périmètre et celle du remplissage (none
si on ne veut pas remplir)
dessinePolygone [[20,240],[600,20],[600,460]], 'black', 'yellow'
Pour dessiner l'axe gradué des abscisses allant de -1 à 3, en mauve :
dessineAxeX -1, 3, 'magenta'
et de façon similaire pour l'axe des ordonnées (dessineAxeY
); on peut
dessiner d'un coup les deux axes avec
dessineAxes -1, 3, -10, 100, 'brown'
Ces fonctions sont utilisées dans les deux suivantes:
Pour représenter graphiquement la fonction carré
sur [-10,10] :
dessineFonction carré, -10, 10, 0, 100, 'red'
Pour représenter graphiquement une suite, il faut la calculer comme une liste de nombres;
par exemple, pour représenter graphiquement la somme des n
premiers entiers
(c'est une suite d'entiers) pour n
allant de 0 à 10, avec des points de rayon
3, en bleu :
dessineSuite (laSommeDe [1..k] pour k dans [0..20]), 10, 0, 60, 3, 'blue'
diagrammeBatons {'pour': 25, 'contre': 60, 'sans opinion': 15}, 100
Le second paramètre est un facteur d'échelle sur l'axe des ordonnées
Variante si le caractère est quantitatif:
lancers = nouveau Sac [] lancers.ajoute dé(6) pour n dans [1..100] diagrammeBatonsTrie lancers.effectifs , 40
Pour dessiner un histogramme, il faut une liste de nombres, les bornes d'un intervalle et le nombre de rectangles :
carrés = (x*x pour x dans [0..100]) histogramme carrés, 0, 10000, 10, 40
Pour dessiner un ensemble on utilise au choix, patate ou patate3D (mais dans ce cas effaceDessin() a pour effet de rendre invisible à l'écran certains éléments graphiques. L'export du dessin conserve ces éléments). Les variables de patate sont
Voici un dessin d'un ensemble de couleurs de cartes à jouer :
couleurs = new Ensemble ['♦','♥','♠','♣'] patate3D couleurs
et le dessin obtenu :
Pour faire des statistiques sur une liste, il est parfois nécessaire de la trier (par exemple, pour calculer les quantiles). Comme "trier" se dit "sort" en anglais, on trie la liste L ainsi:
L.sort (x,y) -> (y-x)
L.sort (x,y) -> (x-y)
Math.max.apply null,L Math.min.apply null,L
Pour connaître l'effectif total d'une liste, on peut utiliser sa "longueur" (en anglais, length):
L.length
laSommeDe L
laMoyenneDe L
laVarianceDe L
lEcartTypeDe L
laMédianeDe
Pour créer le complexe z=3+2i, on entre juste
z = nouveau Complexe 3, 2Sa partie réelle est
z.Re
et sa partie imaginaire est z.Im
.
Pour calculer le conjugué de 3+2i:
z = nouveau Complexe 3, 2 affiche z.conj()
L'inverse de z est z.inverse()
:
z = nouveau Complexe 3, 4 affiche z.inverse()
On peut calculer le module d'un nnombre complexe et le carré du module:
z = nouveau Complexe 3, 4 affiche z.module() affiche z.modCarre()
L'argument d'un nombre complexe est donné en valeur principale en radians:
z = nouveau Complexe racine(3), 1 affiche z.argument()
Pour additionner deux complexes on utilise ".plus" (attention au point):
a = nouveau Complexe 3, 2 b = nouveau Complexe 3, 4 affiche a.plus b
Pour soustraire deux complexes on utilise ".moins":
a = nouveau Complexe 3, 2 b = nouveau Complexe 3, 4 affiche a.moins b
Pour multiplier deux complexes on utilise ".fois":
a = nouveau Complexe 3, 2 b = nouveau Complexe 3, 4 affiche a.fois b
Pour diviser deux complexes on utilise ".sur" (attention au point):
a = nouveau Complexe 3, 2 b = nouveau Complexe 3, 4 affiche a.sur b
Un vecteur est une liste de nombres; on le crée par le raccourci $V
:
v = $V [2,1] affiche v.elements
On voit au passage comment afficher les coordonnées d'un vecteur. On peut également engendrer des vecteurs aléatoires dont les coordonnées sont uniformes sur [0;1]:
u = Vector.Random(2) affiche u.inspect()
On voit au passage une autre méthode d'affichage des coordonnées du vecteur
L'abscisse de v se note v.e(1)
et son ordonnée se note v.e(2)
.
Pour additionner ou soustraire deux vecteurs:
u.add v u.subtract v
Pour multiplier un vecteur par 3:
u.x 3
Pour calculer le produit scalaire de deux vecteurs, on utilise dot
:
liste = (Vector.Random(2).dot(Vector.Random(2)) pour n dans [0...10000]) histogramme liste, 0, 2, 10, 4000
Ce script donne l'histogramme des produits scalaires de deux vecteurs aléatoires de coordonnées uniformes sur [0;1]:
Pour calculer la longueur d'un vecteur, on utilise modulus
. Pour afficher la distribution statistique de la longueur d'un vecteur de coordonnées uniformes sur [0;1]:
liste = (Vector.Random(2).modulus() pour n dans [0...10000]) histogramme liste, 0, 1, 10, 2000
L'histogramme est surprenant:
Pour calculer l'angle entre deux vecteurs, on utilise angleFrom()
:
liste = (Vector.Random(2).angleFrom(Vector.Random(2)) pour n dans [0...10000]) histogramme liste, 0, 2, 10, 4000
Ce script affiche l'histogramme des angles entre deux vecteurs:
Pour faire tourner un vecteur v
d'un angle a
(en radians), faire
u = $V [1,0] a = pi/6 affiche (u.rotate a, $V [0,0]).inspect()
Une matrice est un vecteur de vecteurs:
M = $M [[1,2], [3,4]] affiche M.inspect()
On engendre donc une matrice avec $M
suivi du tableau,
et on l'affiche avec inspect()
. Il y a d'autres moyens d'engendrer des
matrices:
M = Matrix.Diagonal [1,2] M = Matrix.Zero 2, 2 M = Matrix.I(2) M = Matrix.Random 2, 2 M = Matrix.Rotation pi/3
On peut récupérer les éléments d'une matrice avec e(i,j)
, les lignes
avec row(i)
et les colonnes avec col(j)
Pour additionner deux matrices, on utilise add
comme pour les vecteurs.
De même on peut soustraire deux matrices avec subtract
et multiplier une matrice
par un nombre avec x
; la syntaxe est la même que pour les vecteurs.
Pour multiplier deux matrices (et arrondir le résultat pour qu'il soit plus lisible):
M = Matrix.Rotation pi/3 P = Matrix.Rotation pi/6 affiche (M.multiply P).inspect() affiche ((M.multiply P).snapTo 0).inspect()
Pour afficher les éléments diagonaux d'une matrice:
M = Matrix.Rotation pi/3 affiche M.diagonal().inspect()
Pour afficher la transposée:
M = Matrix.Rotation pi/3 affiche M.transpose().inspect()
Pour afficher la trace et le déterminant:
M = Matrix.Rotation pi/3 affiche M.trace() affiche M.determinant()
Pour obtenir l'inverse d'une matrice:
M = Matrix.Rotation pi/3 affiche M.inverse().inspect()
Le principe est le suivant: On écrit le système sous la forme AX=B, où X et B sont
des vecteurs colonne, et A une matrice carrée. Alors la solution du système, écrite
sous forme d'un vecteur colonne, est X=A'B où A' est l'inverse de A. Par exemple,
pour résoudre le système formé par les deux équations 3x-2y=-1
et
x+y=8
:
A = $M [[3,-2],[1,1]] B = $V [-1,8] affiche (A.inv().x B).inspect()
Personne ne sait encore si cet algorithme s'arrête au bout d'un temps fini pour toute valeur initiale entière u0:
u = 35 jusqu'à ce que u == 1 si u%2 est égal à 0 u /= 2 sinon u = 3*u+1 affiche u
u = 35 Collatz = [u] jusqu'à ce que u == 1 si u%2 est égal à 0 u /= 2 sinon u = 3*u+1 Collatz.push u Syr = {} pour n dans [0...Collatz.length] Syr[n]=Collatz[n] trierDansTableau Syr dessineSuite Collatz, Collatz.length-1, 0, 200, 3, 'red'
vol = (n) -> [u, temps] = [n, 0] jusqu'à ce que u == 1 si u%2 est égal à 0 u /= 2 sinon u = 3*u+1 temps++ temps affiche (vol n pour n dans [2..20])
Pour calculer le pgcd de deux nombres a et b, on remplace a et b par b et r jusqu'à ce que r soit nul (r est le reste de la division de a par b).
pgcd = (a,b) -> [x,y] = [a,b] jusqu'à ce que y == 0 [x,y]=[y,x%y] x affiche pgcd 55,34
pgcd = (x,y) -> [x,y]=[y,x%y] jusqu'à ce que y == 0 x affiche pgcd 55,34
La fonction diviseurs programmée ci-dessous associe à chaque entier naturel, l'ensemble de ses diviseurs. Elle crée l'ensemble ld (liste des diviseurs) puis teste les diviseurs possibles d de n. Chaque fois que le reste n%d dans la division euclidienne de n par d est nul, c'est que d est effectivement un diviseur de n, et dans ce cas on ajoute d à l'ensemble des diviseurs de n :
diviseurs = (n) -> ld = nouvel Ensemble pour d dans [1..n] ld.ajoute d si n%d==0 ld affiche diviseurs 24
Cette fonction permet d'explorer des questions d'arithmétique :
Les entiers 0 et 1 sont particuliers de ce point de vue: 0 a une infinité de diviseurs (tous les entiers non nuls) alors que 1, au contraire, n'a qu'un diviseur. D'ailleurs tous les entiers sont divisibles par 1. On s'intéresse donc aux diviseurs propres des entiers valant au moins 2, c'est-à-dire les diviseurs autres que 1 :
dp = (n) -> ld = nouvel Ensemble pour d dans [2..n] ld.ajoute d si n%d==0 ld pour n dans [2..12] affiche "#{n} possède #{dp(n).cardinal()} diviseur(s)"
Les entiers 2, 3, 5, 7 etc ressortent sur la liste affichée, du fait qu'ils ont le nombre minimal de diviseurs propres possibles : Ce sont les nombres premiers.
On peut, au contraire, s'intéresser aux parties aliquotes d'un entier : Ce sont les diviseurs autres que l'entier lui-même. Et plus précisément, à la somme des parties aliquotes :
pa = (n) -> ld = nouvel Ensemble pour d dans [1...n] ld.ajoute d si n%d==0 ld pour n dans [2..12] affiche "S(#{n}) = #{laSommeDe pa(n).support}"
On constate alors qu'il y a trois sortes d'entiers naturels :
On peut généraliser cette étude, avec par exemple la somme des carrés des parties aliquotes :
pour n dans [2..12] affiche "S(#{n}) = #{laSommeDe (carré d pour d dans pa(n).support)}"
La variante présentée ci-dessous, du premier script, affiche non pas les diviseurs de 24, mais les nombres qui sont à la fois diviseurs de 16 et de 24 :
diviseurs = (n) -> ld = nouvel Ensemble pour d dans [1..n] ld.ajoute d si n%d==0 ld affiche (diviseurs 24).inter (diviseurs 16)
Cette "intersection" de diviseurs permet de faire deux constatations importantes en arithmétique :
Les ensembles de diviseurs peuvent être dessinés, avec mise en exergue des diviseurs communs. Avec 6 et 8, ce script
diviseurs = (n) -> ld = nouvel Ensemble pour d dans [1..n] ld.ajoute d si n%d==0 ld effaceDessin() patates diviseurs(6), diviseurs(8)
donne ce dessin (les diviseurs de 6 à gauche, en bleu, ceux de 8 à droite, en rouge):
On voit que les diviseurs de 6 sont 1, 2, 3 et 6; et que ceux de 8 sont 1, 2, 4 et 8: 3 et 6 divisent 6 mais ne divisent pas 8, et 4 et 8 divisent 8 sans diviser 6: 1 et 2 sont les diviseurs communs à 6 et 8; le plus grand est 2 ce qu'on note 2=pgcd(6,8).
On peut faire une expérience statistique : On lance 100 fois deux dés à 20 faces, et on regarde leur pgcd. Le script suivant permet de dessiner le diagramme en bâtons des pgcd :
s = nouveau Sac pour k dans [1..1000] s.ajoute pgcd(dé(20),dé(20)) diagrammeBatonsTrie s.effectifs, 600
Voici le diagramme obtenu :
On constate que les deux dés sont souvent premiers entre eux. Dans quelle proportion ?
Résultat étonnant: Avec des dés à 100 faces la proportion est proche de 6/π² :
s = 0 pour k dans [1..1000] s += 1 si pgcd(dé(100),dé(100))==1 affiche s affiche 6/pi/pi
Les ensembles (de diviseurs ci-dessus) ne sont pas un outil très adapté pour lister les facteurs premiers puisque par exemple 12 a 2 comme facteur premier, mais plusieurs fois. Il va donc falloir donner les facteurs premiers comme un multiensemble (ou ensemble à éléments multiples; on dit aussi sac). Dans alcoffeethmique, on utilise beaucoup les multiensembles en statistique, comme on a pu le voir ci-dessus.
Le script suivant permet de voir que 288000=28×32×53 mais aussi de le voir sur un diagramme en bâtons :
facteurs = (n) -> fp = nouveau Sac d = 2 jusqu'à ce que n==1 si n%d == 0 tant que n%d==0 fp.ajoute d n /= d d += 1 fp diagrammeBatonsTrie facteurs(288000).effectifs, 10
Pour calculer la racine carrée de 5 par l'algorithme de Heron, on remplace une valeur approchée a (par défaut), par sa moyenne avec 5/a (valeur approchée par excès); et on recommence jusqu'à ce que la différence entre les valeurs approchées par défaut et par excès soit devenue imperceptible.
[a,b] = [1,5] jusqu'à ce que -1e-16 < b-a < 1e-16 a=(a+b)/2 b=5/a affiche a
En plaçant les approximations successives de √(2) dans un tableau, on peut les représenter graphiquement, pour vérifier la convergence :
u=[1] pour n dans [1..20] u[n] = (u[n-1]+2/u[n-1])/2 dessineSuite u, 20, 0, 2, 5, 'red'
L'algorithme de Heron, appliqué à de grands nombres décimaux, permet de calculer 1000 décimales de √(5) :
Big.DP = 1000 [a,b]=[Big(1),Big(5)] pour n dans [1..12] a=a.plus(b).div(2) b=Big(5).div(a) affiche a
Pour trouver combien de fois il a fallu boucler, une méthode simple est de faire des
essais en affichant affiche a.minus b
jusqu'à ce que l'affichage donne 0.
La méthode suivante est plus rapide :
Big.DP = 1000 a = Big(5).sqrt() affiche a
Cette rapidité permet d'ailleurs de faire une étude statistique sur les décimales de √(5), mais ceci est une autre histoire :
Le graphique ayant été obtenu avec ce script :
Big.DP = 1000 a = Big(5).sqrt() décimales = a.toString()[2..] stats = nouveau Sac [] pour x dans décimales stats.ajoute parseInt(x) diagrammeBatonsTrie stats.effectifs, 150
Une autre version, avec l'utilisation de fractions:
deux = nouvelle Fraction 2 cinq = nouvelle Fraction 5 [a,b]=[nouvelle Fraction(1),nouvelle Fraction(5)] pour n dans [1..12] a=a.plus(b).sur(deux) b=cinq.sur(a) affiche a.toFloat()
Une légère modification fournit une suite d'approximations rationnelles de la racine de 5:
deux = nouvelle Fraction 2 cinq = nouvelle Fraction 5 [a,b]=[ nouvelle Fraction(1), nouvelle Fraction(5)] pour n dans [1..12] a=a.plus(b).sur(deux) b=cinq.sur(a) affiche a
De même, l'algorithme est aisément transposable aux nombres complexes:
deux = nouveau Complexe 2 cinq = nouveau Complexe 5 [a,b] = [ nouveau Complexe(1), nouveau Complexe(5)] jusqu'à ce que b.moins(a).module() < 1e-8 a=a.plus(b).sur(deux) b=cinq.sur(a) affiche a
racineDe = (x) -> [a,b] = [1,x] jusqu'à ce que (abs b-a) < 1e-16 a=(a+b)/2 b=x/a a affiche racineDe 5
La différence entre racineDe(x)
et la "vraie" racine carrée de x dépend
fortement de x :
racineDe = (x) -> [a,b]=[1,x] jusqu'à ce que (abs b-a) < 1e-5 a=(a+b)/2 b=x/a a erreur = (x) -> racineDe(x) - racine(x) dessineFonction erreur, 0.1, 20, 0, 1e-5, 'blue'
Pour avoir les nombres premiers jusqu'à 400, on enlève de la liste des entiers allant de 2 à 200, tous ceux qui ne sont pas premiers... Jusqu'à ce qu'il ne reste plus rien à enlever...
Cet algorithme se traduit ainsi en CoffeeScript:
crible = [2..400] pour diviseur dans [2..20] crible = (x pour x dans crible quand x <= diviseur ou x%diviseur est différent de 0)
Ensuite, pour savoir si un nombre est premier, on peut faire un test d'appartenance :
affiche 127 dans crible
Un cycliste très maladroit tourne autour d'une maison encerclée de fleurs, numérotées de 1 à 100. Il zigzague de telle manière que, chaque fois qu'il évite une fleur, il écrase la suivante. Et à chaque tour de la maison, il évite une fleur encore intacte sur deux pour écraser la fleur intacte qui suit. Quel est le numéro de la dernière fleur restante ?
On peut résoudre cet exercice avec un crible :
fleurs = [1..100] jusqu'à ce que fleurs.length == 1 fleurs = (fleurs[n] pour n dans [0...fleurs.length] quand n%2 est égal à 1) affiche fleurs
On choisit au hasard deux entiers x et y strictement supérieurs à 1, x étant le plus petit des deux, et on donne à Sam leur somme qui est inférieure à 100, et à Polly leur produit. Après un temps de réflexion suffisamment long, le dialogue suivant se déroule entre Sam et Polly:
On demande quels sont ces deux nombres
Pour la suite on aura besoin, à partir d'une somme (comme celles données à Sam),
de la liste des produits de ses deux termes. Pour cela on construit une fonction
prod
prod = (n) -> (x*(n-x) pour x dans [2..n-2]).unique()
Si Polly, au début, ne peut pas savoir quels sont les nombres, c'est qu'il y a au moins 2 façons d'obtenir le produit qu'on lui a donné. Pour obtenir la liste des produits possibles donnés à Polly, on peut donc faire ainsi :
produits = [] pour y dans [3...100] pour x dans [2...y] si x+y <= 100 produits.empile x*y polly = (p pour p dans produits quand (produits.compteLes p) >= 2)
Sam ne sachant pas non plus quels sont les nombres, en déduit que sa somme est celle de nombres qui sont tous dans la liste de Polly. Donc la liste des produits données à Sam est :
sam = (n pour n dans [5...100] quand (_.intersection prod(n), polly).length == prod(n).length)
L'algorithme suivant permet de savoir quelle est la somme donnée à Sam (un seul produit lui est associé) :
produits = _.flatten (prod(s) pour s dans sam) doublons = (p pour p dans produits quand (produits.compteLes p) > 1 ) pour s dans sam affiche "La somme #{s} a #{(_.difference prod(s), doublons).length} produits"
La variante suivante affecte une variable par cette somme :
produits = _.flatten (prod(s) pour s dans sam) doublons = (p pour p dans produits quand (produits.compteLes p) > 1 ) polly = (s pour s dans sam quand (_.difference prod(s), doublons).length == 1) somme = polly[0]
Une fois qu'on a la somme, on a aussi le produit, avec
produit = (_.difference prod(somme), doublons)[0] affiche produit
On peut trouver les deux nombres en bouclant avec un test:
solution = nouvel Ensemble pour y dans [3...100] pour x dans [2...y] si x+y est égal à somme et x*y est égal à produit solution.ajoute [x,y] affiche solution
En résumé, le script complet qui donne la solution est assez rapide :
prod = (n) -> (x*(n-x) pour x dans [2..n-2]).unique() produits = [] pour y dans [3...100] pour x dans [2...y] si x+y <= 100 produits.empile x*y polly = (p pour p dans produits quand (produits.compteLes p) >= 2) sam = (n pour n dans [5...100] quand (_.intersection prod(n), polly).length == prod(n).length) produits = _.flatten (prod(s) pour s dans sam) doublons = (p pour p dans produits quand (produits.compteLes p) > 1 ) polly = (s pour s dans sam quand (_.difference prod(s), doublons).length == 1) somme = polly[0] produit = (_.difference prod(somme), doublons)[0] solution = nouvel Ensemble pour y dans [3...100] pour x dans [2...y] si x+y est égal à somme et x*y est égal à produit solution.ajoute [x,y] affiche solution
au degré près:
arr3 = (x) -> arrondi(1000*x)/1000 affiche "|angle|sinus|" affiche "|#{n} |#{arr3 sinus n} |" pour n dans [0..90]
à la minute près:
arr3 = (x) -> arrondi(1000*x)/1000 affiche "|angle|sinus|" affiche "|#{arr3 n} |#{arr3 sinus n} |" pour n dans [0..1] par pas de 1/60
Remarque: On peut aussi faire
tableauValeurs sinus, [0..20]
arr3 = (x) -> arrondi(1000*x)/1000 affiche "|angle|cosinus|" affiche "|#{n} |#{arr3 cosinus n} |" pour n dans [0..90]
L'algorithme de dichotomie est efficace pour résoudre l'équation f(x)=0
f = (x) -> ln(x) - 1 [a,b]=[1,10] jusqu'à ce que b-a < 0.000001 m=(a+b)/2 si f(m) < 0 a = m sinon b = m affiche [a,b]
Résoudre une équation, c'est donner la liste de ses solutions; pour le second degré, il y a 0, 1 ou 2 solutions selon le signe du discriminant Δ :
SecondDegré = (a,b,c) -> S = nouvel Ensemble [] Delta = b*b-4*a*c r = laRacineDe Delta S.ajoute (-b-r)/(2*a) S.ajoute (-b+r)/(2*a) S affiche SecondDegré 1, 2, 1
Et au cas où Δ est négatif, on peut résoudre l'équation sur les complexes :
SecondDegré = (a,b,c) -> S = nouvel Ensemble [] Delta = b*b-4*a*c si Delta >= 0 r = laRacineDe Delta S.ajoute (-b-r)/(2*a) S.ajoute (-b+r)/(2*a) sinon r = nouveau Complexe 0, laRacineDe -Delta d = nouveau Complexe 2*a m = nouveau Complexe -b S.ajoute m.moins(r).sur(d) S.ajoute m.plus(r).sur(d) S affiche SecondDegré 1, -1, 1
Combien de fois faut-il lancer un dé équilibré pour que la probabilité d'avoir un 6 dépasse 0,99 ?
La condition de sortie se traduisant par le fait que la probabilité de ne pas avoir de 6 est en-dessous de 0,01; et comme si les lancers sont indépendants, cette probabilité suit une suite géométrique de raison 5/6:
proba = 1 nombreDeLancers=1 jusqu'à ce que proba < 0.01 proba *= 5/6 nombreDeLancers++ affiche "En lançant #{nombreDeLancers} fois le dé, la probabilité d'avoir un 6 vaut #{1-proba}"
On construit une liste (ou tableau) des termes successifs de la suite, pour représenter
graphiquement celle-ci. On remarque comme comme sa raison est inférieure à 1, la valeur
maximale est 1; donc on choisit 0 pour yMin
et 1 pour yMax
:
u=[1] pour n dans [1..20] u[n]=5/6*u[n-1] dessineSuite u, 20, 0, 1, 5, 'red'
On peut dessiner une suite géométrique par pavage de Truchet. Pour cela on écrit une matrice binaire à partir des termes successifs de la suite, et on la dessine sous forme de pavage de Truchet, ligne par ligne. Pour être sûr d'avoir des "0" en nombre suffisant à gauche (alignement des termes) on additionne une puissance de 2 et après conversion en binaire, on enlève le "1" initial :
pour n dans [0..15] u = puissance 3, n ligneTruchet ((puissance(2,24)+u).toString 2)[1..], 0, n*20
Les puissances de 3 donnent un motif intéressant :
Pour la version couleur, il faut alterner les lignes paires et impaires :
pour n dans [0..15] u = puissance 3, n if n%2==0 Lpair ((puissance(2,24)+u).toString 2)[1..], 0, n*20 else Limpair ((puissance(2,24)+u).toString 2)[1..], 0, n*20
Algorithme de Briggs pour calculer des logarithmes
log = (x) -> si x > 0 [r,n] = [x,0] jusqu'à ce que 0.999999 < r < 1.000001 n++ r=racine r r -= 1 r *= 2 pour k dans [1..n] r affiche log 2.7182818
En calculant un logarithme par l'algorithme de Briggs, on commet une erreur d'approximation (la différence entre log(x) et ln(x)) qui dépend de x; sa représentation graphique permet de voir comment :
log = (x) -> si x > 0 [r,n] = [x,0] jusqu'à ce que 0.999999 < r < 1.000001 n++ r = racine r r -= 1 r *= 2 pour k dans [1..n] r erreur = (x) -> log(x)-ln(x) dessineFonction erreur, 0.1, 10, 0, 0.000002, 'blue'
La suite Fn est définie par la relation de récurrence Fn+2=Fn+1+Fn. Si F0=F1=1, la suite est entièrement déterminée.
[a,b] = [1,1] pour n dans [1..10] [a,b] = [a+b,a] affiche "F(#{n})=#{b}"
nombreDor = (1 + racine 5)/2 pour n dans [1..10] b = puissance nombreDor, n b /= racine 5 b = arrondi b affiche "F(#{n})=#{b}"
Pour représenter graphiquement la suite de Fibonacci, on met ses termes successifs dans un tableau. Ce qui revient à les pousser l'un après l'autre dedans :
u = [1,1] pour n dans [2..30] u[n]=u[n-2]+u[n-1] dessineSuite u, 10, 0, 100, 5, 'red'
Les pavages de Truchet permettent de dessiner des labyrinthes à partir de suite de nombres binaires.
[a,b] = [1,1] pour n dans [1..16] [a,b] = [b,a+b] affiche truchet ((puissance(2,12)+b).toString 2)[1..]
Avec les nombres de Fibonacci en binaire, le résultat est assez intéressant :
╲╲╲╲╲╲╲╲╲╲╱╲ ╲╲╲╲╲╲╲╲╲╲╱╱ ╲╲╲╲╲╲╲╲╲╱╲╱ ╲╲╲╲╲╲╲╲╱╲╲╲ ╲╲╲╲╲╲╲╲╱╱╲╱ ╲╲╲╲╲╲╲╱╲╱╲╱ ╲╲╲╲╲╲╱╲╲╲╱╲ ╲╲╲╲╲╲╱╱╲╱╱╱ ╲╲╲╲╲╱╲╱╱╲╲╱ ╲╲╲╲╱╲╲╱╲╲╲╲ ╲╲╲╲╱╱╱╲╱╲╲╱ ╲╲╲╱╲╱╱╱╱╲╲╱ ╲╲╱╲╲╱╱╲╲╲╱╲ ╲╲╱╱╱╱╲╱╱╲╱╱ ╲╱╱╲╲╲╱╱╱╱╲╱ ╱╲╱╲╲╲╲╱╱╲╲╲
Cette variante est plus graphique (méandres) :
[a,b] = [1,1] pour n dans [0..23] [a,b] = [b,a+b] ligneTruchet ((puissance(2,24)+b).toString 2)[1..], 0, n*20
Lorsque Galilee lui donnait des cours, Cosme de Medicis, futur Grand-Duc de Toscane, lui a demandé comment ça se fait que lorsqu'on additionne les résultats de 3 dés, le 10 sort plus souvent que le 9, alors que
stats = nouveau Sac [] pour n dans [1..100000] stats.ajoute dé(6)+dé(6)+dé(6) trierDansTableau stats.effectifs diagrammeBatonsTrie stats.effectifs, 20000
loi = nouveau Sac [] loi.ajoute(a+b+c) pour a dans [1..6] pour b dans [1..6] pour c dans [1..6] d = puissance 6, 3 affiche "La probabilité d'avoir un 9 vaut #{loi.effectifs[9]/d}" affiche "La probabilité d'avoir un 10 vaut #{loi.effectifs[10]/d}"
Dans une lettre à Blaise Pascal, le Chevalier de Méré
lui a posé la question suivante: Comment se fait-il qu'en lançant
4 dés, on a plus d'une chance sur 2 d'avoir au moins un 6 ?
gains = 0 pour n dans [1..100000] jeu=(dé(6) pour n dans [1..4]) si (x pour x dans jeu quand x est égal à 6).length gains++ affiche gains/100000
gains=0 pour a dans [1..6] pour b dans [1..6] pour c dans [1..6] pour d dans [1..6] si a == 6 ou b == 6 ou c == 6 ou d == 6 gains++ d = puissance 6, 4 affiche gains/d
Comme la probabilité de ne pas avoir de 6 en lançant un dé est 5/6, et par indépendance des lancers de dés, la probabilité de na pas avoir de 6 en lançant 4 dés est le quatrième terme d'une suite géométrique de premier terme 5/6 et de raison 5/6:
u = 1 (u *= 5/6 pour n dans [1..4]) affiche 1-u
Au jeu de Dada (ou des "petits chevaux") on lance un dé jusqu'à ce qu'on ait un 6. Alors seulement, le petit cheval peut "sortir de l'écurie" (on peut commencer à jouer)
La variable aléatoire "nombre de fois qu'on doit lancer le dé pour avoir un 6" suit une loi géométrique de paramètre 1/6. Pour la simuler:
X = 0 X++ jusqu'à ce que dé(6) == 6 affiche X
Pour jouer plusieurs fois aux petits chevaux, on peut refaire tout ça dans une boucle; on en profite pour faire un tableau d'effectifs:
stats = nouveau Sac [] pour n dans [1..100] X = 0 X++ jusqu'à ce que dé(6) == 6 stats.ajoute X trierDansTableau stats.effectifs diagrammeBatonsTrie stats.effectifs, 20
La probabilité que l'algorithme précédent s'arrête au bout d'un temps fini est 1. Néanmoins, on prend moins de risque à tronquer X, en remplaçant tous les X supérieurs à 20 par des 0:
stats = nouveau Sac [] pour n dans [1..100] X = 0 X++ jusqu'à ce que dé(6) == 6 X = 0 si X > 20 stats.ajoute X trierDansTableau stats.effectifs diagrammeBatonsTrie stats.effectifs, 20
Tirer 5 cartes (une "main") dans un jeu de 32 cartes, c'est constituer un échantillon d'effectif 5 dans une population totale de 32. C'est donc la base de la statistique.
On vérifie que les 4 couleurs sont équiprobables :
couleur=['♦','♥','♠','♣'] urne = nouveau Sac urne.ajoute prendreAuHasardDans couleur pour n dans [1..1000] mettreDansTableau urne.effectifs diagrammeBatons urne.effectifs
Les 4 couleurs ont bien l'air équiréparties :
Remarque: On peut aussi représenter les effectifs comme une fonction qui, à chaque couleur de carte, associe un nombre :
On a fait sagittal urne.effectifs
pour avoir ce diagramme.
Si on répète 5 fois l'expérience de tirer une carte, on risque d'avoir plusieurs fois la même carte (tirage avec remise). Mais ce risque est minime:
couleur=['♦','♥','♠','♣'] valeur=['1','7','8','9','10','V','D','R'] jeu=[] pour c dans couleur pour v dans valeur jeu.empile "#{v}#{c}" main = tirageAvecRemise 5, jeu affiche main
couleur=['♦','♥','♠','♣'] valeur=['1','7','8','9','10','V','D','R'] jeu=[] pour c dans couleur pour v dans valeur jeu.empile "#{v}#{c}" main = tirageSansRemise 5, jeu affiche main
Cas particulier: Pour mélanger le jeu, on fait un tirage sans remise de 32 cartes :
couleur = ['♦','♥','♠','♣'] valeur = ['1','7','8','9','10','V','D','R'] jeu = [] pour c dans couleur pour v dans valeur jeu.empile "#{v}#{c}" affiche mélangée jeu
Pour savoir quelle est la probabilité d'avoir un carré d'as,
casPossibles = combinaison 32, 5 casFavorables = combinaison 28, 1 affiche casFavorables/casPossibles
Bien entendu on peut aussi avoir la valeur exacte de la probabilité:
casPossibles = combinaison 32, 5 casFavorables = combinaison 28, 1 affiche nouvelle Fraction casFavorables, casPossiblesOn peut exprimer le résultat en disant qu'on a une chance sur 7192 d'avoir un carré d'as.
La loi B(10,0.7) est la loi du nombre de boules rouges dans un tirage (avec remise) dans une urne contenant 70 % de boules rouges.
On peut utiliser deux sacs:
urne
, contient les boules rouges et bleues: C'est l'urne !stats
, contient les effectifs des nombres de boules rouges
obtenues dans 100 tirages avec remise de 10 boules extraites du sac précédent.urne = nouveau Sac [] urne.ajouteFois 70, 'rouge' urne.ajouteFois 30, 'bleu' stats = nouveau Sac [] stats.ajoute (urne.extraireAuHasard() pour n dans [1..10]).compteLes 'rouge' pour n dans [1..1000] trierDansTableau stats.effectifs diagrammeBatonsTrie stats.effectifs
Pour afficher les valeurs prises par la loi binomiale de paramètres 10 et 0,7 (les probabilités), on peut faire
affiche (binomiale 10, 0.7, k pour k dans [0..10])
En tirant 200 boules (avec remise) dans une urne contenant 70 % de boules rouges, un intervalle de fluctuation à 95 % pour le nombre de boules rouges parmi les 200 s'obtient par
affiche IntFluctBinom 200, 0.7
Pour calculer un intervalle de confiance sur une proportion p, on utilise de l'algorithmique "de base" (pas de boucles): Affectations, entrées, sorties et éventuellement un test si on veut améliorer.
Cet algorithme simplifié est vu en Seconde et en Terminales L/ES et S
N = entre "Taille de l'échantillon ?" ns = entre "Combien de succès ?" p = ns/N borneInférieure = p-1/laRacineDe N borneSupérieure = p+1/laRacineDe N affiche "Intervalle de confiance à 95 %: [#{borneInférieure};#{borneSupérieure}]"
Deux problèmes peuvent se poser:
N = entre "Taille de l'échantillon ?" ns = entre "Combien de succès ?" p = ns/N borneInférieure = p-1/laRacineDe N borneInférieure = 0 si borneInférieure < 0 borneSupérieure = p+1/laRacineDe N borneSupérieure = 1 si borneSupérieure > 1 if N > 24 affiche "Intervalle de confiance à 95 %: [#{borneInférieure};#{borneSupérieure}]"
Cet algorithme, plus précis notamment lorsque la proportion de succès dans l'échantillon est proche de 0 ou de 1, est au programme des Terminales STL/STI2D et des BTS :
N = entre "Taille de l'échantillon ?" ns = entre "Combien de succès ?" p = ns/N q = 1-p borneInférieure = p-1.96*racine(p*q/N) borneSupérieure = p+1.96*racine(p*q/N) affiche "Intervalle de confiance à 95 % : [#{borneInférieure};#{borneSupérieure}]"
Cette fois-ci, la formule n'est appliquée que si (avec les notations de l'algorithme), N×p et N×q sont tous les deux supérieurs à 5. De plus, l'échantillon doit être suffisamment grand (N≥25):
N = entre "Taille de l'échantillon ?" ns = entre "Combien de succès ?" p = ns/N q = 1-p borneInférieure = p-1.96*racine(p*q/N) borneSupérieure = p+1.96*racine(p*q/N) si N > 25 et N*p > 5 et N*q > 5 affiche "Intervalle de confiance à 95 % : [#{borneInférieure};#{borneSupérieure}]" sinon affiche "Il ne serait pas raisonnable de calculer un intervalle de confiance avec ces données"
Une urne contient des boules rouges (au moins une) et des boules bleues (au moins une). On répète un grand nombre de fois l'expérience suivante:
L'urne est modélisée par un sac. On initialise ce sac à un sac contenant une boule de chaque couleur. Ensuite, on va répéter 10 fois l'action d'extraire une boule au hasard (mais on ne l'enlève pas vraiment, on la regarde seulement), et d'ajouter le résultat dans l'urne :
urne = nouveau Sac ['rouge','bleue'] pour k dans [1..10] urne.ajoute urne.extraireAuHasard() affiche urne
Pour calculer le pourcentage de boules rouges, on divise le nombre de boules rouges par le cardinal (le nombre total de boules) de l'urne, et on multiplie par 100 :
urne = nouveau Sac ['rouge','bleue'] pour k dans [1..10] urne.ajoute urne.extraireAuHasard() affiche urne.effectifs.rouge/urne.cardinal()*100
La suite des valeurs successives du pourcentage de boules rouges est stockée, par empilements successifs, dans un tableau "stats". Puis dessinée avec ce script:
urne = nouveau Sac ['rouge','bleue'] stats = [50] pour k dans [1..1000] urne.ajoute urne.extraireAuHasard() stats.empile urne.effectifs.rouge/urne.cardinal()*100 dessineSuite stats, 1000, 0, 100, 2, 'red'
Le dessin montre une stabilisation à long terme mais pas nécessairement vers 50% de boules rouges :
On va regarder ensuite la répartition statistique des fréquences limites.
En admettant que la proportion de boules rouges est déjà stabilisée au bout de 200 itérations, on peut fabriquer un nouveau tableau statistique où empiler les 200ièmes pourcentages, et en faire l'histogramme :
stats = [] pour j dans [1..1000] urne = nouveau Sac ['rouge','bleue'] pour k dans [1..200] urne.ajoute urne.extraireAuHasard() stats.empile urne.effectifs.rouge/urne.cardinal()*100 histogramme stats, 0, 100, 10, 200
Avec une boule de chaque couleur au début, l'histogramme est plat :
Avec 5 boules bleues au départ, l'histogramme est celui d'une loi de probabilité moins uniforme :
stats = [] pour j dans [1..1000] urne = nouveau Sac ['rouge','bleue'] urne.ajouteFois 4, 'bleue' pour k dans [1..200] urne.ajoute urne.extraireAuHasard() stats.empile urne.effectifs.rouge/urne.cardinal()*100 histogramme stats, 0, 100, 20, 300
Et avec 10 boules de chaque couleur au début :
stats = [] pour j dans [1..1000] urne = nouveau Sac ['rouge','bleue'] urne.ajouteFois 9, 'bleue' urne.ajouteFois 9, 'rouge' pour k dans [1..200] urne.ajoute urne.extraireAuHasard() stats.empile urne.effectifs.rouge/urne.cardinal()*100 histogramme stats, 10, 90, 16, 250
on se rapproche d'une distribution normale :
Ne pas oublier, de temps en temps, de faire effaceDessin()
pour vider le graphique.
pour n dans [1..100] par pas de 2 x1 = 100+4*n y1 = 440 x2 = 100 y2 = 40+4*n dessineSegment x1, y1, x2, y2, 'black'
sommets = [] pour n dans [0...17] angle = n*8/17*360 sommets.empile [320+200*cosinus(angle),240+200*sinus(angle)] dessinePolygone sommets, 'blue', 'green'
N = 24 pour n dans [0...N] angle = n/N*360 cx = 320 + 100*cosinus(angle) cy = 240 + 100*sinus(angle) dessineCercle cx, cy, abs(100*sinus(angle)), 'blue'
A = nouveau Point 50, 240 B = nouveau Point 600, 20 C = nouveau Point 600, 460 M = nouveau Point dé(640), dé(480) pour n dans [1..1000] selon dé(3) quand 1 M = M.milieu A dessineCercle M.x, M.y, 2, 'red' quand 2 M = M.milieu B dessineCercle M.x, M.y, 2, 'blue' sinon M = M.milieu C dessineCercle M.x, M.y, 2, 'green'
A = nouveau Point 50, 240 B = nouveau Point 600, 20 C = nouveau Point 600, 460 gasket = (a,b,c,N) -> si N < 1 dessinePolygone [[a.x,a.y],[b.x,b.y],[c.x,c.y]], 'black', 'none' sinon gasket(a, a.milieu(b), a.milieu(c), N-1) gasket(b, b.milieu(c), b.milieu(a), N-1) gasket(c, c.milieu(a), c.milieu(b), N-1) gasket A, B, C, 6
L'algorithme de Box-Muler permet de simuler des variables aléatoires normales de paramètres respectifs (320;40) et (240;40) simultanément ce qui définit un nuage de 50 points, ici représenté en rouge, accompagné de son diagramme de Voronoi (le diagramme de Voronoi de deux points est leur médiatrice) :
nuage = [] pour n dans [1..50] T = alea()*2*pi R = -80*ln(alea()) nuage.empile [320+R*cos(T),240+R*sin(T)] dessineVoronoi nuage, 'darkGreen', 0.5
Ce script dessine un champ de vecteur (c'est-à-dire une fonction qui, à tout point du plan, associe un vecteur) :
pour r dans [1..16] pour t dans [1..r] angle = (t+0.5)/r*360 c = cosinus(angle) s = sinus(angle) flèche 320+16*r*c,240+16*r*s, 320+16*r*c-32*s,240+16*r*s+32*c, 4 dessinePoint 320+16*r*c,240+16*r*s, 6
L'origine des vecteurs est représentée par des billes, superflues :
On se place dans l’espace muni d’un repère orthonormé. On considère le plan P d’équation x − y + 3z + 1 = 0 et la droite D dont une représentation paramétrique est
On donne les points A(1 ; 1; 0), B(3 ; 0 ; −1) et C (7 ; 1 ; −2).
Pour entrer les définitions, on découvre rapidement que le plan P passe par le
point de coordonnées (0,-1,0) et on lit sur son équation les coordonnées d'un vecteur
normal de P; de même, on lit dans la représentation paramétrique de D les coordonnées
(0,1,-5) d'un point et les coordonnées (2,1,3) d'un vecteur directeur. On utilise
les notations $V
pour les points (qui sont traités comme des vecteurs),
$L
pour les droites (comme "line") et $P
pour les plans:
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2]
Pour vérifier ceci, on crée une droite qu'on appelle AB
à partir de la
représentation paramétrique puis on
vérifie qu'elle passe par A et par B:
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] affiche AB.contains A affiche AB.contains B
Comme la droite "AB" passe par A et par B, c'est bien la droite (AB)...
On va vérifier que les vecteurs directeurs des deux droites sont perpendiculaires :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] affiche D.direction.isPerpendicularTo AB.direction
On regarde si elles sont parallèles (auquel cas elles sont coplanaires, mais on a vu dans la proposition 2 qu'elles ne sont pas parallèles), et si elles sont sécantes, auquel cas elles sont coplanaires :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] affiche D.isParallelTo AB affiche D.intersects AB
On va d'abord vérifier que D coupe P :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] affiche D.intersects P
Maintenant que c'est fait, on va chercher où ils se coupent :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] affiche ((D.intersectionWith P).map (x)-> arrOdg x, 6).inspect()
Apparemment, D et P se coupent mais pas en E, les coordonnées du point d'intersection étant (3, 2.5, -0.5). On va vérifier que, si P passe par E, ce n'est pas le cas de D :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] E = $V [8,-3,-4] affiche D.contains E affiche P.contains E
D'ailleurs, on peut avoir les coordonnées (approximatives) du point de D le plus proche de E :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] AB = $L [5,-1,-2], [-2,1,1] E = $V [8,-3,-4] affiche (D.pointClosestTo E).inspect()
On définit le plan (ABC) par un de ses points (le point A) et deux de ses vecteurs, ceux allant de A respectivement à B et à C. Puis on regarde si ce plan est parallèle à P :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] ABC = $P A, B.subtract A, C.subtract A affiche P.isParallelTo ABC
On peut aussi vérifier que les trois points A, B et C sont à la même distance de P :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] ABC = $P A, B.subtract A, C.subtract A affiche (P.distanceFrom point for point in [A,B,C])
Bien bien bien, ils ne sont pas parallèles. Mais alors, dans ce cas, ils sont sécants !
Et puisqu'ils sont sécants, leur intersection est une droite dI
. On peut obtenir
un point et un vecteur directeur de cette droite :
P = $P [0,1,0], [1,-1,3] D = $L [0,1,-5], [2,1,3] A = $V [1,1,0] B = $V [3,0,-1] C = $V [7,1,-2] ABC = $P A, B.subtract A, C.subtract A dI = P.intersectionWith ABC affiche [dI.anchor.inspect(), dI.direction.x(1).inspect()]
On parle de récursivité lorsqu'un algorithme fait appel à lui-même.
La définition revient à dire que
puissRec = (base, exposant) -> si exposant est égal à 0 1 sinon base*puissRec base, exposant-1 affiche puissRec 2, 8
La factorielle de 0 est définie comme étant 1; sinon, n! = n × (n-1)!
factorielleRec = (n) -> si n est égal à 0 1 sinon n*factorielleRec n-1 affiche factorielleRec 10
Le jeu des tours de Hanoï, inventé par Édouard Lucas, comprend trois "tours", sur lesquelles sont empilés des cylindres (ou "disques" épais) de taille décroissante, et le but du jeu est d'amener les cylindres de la tour "source" vers la tour "destination" (à l'aide d'une tour "intermédiaire"), en respectant les règles suivantes:
Une solution de ce jeu peut se décrire récursivement, avec l'algorithme suivant:
hanoi = (n, source, interm, destination) -> si n est égal à 1 affiche "Déplacer de #{source} vers #{destination}" sinon hanoi n-1, source, destination, interm hanoi 1, source, interm, destination hanoi n-1, interm, source, destination hanoi 3, "gauche", "milieu", "droite"