Étude du grégarisme spatial



On veut étudier le caractère grégaire de plusieurs espèces.
Une espèce est grégaire si les individus ont tendance à se rapprocher les uns des autres. Dans le cas contraire, on dira que les individus ont une tendance solitaire.


Protocole de l’expérience

Deux individus d’une espèce sont placés dans un espace qui peut prendre diverses formes et peuvent se déplacer dans cet espace.
Un appareil relié à un ordinateur détermine à intervalle régulier la position de chacun des individus et calcule la distance entre eux.
Au bout d’un moment l’expérience s’arrête et l’ordinateur calcule la moyenne des distances recueillies.

Expériences

Chevaux

On place deux chevaux dans un pré carré de côté 20 mètres.
La distance moyenne entre eux est de 4 mètres.
Est-ce supérieur ou inférieur à la moyenne obtenue par des positions aléatoires indépendantes des chevaux ?
Les chevaux ont-ils une tendance grégaire ?
On répète l’expérience avec deux vieux chevaux et on obtient une moyenne de 6,3 mètres. Conclusion ?

Voir les éléments attendus

Algorithme

somme←0
Pour n de 1 à 10000 :

xA←au hasard de 0 à 20
yA←au hasard de 0 à 20
xB←au hasard de 0 à 20
yB←au hasard de 0 à 20
somme←somme+distance de A à B

moyenne←somme/10000

En langage Python

import random
import math
n=10000 #nombre d'experiences
cote=20 #longueur du cote du carre
somme=0 # pour calculer la moyenne
for i in range(0,n) :
    #premier cheval
    xA=random.random()*cote #entre 0 et 20
    yA=random.random()*cote
    #deuxieme cheval
    xB=random.random()*cote
    yB=random.random()*cote
    #
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

En langage Python avec le module graphique Tkinter

import random
import math
from Tkinter import *
canvas = Canvas(width=400, height=400, bg='white') #image : 400x400
canvas.pack(expand=YES, fill=BOTH)

n=200 #nombre d'experiences
cote=20 #longueur du cote du carre
somme=0 # pour calculer la moyenne
for i in range(0,n) :
    #premier cheval
    xA=random.random()*cote #entre 0 et 20
    yA=random.random()*cote
    #deuxieme cheval
    xB=random.random()*cote
    yB=random.random()*cote
    #
    canvas.create_line(20*xA,20*yA,20*xB,20*yB) #echelle x20
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

mainloop()

Blattes

Lorsqu’on place deux blattes dans une boite circulaire plate de rayon 10 cm, la moyenne des distances entre elles est d’environ 3,5 cm. Les blattes ont-elles une tendance grégaire ?

Voir les éléments attendus

Algorithme (haut niveau)

somme←0
Pour n de 1 à 10000 :

Choisir les coordonnées de A dans le cercle de centre (0,0) et de rayon 10 cm.
Choisir les coordonnées de B dans le cercle de centre (0,0) et de rayon 10 cm.
Calculer la longueur AB et l'ajouter à somme

FinPour

moyenne←somme/10000

L'écriture de cet algorithme permet de s'affranchir des difficultés techniques telle celle qui permet de s'assurer que les points choisis au hasard sont bien dans le cercle voulu.
On pourra pour cela :

  • Prendre les coordonnées dans un carré de côté 20 cm et faire passer un test pour s'assurer que le point est aussi dans le cercle.
  • Utiliser les coordonnées polaires.

Algorithme (bas niveau avec test de présence dans le cercle voulu)

somme←0
Pour n de 1 à 10000 :

Répéter

xA←au hazard de -10 à 10
yA←au hazard de -10 à 10

Jusqu'à ce que xA²+yA²≤10²
Répéter

xB←au hazard de -10 à 10
yB←au hazard de -10 à 10

Jusqu'à ce que xB²+yB²≤10²
somme←somme+racine((xB-xA)²+(yB-yA)²)

FinPour

