INTRODUCTION |
On utilise fréquemment des simulations informatiques pour prédire le comportement futur d’un système sur la base d’observations ponctuelles ou s’étendant sur un certain laps de temps dans un passé récent. De telles prédictions peuvent être d’une grande importance stratégique et nous avertir suffisamment tôt d’un scénario pouvant conduire à une catastrophe, ce qui permet par exemple d’envisager des mesures préventives. Dans ce domaine, les sujets qui préoccupent beaucoup notre société sont par exemple le changement climatique global et l’augmentation de la population mondiale. On conçoit une population comme un système d’individus dont le nombre change au fil du temps en fonction de mécanismes internes, d’interactions, et d’influences externes. Si l’on néglige les influences extérieures, on parle de système fermé. Pour bon nombre de populations, la variation de la population au temps t est proportionnelle à sa taille au temps t. La variation de la valeur actuelle est calculée à partir du taux de variation de la manière suivante : nouvelle valeur - ancienne valeur = ancienne valeur * taux de variation * intervalle de temps Puisque le membre de gauche représente la différence entre la nouvelle valeur et l’ancienne, cette relation est appelée équation aux différences. Le taux d’accroissement peut également être interprété comme une probabilité de croissance par individu et par unité de temps. Si celui–ci est négatif, c’est que la population est en déclin. Le taux d’accroissement peut bien entendu changer au cours du temps.
|
CROISSANCE EXPONENTIELLE |
Les projections concernant les variations de population sont d’un intérêt primordial et peuvent affecter de manière très significative la prise de décisions politiques. Le dernier débat en date est celui de la régulation de la proportion d’étrangers dans la population. L’OFS (Office Fédéral de la Statistique) publie chaque année le nombre d’habitants en Suisse. Les valeurs pour les années 2010 et 2011 sont les suivantes (source:: http://www.bfs.admin.ch, mot-clé: STAT-TAB):2010: Total z0 = 7 870 134, parmi lesquels s0 = 6 103 857 sont suisses Peut-on échafauder une prédiction de la proportion entre suisses et étrangers pour les 50 prochaines années à partir de cette information ? Il faudrait d’abord calculer le nombre d’étrangers avec a0 = z0 - s0 et a1 = z1 - s0et, à partir de ces valeurs, trouver le taux d’accroissement annuel entre 2010 et 2011 pour les habitants suisses et les étrangers.
from gpanel import * # source: Swiss Federal Statistical Office, STAT-TAB z2010 = 7870134 # Total 2010 z2011 = 7954662 # Total 2011 s2010 = 6103857 # Swiss 2010 s2011 = 6138668 # Swiss 2011 def drawGrid(): # Horizontal for i in range(11): y = 2000000 * i line(0, y, 50, y) text(-3, y, str(2 * i)) # Vertical for k in range(11): x = 5 * k line(x, 0, x, 20000000) text(x, -1000000, str(int(x + 2010))) def drawLegend(): setColor("lime green") y = 21000000 move(0, y) draw(5, y) text("Swiss") setColor("red") move(15, y) draw(20, y) text("foreigner") setColor("blue") move(30, y) draw(35, y) text("Total") makeGPanel(-5, 55, -2000000, 22000000) title("Population growth extended") drawGrid() drawLegend() a2010 = z2010 - s2010 # foreigners 2010 a2011 = z2011 - s2011 # foreigners 2011 lineWidth(3) setColor("blue") line(0, z2010, 1, z2011) setColor("lime green") line(0, s2010, 1, s2011) setColor("red") line(0, a2010, 1, a2011) rs = (s2011 - s2010) / s2010 # Swiss growth rate ra = (a2011 - a2010) / a2010 # foreigners growth rate # iteration s = s2011 a = a2011 z = s + a sOld = s aOld = a zOld = z for i in range(0, 49): s = s + rs * s # model assumptions a = a + ra * a # model assumptions z = s + a setColor("blue") line(i + 1, zOld, i + 2, z) setColor("lime green") line(i + 1, sOld, i + 2, s) setColor("red") line(i + 1, aOld, i + 2, a) zOld = z sOld = s aOld = a
|
MEMENTO |
Comme le montrent les chiffres, la proportion d’étrangers double entre 2010 et 2035, de sorte qu’elle double en seulement 25 ans et qu’elle quadruple si l’on ajoute encore 25 ans supplémentaires. Il est évident que la taille de la population augmente alors bien plus vite que proportionnellement au temps puisque son taux d’accroissement est constant. Si T est le temps nécessaire pour que la population double, la taille y après un temps t vaut, pour une population de taille initiale A,
Du fait que la variable temporelle se trouve à l’exposant, cette courbe présente une croissance exponentielle extrêmement rapide. |
CROISSANCE LIMITÉE |
On peut comprendre le déroulement de cette expérience à l’aide d’un modèle dans lequel la croissance exponentielle parvient à saturation. On peut considérer que le taux d’accroissement décroit linéairement avec l’augmentation de la population y jusqu’à ce qu’il soit nul pour une certaine valeur de saturation m.
En utilisant la valeur initiale y0 = 9.6 mg, la valeur de saturation m = 662 mg et le taux d’accroissement initial r0 = 0.62 /h, on obtient une bonne corrélation entre la théorie et l’expérience. from gpanel import * z = [9.6, 18.3, 29.0, 47.2, 71.1, 119.1, 174.6, 257.3, 350.7, 441.0, 513.3, 559.7, 594.8, 629.4, 640.8, 651.1, 655.9, 659.6, 661.8] def r(y): return r0 * (1 - y / m) r0 = 0.62 y = 9.6 m = 662 makeGPanel(-2, 22, -100, 1100) title("Bacterial growth") drawGrid(0, 20, 0, 1000) lineWidth(2) for n in range(0, 19): move(n, z[n]) setColor("black") fillCircle(0.2) if n > 0: dy = y * r(y) yNew = y + dy setColor("lime green") line(n - 1, y, n, yNew) y = yNew
|
MEMENTO |
En supposant une diminution linéaire du taux d’accroissement, on obtient une courbe de saturation « en S » typique de l’évolution d’une population. On parle également de croissance logistique ou de courbe sigmoïde. |
LIFE TABLES |
Une manière possible d’estimer la santé d’une population est de considérer la probabilité de dépasser un certain âge ou de mourir à un certain âge. Si l’on veut analyser la distribution des âges au sein de la population suisse, on peut utiliser des données actuelles publiées par l’OFS, à savoir les tables de mortalité (source: http://www.bfs.admin.ch, keyword: STAT-TAB).
Ces dernières mettent en évidence les probabilités observées (qx et qy) pour les hommes et les femmes de mourir à un certain âge, en fonction du sexe. La manière dont ces tables sont construites est relativement simple à comprendre : pour chaque tranche d’âge d’une année (entre 0 et 1 ans, entre 1 et 2 ans, etc.), on considère le nombre de morts survenues l’année dernière chez les hommes d’une part et chez les femmes de l’autre. On divise ensuite chacun de ces nombres par le nombre d’individus total de cette tranche d’âge au début de l’année.
import exceptions from gpanel import * def readData(filename): table = [] fData = open(filename) while True: line = fData.readline().replace(" ", "").replace("'", "") if line == "": break line = line[:-1] # remove trailing \n try: q = float(line) except exceptions.ValueError: break table.append(q) fData.close() return table makeGPanel(-10, 110, -0.1, 1.1) title("Mortality probability (blue -> male, red -> female)") drawGrid(0, 100, 0, 1.0) qx = readData("qx.dat") qy = readData("qy.dat") for t in range(101): setColor("blue") p = qx[t] line(t, 0, t, p) setColor("red") q = qy[t] line(t + 0.2, 0, t + 0.2, q)
|
MEMENTO |
|
ÉVOLUTION TEMPORELLE D’UNE POPULATION |
import exceptions from gpanel import * n = 10000 # size of the population def readData(filename): table = [] fData = open(filename) while True: line = fData.readline().replace(" ", "").replace("'", "") if line == "": break line = line[:-1] # remove trailing \n try: q = float(line) except exceptions.ValueError: break table.append(q) fData.close() return table makeGPanel(-10, 110, -1000, 11000) title("Population behavior/predictions (blue -> male, red -> female)") drawGrid(0, 100, 0, 10000) qx = readData("qx.dat") qy = readData("qy.dat") x = n # males y = n # females for t in range(101): setColor("blue") rx = qx[t] x = x - x * rx line(t, 0, t, x) setColor("red") ry = qy[t] y = y - y * ry line(t + 0.2, 0, t + 0.2, y) |
ESPÉRANCE DE VIE DES FEMMES ET DES HOMMES |
Il apparaît clairement de la précédente analyse que les femmes vivent plus longtemps que les hommes. On peut également exprimer cette différence à l’aide d’une seule grandeur appelée espérance de vie. Il s’agit de l’âge moyen atteint par les femmes et par les hommes. Rappelons-nous brièvement la manière dont une moyenne, par exemple la moyenne des notes d’une classe, est définie : on calcule la somme s des notes de l’ensemble des étudiants que l’on divise par le nombre n d’étudiants. Par souci de simplicité, supposons que toutes les notes sont des nombres entiers compris entre 1 et 6, de sorte que l’on peut calculer s de la manière suivante: s = nombre d’étudiants avec note 1 * 1 + nombre d’étudiants avec note 2 * 2 + ... nombre d’étudiants avec note 6 * 6 ou, de manière plus générale: moyenne = somme sur (fréquences valeur * valeur) divisé par le nombre total Si on lit les fréquences à partir d’une distribution de fréquences h de la valeur x (dans notre cas, il s’agit des notes entre 1 et 6), on utilise plutôt le terme d’espérance mathématique pour désigner la moyenne et on peut écrire
Comme vous pouvez le constater, les fréquences hi sont pondérées dans la sommepar la valeur xi L’espérance de vie n’est rien d’autre que l’espérance mathématique de l’âge auquel les femmes et les hommes meurent. Pour calculer cette valeur à l’aide d’une simulation informatique, on commence avec un certain nombre d’hommes et de femmes (n = 10000) et on détermine le nombre d’hommes (hx)et de femmes (hy) qui meurt entre l’âge t et t+1. Evidemment ces nombres peuvent être exprimés de la manière suivante en utilisant la taille au temps t de la population x et y livrés par le programme précédent ainsi que les taux de mortalité rx et ry des hommes, respectivement des femmes: hx = x * rx bzw. hy = y * ry
n = 10000 # size of the population def readData(filename): table = [] fData = open(filename) while True: line = fData.readline().replace(" ", "") if line == "": break line = line[:-1] # remove trailing \n try: q = float(line) except exceptions.ValueError: break table.append(q) fData.close() return table qx = readData("qx.dat") qy = readData("qy.dat") x = n y = n xSum = 0 ySum = 0 for t in range(101): rx = qx[t] x = x - x * rx mx = x * rx # male deaths xSum = xSum + mx * t # male sum ry = qy[t] y = y - y * ry my = y * ry # female deaths ySum = ySum + my * t # female sum print "Male life expectancy:", xSum / 10000 print "Female life expectancy:", ySum / 10000 Les données de la population suisse révèlent une espérance de vie de 76 ans pour les hommes et de 81 ans pour les femmes. |
PYRAMIDE DES ÂGES |
import exceptions from gpanel import * def readData(filename): table = [] fData = open(filename) while True: line = fData.readline().replace(" ", "").replace("'", "") if line == "": break line = line[:-1] # remove trailing \n try: q = float(line) except exceptions.ValueError: break table.append(q) fData.close() return table def drawAxis(): text(0, -3, "0") line(0, 0, 0, 100) text(0, 103, "100") makeGPanel(-100000, 100000, -10, 110) title("Population pyramid (green -> male, red -> female)") lineWidth(4) zx = readData("zx.dat") zy = readData("zy.dat") for t in range(101): setColor("red") x = zx[t] line(0, t, -x, t) setColor("darkgreen") y = zy[t] line(0, t, y, t) setColor("black") drawAxis()
|
MEMENTO |
On repère facilement les baby-boomers nés dans les années 1955 – 1965 (entre 47 et 57 ans). |
MODIFICATION DE LA DISTIBUTION DES ÂGES |
Le programme permet d'avancer d'une année par une simple pression de n’importe quelle touche du clavier. import exceptions from gpanel import * k = 2.0 def readData(filename): table = [] fData = open(filename) while True: line = fData.readline().replace(" ", "").replace("'", "") if line == "": break line = line[:-1] # remove trailing \n try: q = float(line) except exceptions.ValueError: break table.append(q) fData.close() return table def drawAxis(): text(0, -3, "0") line(0, 0, 0, 100) text(0, 103, "100") lineWidth(1) for y in range(11): line(-80000, 10* y, 80000, 10 * y) text(str(10 * y)) def drawPyramid(): clear() title("Number of children: " + str(k) + ", year: " + str(year) + ", total population: " + str(getTotal())) lineWidth(4) for t in range(101): setColor("red") x = zx[t] line(0, t, -x, t) setColor("darkgreen") y = zy[t] line(0, t, y, t) setColor("black") drawAxis() repaint() def getTotal(): total = 0 for t in range(101): total += zx[t] + zy[t] return int(total) def updatePop(): global zx, zy zxnew = [0] * 110 zynew = [0] * 110 # getting older and dying for t in range(101): zxnew[t + 1] = zx[t] - zx[t] * qx[t] zynew[t + 1] = zy[t] - zy[t] * qy[t] # making a baby r = k / 20 nbMother = 0 for t in range(20, 40): nbMother += zy[t] zxnew[0] = r / 2 * nbMother zynew[0] = zxnew[0] zx = zxnew zy = zynew makeGPanel(-100000, 100000, -10, 110) zx = readData("zx.dat") zy = readData("zy.dat") qx = readData("qx.dat") qy = readData("qy.dat") year = 2012 enableRepaint(False) while True: drawPyramid() getKeyWait() year += 1 updatePop()
|
MEMENTO |
On observe que le futur de la population dépend sensiblement du nombre k. Même avec la valeur k = 2, la population va décroître sur le long terme. Pour éviter le clignotement de l’écran lors de la pression sur la touche du clavier, il faut désactiver le rendu automatique à l’aide de l’appel enableRepaint(False). Dans la fonction drawPyramid() l’appel clear() ne va alors supprimer le graphique que de la mémoire tampon hors écran. Le rendu ne sera alors effectué à l’écran qu’après la fin du calcul de repaint(). |
EXERCICES |
|
MATÉRIEL SUPPLÉMENTAIRE |
SYSTÈME PROIE-PRÉDATEUR |
Il est très intéressant de considérer le comportement de deux populations vivant dans un même écosystème et interagissant l’une avec l’autre. Considérons le scénario suivant : des lapins et des renards cohabitent dans un territoire fermé. Les lapins se multiplient à un taux constant rx. Si un renard croise un lapin il y a une certaine probabilité qu’il le mange. À leur tour, les renards présentent un taux de mortalité ry et leur taux de croissance est déterminé par leur consommation de lapins. En supposant que la probabilité qu’un renard croise un lapin soit proportionnelle au produit entre le nombre de lapins et le nombre de renards, on obtient deux équations aux différences pour x et y [plus... Ces équations traduisent mathématiquement les lois de Lotka-Volterra].
from gpanel import * rx = 0.08 ry = 0.2 gx = 0.002 gy = 0.0004 def dx(): return rx * x - gx * x * y def dy(): return -ry * y + gy * x * y x = 500 y = 20 makeGPanel(-20, 220, -200, 2200) title("Predator-Prey system (red: bunnies, blue: foxes)") drawGrid(0, 200, 0, 2000) lineWidth(2) for n in range(200): xNew = x + dx() yNew = y + dy() setColor("red") line(n, x, n + 1, xNew) setColor("blue") line(n, y, n + 1, yNew) x = xNew y = yNew
|
MEMENTO |
Le nombre de lapins et de renards est sans arrêt en train de fluctuer. Qualitativement, ce processus cyclique peut être interprété de la manière suivante : puisque les renards mangent les lapins, leur population est en nette croissance lorsqu’il y a beaucoup de lapins. Puisque cela a tendance à décimer la population de lapins, la reproduction des renards ralentit. Ce déclin des renards permet aux lapins de se reproduire à nouveau, pratiquement au-delà de toute limite. |
EXERCICES |
|