Moyenne←somme/10000

En langage Python

import random
import math

n=10000 #nombre d'experiences
somme=0 #pour calculer la moyenne
#En python, il n'y a pas de boucle du type repeter...jusqu'a ce que...
#Alors il faut ruser : on place les points en dehors du cercle pour
#etre sur de rentrer dans la boucle faire...tant que... (boucle while)
for i in range(0,n) :
    #premier insecte
    xA=10;yA=10 #En dehors du cercle !
    while ((xA*xA+yA*yA)>100) :
        xA=random.uniform(-10,10) # entre -10 et 10 avec loi uniforme
        yA=random.uniform(-10,10)
    #deuxieme insecte
    xB=10;yB=10
    while ((xB*xB+yB*yB)>100) :
        xB=random.uniform(-10,10) # entre -10 et 10 avec loi uniforme
        yB=random.uniform(-10,10)
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

En langage Python avec le module graphique Tkinter

import random
import math
from Tkinter import *
canvas = Canvas(width=400, height=400, bg='white')
canvas.pack(expand=YES, fill=BOTH)

n=500
somme=0
for i in range(0,n) :
    #premier insecte
    xA=10;yA=10 #En dehors du cercle !
    while ((xA*xA+yA*yA)>100) :
        xA=random.uniform(-10,10)
        yA=random.uniform(-10,10)
    #deuxieme insecte
    xB=10;yB=10
    while ((xB*xB+yB*yB)>100) :
        xB=random.uniform(-10,10)
        yB=random.uniform(-10,10)
    canvas.create_line(200+20*xA,200+20*yA,200+20*xB,200+20*yB)
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

mainloop()

Algorithme (bas niveau avec coordonnées polaires - Version naïve)

somme←0
Pour n de 1 à 10000 :

angleA←au hasard de 0 à 2π
rayonA←au hasard de 0 à 10
xA←rayonA×cos(angleA)
yA←rayonA×sin(angleA)
angleB←au hasard de 0 à 2π
rayonB←au hasard de 0 à 10
xB←rayonB×cos(angleB)
yB←rayonB×sin(angleB)
somme←somme+racine((xB-xA)²+(yB-yA)²)

FinPour

Moyenne←somme/10000

En langage Python

import random
import math
n=10000
rayon=10
somme=0
for i in range(0,n) :
    #premier insecte
    angleA=random.random()*2*math.pi #random() : entre 0 et 1
    rayonA=random.random()*rayon
    xA=rayonA*math.cos(angleA)
    yA=rayonA*math.sin(angleA)
    #deuxieme insecte
    angleB=random.random()*2*math.pi
    rayonB=random.random()*rayon
    xB=rayonB*math.cos(angleB)
    yB=rayonB*math.sin(angleB)
    #
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

En langage Python avec le module graphique Tkinter

import random
import math
from Tkinter import *
canvas = Canvas(width=400, height=400, bg='white')
canvas.pack(expand=YES, fill=BOTH)

n=500
rayon=10
somme=0
for i in range(0,n) :
    #premier insecte
    angleA=random.random()*2*math.pi #random() : entre 0 et 1
    rayonA=random.random()*rayon
    xA=rayonA*math.cos(angleA)
    yA=rayonA*math.sin(angleA)
    #deuxieme insecte
    angleB=random.random()*2*math.pi
    rayonB=random.random()*rayon
    xB=rayonB*math.cos(angleB)
    yB=rayonB*math.sin(angleB)
    #
    canvas.create_line(200+20*xA,200+20*yA,200+20*xB,200+20*yB)
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

mainloop()

Avec cette version, un problème apparaît : les deux algorithmes ne donnent pas les mêmes résultats. Une discussion peut être engagée pour en déterminer la cause : choisir un point dans le carré et vérifier après coup s'il est dans le cercle est-il équivalent à choisir directement un point dans le cercle ? Ou bien est-ce un problème avec l'utilisation des coordonnées polaires ?
En mettant côte à côte les deux dessins obtenus, la solution apparaît :


Dessin obtenu avec l'algorithme avec test de présence dans le cercle


Dessin obtenu avec l'algorithme avec les coordonnées polaires

Dans l'algorithme avec les coordonnées polaires, les points vers le centre du cercle sont sur-représentés. Cela vient du fait qu'en prenant un point au hasard dans le cercle, la distance au centre du cercle ne suit pas une loi uniforme.
La probabilité de présence dans un disque dépend de l'aire de ce disque, elle-même étant fonction du carré de son rayon.
Une solution est de redresser le choix de la distance au centre.
Pour cela, on peut choisir le rayon selon la racine carrée d'une loi uniforme. (Voir des explications)

On obtient l'algorithme suivant :

Algorithme (bas niveau avec coordonnées polaires – Version corrigée)

somme←0
Pour n de 1 à 10000 :

angleA←au hasard de 0 à 2π
rayonA←racine(au hasard de 0 à 1) x 10
xA←rayonA×cos(angleA)
yA←rayonA×sin(angleA)
angleB←au hasard de 0 à 2π
rayonB←racine(au hasard de 0 à 1) x 10
xB←rayonB×cos(angleB)
yB←rayonB×sin(angleB)
somme←somme+racine((xB-xA)²+(yB-yA)²)

FinPour

Moyenne←somme/10000

En langage Python

import random
import math
n=10000
rayon=10
somme=0
for i in range(0,n) :
    #premier insecte
    angleA=random.random()*2*math.pi
    rayonA=math.sqrt(random.random())*rayon #redressement
    xA=rayonA*math.cos(angleA)
    yA=rayonA*math.sin(angleA)
    #deuxieme insecte
    angleB=random.random()*2*math.pi
    rayonB=math.sqrt(random.random())*rayon #redressement
    xB=rayonB*math.cos(angleB)
    yB=rayonB*math.sin(angleB)
    #
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

Complément pour le professeur : une autre démarche possible

Plutôt que de retirer les coordonnées de façon aléatoire à chaque fois, on simule une marche aléatoire : on part de la position précédente et on se déplace dans une direction aléatoire d'une distance donnée.

import random
import math
from Tkinter import *
canvas = Canvas(width=400, height=400, bg='white')
canvas.pack(expand=YES, fill=BOTH)

n=3000
somme=0

#premier insecte : choix du depart
xA=10;yA=10 #En dehors du cercle !
while ((xA*xA+yA*yA)>100) :
    xA=random.uniform(-10,10) # entre -10 et 10
    yA=random.uniform(-10,10)
#deuxieme insecte : choix du depart
xB=10;yB=10
while ((xB*xB+yB*yB)>100) :
    xB=random.uniform(-10,10) # entre -10 et 10
    yB=random.uniform(-10,10)
for i in range (0,n) :
    #On stoque les coordonnees
    oldxA=xA;oldyA=yA;oldxB=xB;oldyB=yB
    angleA=random.random()*2*math.pi  #angle de deplacement
    xA=xA+.5*math.cos(angleA) #deplacement
    yA=yA+.5*math.sin(angleA)
    if (xA*xA+yA*yA>100) : #si on sort du cercle...
        norme=math.sqrt(xA*xA+yA*yA) #alors on se replace au bord
        xA=xA*10/norme
        yA=yA*10/norme
    angleB=random.random()*2*math.pi
    xB=xB+.5*math.cos(angleB)
    yB=yB+.5*math.sin(angleB)
    if (xB*xB+yB*yB>100) :
        norme=math.sqrt(xB*xB+yB*yB)
        xB=xB*10/norme
        yB=yB*10/norme
    canvas.create_line(200+20*xA,200+20*yA,200+20*oldxA,200+20*oldyA,fill="red")
    canvas.create_line(200+20*xB,200+20*yB,200+20*oldxB,200+20*oldyB,fill="blue")
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

mainloop()

On peut modifier le comportement des insectes pour instaurer le grégarisme de la façon suivante : lorsqu'un déplacement tend à s'éloigner du congénère, le déplacement est moindre (de 20% dans l'exemple ci-dessous)

import random
import math
from Tkinter import *
canvas = Canvas(width=400, height=400, bg='white')
canvas.pack(expand=YES, fill=BOTH)

n=3000
somme=0

#premier insecte
xA=10;yA=10 #En dehors du cercle !
while ((xA*xA+yA*yA)>100) :
    xA=random.uniform(-10,10) # entre -10 et 10 avec une loi uniforme
    yA=random.uniform(-10,10)
    #deuxieme insecte
xB=10;yB=10
while ((xB*xB+yB*yB)>100) :
    xB=random.uniform(-10,10) # entre -10 et 10 avec une loi uniforme
    yB=random.uniform(-10,10)
for i in range (0,n) :
    oldxA=xA;oldyA=yA;oldxB=xB;oldyB=yB
    angleA=random.random()*2*math.pi
    xA=xA+.5*math.cos(angleA)
    yA=yA+.5*math.sin(angleA)
    #teste si le deplacement eloigne les points
    if ((xB-xA)**2+(yB-yA)**2>(xB-oldxA)**2+(yB-oldyA)**2) :
        xA=oldxA+.4*math.cos(angleA) #dans ce cas, le deplacement
        yA=oldyA+.4*math.sin(angleA) #est moindre
    if (xA*xA+yA*yA>100) :
        norme=math.sqrt(xA*xA+yA*yA)
        xA=xA*10/norme
        yA=yA*10/norme
    angleB=random.random()*2*math.pi
    xB=xB+.5*math.cos(angleB)
    yB=yB+.5*math.sin(angleB)
    if ((xB-xA)**2+(yB-yA)**2>(oldxB-xA)**2+(oldyB-yA)**2) :
        xB=oldxB+.4*math.cos(angleB)
        yB=oldyB+.4*math.sin(angleB)
    if (xB*xB+yB*yB>100) :
        norme=math.sqrt(xB*xB+yB*yB)
        xB=xB*10/norme
        yB=yB*10/norme
    canvas.create_line(200+20*xA,200+20*yA,200+20*oldxA,200+20*oldyA,fill="red")
    canvas.create_line(200+20*xB,200+20*yB,200+20*oldxB,200+20*oldyB,fill="blue")
    distance=math.sqrt((xB-xA)*(xB-xA)+(yB-yA)*(yB-yA))
    somme=somme+distance
print("Moyenne : "+str(somme/n))

mainloop()

En modifiant les paramètres, on peut simuler une tendance à l'éloignement (pour un éloignement, on augmente la distance de déplacement de 20%) :

Plusieurs situations

Espèce Description du champ d’expérimentation distance moyenne mesurée et calculée
Scarabée Fond d’une boite de forme circulaire de rayon 15 cm 9,5 cm
Caméléon Fond d’une boite de forme rectangulaire de dimensions 1 m par 60 cm 105 cm
Humain 10 sièges alignés dans une salle d’attente 3 chaises vides d’écart
Crevette Aquarium de dimensions 80 cm par 80 cm par 60 cm 25 cm

En classe

Objectifs

Seconde : Distance entre deux points du plan.
Terminale : Module et argument d’un nombre complexe et son interprétation géométrique ; notion de loi de densité

Documents

Déroulement possible en classe (à adapter selon les besoins, les objectifs) : document élève (fichier source LaTeX et ses annexes) ; fichier d’exemple (chevaux)

Travaux d’élèves

Discussion sur le nombre d’expériences :

Distance entre deux points :

Changement de modèle (un élève se demande si le modèle présenté est équivalent à celui avec 3 chevaux) :

Tagués avec : , ,
Publié dans Activités, Activités pour le lycée, Algorithmique