{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Exercices expliqu\u00e9s de programmation\n", "\n", "Quelques exercices autour de la copie de liste, du temps de calcul, de l'h\u00e9ritage."]}, {"cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [{"data": {"text/html": ["
run previous cell, wait for 2 seconds
\n", ""], "text/plain": [""]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["from jyquickhelper import add_notebook_menu\n", "add_notebook_menu()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Copie de listes\n", "\n", "La fonction ``somme`` est cens\u00e9e faire la concat\u00e9nation de toutes les listes contenues dans ``ens``. Le r\u00e9sultat retourn\u00e9 est effectivement celui d\u00e9sir\u00e9 mais la fonction modifie \u00e9galement la liste ``ens``, pourquoi ?"]}, {"cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["[0, 1, 2, 3]\n", "[[0, 1, 2, 3], [2, 3]]\n"]}], "source": ["def somme(tab):\n", " l = tab[0]\n", " for i in range(1, len(tab)):\n", " l += tab [i]\n", " return l\n", "\n", "ens = [[0,1],[2,3]] \n", "print(somme(ens))\n", "print(ens)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le probl\u00e8me vient du fait qu'une affectation en *python* (seconde ligne de la fonction ``somme`` ne fait pas une copie mais cr\u00e9e un second identificateur pour d\u00e9signer la m\u00eame chose. Ici, ``l`` et ``tab[0]`` d\u00e9signent la m\u00eame liste, modifier l'une modifie l'autre. Ceci explique le r\u00e9sultat. Pour corriger, il fallait faire une copie explicite de ``tab[0]`` :"]}, {"cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["[0, 1, 2, 3]\n", "[[0, 1], [2, 3]]\n"]}], "source": ["import copy ###### ligne ajout\u00e9e\n", "def somme(tab):\n", " l = copy.copy (tab[0]) ###### ligne modifi\u00e9e\n", " for i in range(1, len (tab)):\n", " l += tab[i]\n", " return l\n", "\n", "ens = [[0,1],[2,3]] \n", "print(somme(ens))\n", "print(ens)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Il \u00e9tait possible, dans ce cas, de se passer de copie en \u00e9crivant :"]}, {"cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["[0, 1, 2, 3]\n", "[[0, 1], [2, 3]]\n"]}], "source": ["def somme(tab) :\n", " l = [] ###### ligne modifi\u00e9e\n", " for i in range (0, len (tab)) : ###### ligne modifi\u00e9e\n", " l += tab [i]\n", " return l\n", "\n", "ens = [[0,1],[2,3]] \n", "print(somme(ens))\n", "print(ens)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Erreur de logique\n", "\n", "Le programme suivant fonctionne mais le r\u00e9sultat n'est pas celui escompt\u00e9. "]}, {"cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [{"data": {"text/plain": ["['un', 'deux', 'deux', 'deux', 'cinq']"]}, "execution_count": 6, "metadata": {}, "output_type": "execute_result"}], "source": ["l = [\"un\", \"deux\", \"trois\", \"quatre\", \"cinq\"]\n", "\n", "for i in range (0,len (l)) :\n", " mi = i\n", " for j in range (i, len (l)) :\n", " if l[mi] < l [j] : mi = j\n", " e = l [i]\n", " l [mi] = l [i]\n", " l [i] = e\n", "\n", "l"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Ce programme est cens\u00e9 effectuer un tri par ordre alphab\u00e9tique **d\u00e9croissant**. Le probl\u00e8me intervient lors de la permutation de l'\u00e9l\u00e9ment ``l[i]`` avec l'\u00e9l\u00e9ment ``l[mi]``. Il faut donc \u00e9crire :"]}, {"cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [{"data": {"text/plain": ["['un', 'trois', 'quatre', 'deux', 'cinq']"]}, "execution_count": 7, "metadata": {}, "output_type": "execute_result"}], "source": ["l = [\"un\", \"deux\", \"trois\", \"quatre\", \"cinq\"]\n", "for i in range (0,len (l)) :\n", " mi = i\n", " for j in range (i, len (l)) :\n", " if l[mi] < l [j] : mi = j\n", " e = l [mi] ######## ligne modifi\u00e9e\n", " l [mi] = l [i]\n", " l [i] = e\n", " \n", "l"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Co\u00fbt d'un algorithme\n", "\n", "Le co\u00fbt d'un algorithme ou d'un programme est le nombre d'op\u00e9rations (additions, multiplications, tests, ...) qu'il effectue. Il s'exprime comme un multiple d'une fonction de la dimension des donn\u00e9es que le programme manipule. Par exemple : $O(n)$, $O(n^2)$, $O(n\\ln n)$, ..."]}, {"cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["1.5\n", "1.25\n"]}], "source": ["def moyenne (tab) :\n", " s = 0.0\n", " for x in tab : \n", " s += x\n", " return s / len (tab)\n", " \n", "def variance (tab) :\n", " s = 0.0\n", " for x in tab :\n", " t = x - moyenne (tab)\n", " s += t * t\n", " return s / len (tab)\n", " \n", "l = [ 0,1,2, 2,3,1,3,0]\n", "print(moyenne (l))\n", "print(variance (l))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Tout d'abord, le co\u00fbt d'un algorithme est tr\u00e8s souvent exprim\u00e9 comme un multiple de la dimension des donn\u00e9es qu'il traite. Ici, la dimension est la taille du tableau ``tab``. Par exemple, si on note ``n = len(tab)``, alors le co\u00fbt de la fonction ``moyenne`` s'\u00e9crit $O(n)$ car cette fonction fait la somme des $n$ \u00e9l\u00e9ments du tableau.\n", "\n", "La fonction ``variance`` contient quant \u00e0 elle un petit pi\u00e8ge. Si elle contient elle aussi une boucle, chacun des $n$ passages dans cette boucle fait appel \u00e0 la fonction ``moyenne``. Le co\u00fbt de la fonction ``variance`` est donc \n", "$O(n^2)$.\n", "\n", "Il est possible d'acc\u00e9l\u00e9rer le programme car la fonction ``moyenne`` retourne le m\u00eame r\u00e9sultat \u00e0 chaque passage dans la boucle. Il suffit de m\u00e9moriser son r\u00e9sultat dans une variable avant d'entrer dans la boucle comme suit :"]}, {"cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [{"data": {"text/plain": ["1.25"]}, "execution_count": 9, "metadata": {}, "output_type": "execute_result"}], "source": ["def variance (tab) :\n", " s = 0.0\n", " m = moyenne (tab)\n", " for x in tab :\n", " t = x - m\n", " s += t * t\n", " return s / len (tab)\n", "\n", "variance(l)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le co\u00fbt de la fonction ``variance`` est alors $O(n)$."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le co\u00fbt d'un algorithme peut \u00eatre \u00e9valu\u00e9 de mani\u00e8re plus pr\u00e9cise et n\u00e9cessiter un r\u00e9sultat comme $n^2 + 3n + 2$ mais cette exigence est rarement utile pour des langages comme *python*. L'expression ``for x in tab:`` cache n\u00e9cessairement un test qu'il faudrait prendre en compte si plus de pr\u00e9cision \u00e9tait exig\u00e9e. Il faudrait \u00e9galement se tourner vers un autre langage de programmation, plus pr\u00e9cis dans sa syntaxe. Par exemple, lorsqu'on con\u00e7oit un programme avec le langage C ou C++, \u00e0 partir du m\u00eame code informatique, on peut construire deux programmes ex\u00e9cutables. Le premier (ou version *debug*), lent, sert \u00e0 la mise au point : il inclut des tests suppl\u00e9mentaires permettant de v\u00e9rifier \u00e0 chaque \u00e9tape qu'il n'y a pas eu d'erreur (une division par z\u00e9ro par exemple). Lorsqu'on est s\u00fbr que le programme marche, on construit la seconde version (ou *release*), plus rapide, dont ont \u00e9t\u00e9 \u00f4t\u00e9s tous ces tests de conception devenus inutiles. \n", "\n", "*python* aboutit \u00e0 un programme lent qui inclut une quantit\u00e9 de tests invisibles pour celui qui programme mais qui d\u00e9tecte les erreurs plus vite et favorise une conception rapide. Il n'est pas adapt\u00e9 au traitement d'information en grand nombre et fait une multitude d'op\u00e9rations cach\u00e9es."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## H\u00e9ritage double\n", "\n", "On a besoin dans un programme de cr\u00e9er une classe ``carre`` et une classe ``rectangle``. Mais on ne sait pas quelle classe doit h\u00e9riter de l'autre. Dans le premier programme, ``rectangle`` h\u00e9rite de ``carre``."]}, {"cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [{"data": {"text/plain": ["12"]}, "execution_count": 10, "metadata": {}, "output_type": "execute_result"}], "source": ["class carre:\n", " def __init__ (self, a):\n", " self.a = a\n", " def surface (self):\n", " return self.a ** 2\n", "\n", "class rectangle(carre):\n", " def __init__ (self, a,b) :\n", " carre.__init__(self,a)\n", " self.b = b\n", " def surface (self):\n", " return self.a * self.b\n", "\n", "rectangle(3, 4).surface()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Dans le second programme, c'est la classe ``carre`` qui h\u00e9rite de la classe ``rectangle``."]}, {"cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [{"data": {"text/plain": ["9"]}, "execution_count": 11, "metadata": {}, "output_type": "execute_result"}], "source": ["class rectangle :\n", " def __init__ (self, a,b) :\n", " self.a = a\n", " self.b = b\n", " def surface (self) :\n", " return self.a * self.b\n", "\n", "class carre (rectangle) :\n", " def __init__ (self, a) :\n", " rectangle.__init__ (self, a,a)\n", " def surface (self) :\n", " return self.a ** 2\n", " \n", "carre(3).surface()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["* Dans le second programme, est-il n\u00e9cessaire de red\u00e9finir la m\u00e9thode ``surface`` dans la classe ``carre`` ?\n", "* Quel est le sens d'h\u00e9ritage qui vous para\u00eet le plus cens\u00e9, ``class rectangle(carre)`` ou ``class carre(rectangle)`` ?\n", "* On d\u00e9sire ajouter la classe ``losange``. Est-il plus simple que ``rectangle`` h\u00e9rite de la classe ``carre`` ou l'inverse pour introduire la classe ``losange`` ? Quel ou quels attributs suppl\u00e9mentaires faut-il introduire dans la classe ``losange`` ?"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le principe de l'h\u00e9ritage est qu'une classe ``carre`` h\u00e9ritant de la classe ``rectangle`` h\u00e9rite de ses attributs et m\u00e9thodes. L'aire d'un carr\u00e9 est \u00e9gale \u00e0 celle d'un rectangle dont les c\u00f4t\u00e9s sont \u00e9gaux, par cons\u00e9quent, la m\u00e9thode ``surface`` de la classe retourne la m\u00eame valeur que celle de la classe ``rectangle``. Il n'est donc pas n\u00e9cessaire de la red\u00e9finir.\n", "* D'apr\u00e8s la r\u00e9ponse de la premi\u00e8re question, il para\u00eet plus logique de consid\u00e9rer que ``carre`` h\u00e9rite de ``rectangle``.\n", "* Un losange est d\u00e9fini par un c\u00f4t\u00e9 et un angle ou un c\u00f4t\u00e9 et la longueur d'une de ses diagonales, soit dans les deux cas, deux param\u00e8tres. Dans la premi\u00e8re question, il paraissait plus logique que la classe la plus sp\u00e9cifique h\u00e9rite de la classe la plus g\u00e9n\u00e9rale afin de b\u00e9n\u00e9ficier de ses m\u00e9thodes. Pour introduire le losange, il para\u00eet plus logique de partir du plus sp\u00e9cifique pour aller au plus g\u00e9n\u00e9ral afin que chaque classe ne contienne que les informations qui lui sont n\u00e9cessaires."]}, {"cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [{"data": {"text/plain": ["8.183676841431136"]}, "execution_count": 12, "metadata": {}, "output_type": "execute_result"}], "source": ["import math\n", "\n", "class carre :\n", " def __init__ (self, a) :\n", " self.a = a\n", " def surface (self) :\n", " return self.a ** 2\n", "\n", "class rectangle (carre) :\n", " def __init__ (self, a,b) :\n", " carre.__init__(self,a)\n", " self.b = b\n", " def surface (self) :\n", " return self.a * self.b\n", "\n", "class losange (carre) :\n", " def __init__ (self, a,theta) :\n", " carre.__init__(self,a)\n", " self.theta = theta\n", " def surface (self) :\n", " return self.a * math.cos (self.theta) * self.a * math.sin (self.theta) * 2\n", " \n", "losange(3, 1).surface()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le sens de l'h\u00e9ritage d\u00e9pend de vos besoins. Si l'h\u00e9ritage porte principalement sur les m\u00e9thodes, il est pr\u00e9f\u00e9rable de partir du plus g\u00e9n\u00e9ral pour aller au plus sp\u00e9cifique. La premi\u00e8re classe sert d'interface pour toutes ses filles. Si l'h\u00e9ritage porte principalement sur les attributs, il est pr\u00e9f\u00e9rable de partir du plus sp\u00e9cifique au plus g\u00e9n\u00e9ral. Dans le cas g\u00e9n\u00e9ral, il n'y a pas d'h\u00e9ritage plus sens\u00e9 qu'un autre mais pour un probl\u00e8me donn\u00e9, il y a souvent un h\u00e9ritage plus sens\u00e9 qu'un autre. "]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Pr\u00e9cision des calculs\n", "\n", "Voici un aper\u00e7u de la pr\u00e9cision des calculs pour le calcul $1 - 10^{-n}$. L'exercice a pour but de montrer que l'ordinateur ne fait que des calculs approch\u00e9s et que la pr\u00e9cision du r\u00e9sultat d\u00e9pend de la m\u00e9thode num\u00e9rique employ\u00e9e."]}, {"cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["0 \t 0.9 \t 0.1 \t 0.31622776601683794\n", "1 \t 0.99 \t 0.01 \t 0.1\n", "2 \t 0.999 \t 0.001 \t 0.03162277660168379\n", "3 \t 0.9999 \t 0.0001 \t 0.01\n", "4 \t 0.99999 \t 1e-05 \t 0.0031622776601683794\n", "5 \t 0.999999 \t 1.0000000000000002e-06 \t 0.001\n", "6 \t 0.9999999 \t 1.0000000000000002e-07 \t 0.000316227766016838\n", "7 \t 0.99999999 \t 1.0000000000000002e-08 \t 0.0001\n", "8 \t 0.999999999 \t 1.0000000000000003e-09 \t 3.1622776601683795e-05\n", "9 \t 0.9999999999 \t 1.0000000000000003e-10 \t 1e-05\n", "10 \t 0.99999999999 \t 1.0000000000000003e-11 \t 3.1622776601683796e-06\n", "11 \t 0.999999999999 \t 1.0000000000000002e-12 \t 1.0000000000000002e-06\n", "12 \t 0.9999999999999 \t 1.0000000000000002e-13 \t 3.1622776601683797e-07\n", "13 \t 0.99999999999999 \t 1.0000000000000002e-14 \t 1.0000000000000001e-07\n", "14 \t 0.999999999999999 \t 1e-15 \t 3.162277660168379e-08\n", "15 \t 0.9999999999999999 \t 1.0000000000000001e-16 \t 1e-08\n", "16 \t 1.0 \t 1e-17 \t 3.1622776601683795e-09\n", "17 \t 1.0 \t 1e-18 \t 1e-09\n", "18 \t 1.0 \t 1.0000000000000001e-19 \t 3.1622776601683795e-10\n"]}], "source": ["x = 1.0\n", "for i in range (0,19) :\n", " x = x / 10\n", " print(i, \"\\t\", 1.0 - x, \"\\t\", x, \"\\t\", x **(0.5))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le programme montre que l'ordinateur affiche ``1`` lorsqu'il calcule $1-10^{-17}$. Cela signifie que la pr\u00e9cision des calculs en *python* est au mieux de $10^{-16}$. C'est encore moins bon dans le cas de *float* ou r\u00e9el simple pr\u00e9cision cod\u00e9 sur 4 octets au lieu de 8 pour les *double*."]}, {"cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["0 \t 0.8999999985098839 \t 0.1 \t 0.3162277683729184\n", "1 \t 0.9900000002235174 \t 0.01 \t 0.0999999988824129\n", "2 \t 0.9990000000689179 \t 0.0009999999 \t 0.03162277551199656\n", "3 \t 0.9999000000098022 \t 9.999999e-05 \t 0.009999999509891484\n", "4 \t 0.9999900000011621 \t 9.999999e-06 \t 0.0031622774764217087\n", "5 \t 0.9999990000001162 \t 9.999999e-07 \t 0.0009999999418942008\n", "6 \t 0.999999900000013 \t 9.999999e-08 \t 0.0003162277453952373\n", "7 \t 0.999999990000001 \t 9.999999e-09 \t 9.999999525523424e-05\n", "8 \t 0.9999999990000001 \t 9.999999e-10 \t 3.162277439909038e-05\n", "9 \t 0.9999999999 \t 9.999999e-11 \t 9.99999937286775e-06\n", "10 \t 0.99999999999 \t 9.999999e-12 \t 3.162277516708525e-06\n", "11 \t 0.999999999999 \t 9.999999e-13 \t 9.999999437919884e-07\n", "12 \t 0.9999999999999 \t 9.999999e-14 \t 3.162277525279896e-07\n", "13 \t 0.99999999999999 \t 9.999999e-15 \t 9.999999488741863e-08\n", "14 \t 0.999999999999999 \t 9.999999e-16 \t 3.162277498494361e-08\n", "15 \t 0.9999999999999999 \t 9.999999e-17 \t 9.999999422567411e-09\n", "16 \t 1.0 \t 9.999999e-18 \t 3.162277503725911e-09\n", "17 \t 1.0 \t 9.999999e-19 \t 9.999999712080637e-10\n", "18 \t 1.0 \t 1e-19 \t 3.1622776099917643e-10\n"]}], "source": ["import numpy\n", "x = numpy.float32(1.0)\n", "for i in range (0,19) :\n", " x = x / numpy.float32(10)\n", " print(i, \"\\t\", 1.0 - x, \"\\t\", x, \"\\t\", x **(0.5))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["On \u00e9crit une classe ``matrice_carree_2`` qui repr\u00e9sente une matrice carr\u00e9e de dimension 2."]}, {"cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["0.999999999999\n", "1.0\n"]}], "source": ["class matrice_carree_2 :\n", " def __init__ (self, a,b,c,d) :\n", " self.a, self.b, self.c, self.d = a,b,c,d\n", " \n", " def determinant (self) :\n", " return self.a * self.d - self.b * self.c\n", " \n", "m1 = matrice_carree_2 (1.0,1e-6,1e-6,1.0)\n", "m2 = matrice_carree_2 (1.0,1e-9,1e-9,1.0)\n", "print(m1.determinant())\n", "print(m2.determinant())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La seconde valeur est donc fausse. On consid\u00e8re maintenant la matrice $M = \\left(\\begin{array}{cc} 1 & 10^{-9} \\\\ 10^{-9} & 1 \\end{array} \\right)$.\n", "\n", "On pose $D = \\det(M) = 1 - 10^{-18}$ et $T = tr(M) = 2$. $\\Delta$ est le d\u00e9terminant de $M$ et $T$ sa trace. On sait que les valeurs propres de $M$ not\u00e9es $\\lambda_1, \\lambda_2$ v\u00e9rifient :\n", "\n", "$$\n", "\\begin{array}{lll}\n", "D &=& \\lambda_1 \\lambda_2 \\\\\n", "T &=& \\lambda_1 + \\lambda_2\n", "\\end{array}\n", "$$\n", "\n", "On v\u00e9rifie que $(x - \\lambda_1)(x - \\lambda_2) = x^2 - x (\\lambda_1 + \\lambda_2) + \\lambda_1 \\lambda_2$. Les valeurs propres de $M$ sont donc solutions de l'\u00e9quation : $x^2 - T x + D = 0$. \n", "\n", "Le discriminant de ce polyn\u00f4me est $\\Delta = T^2 - 4 D$. On peut donc exprimer les valeurs propres de la matrice $M$ par : \n", "\n", "$$\n", "\\begin{array}{lll}\n", "\\lambda_1 &=& \\frac{T - \\sqrt{\\Delta}}{2} \\\\\n", "\\lambda_2 &=& \\frac{T + \\sqrt{\\Delta}}{2} \n", "\\end{array}\n", "$$\n", "\n", "On ajoute donc la m\u00e9thode suivante \u00e0 la classe ``matrice_carree_2`` :"]}, {"cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["(0.9999990000110609, 1.000000999988939)\n", "(1.0, 1.0)\n"]}], "source": ["class matrice_carree_2 :\n", " def __init__ (self, a,b,c,d) :\n", " self.a, self.b, self.c, self.d = a,b,c,d\n", " \n", " def determinant (self) :\n", " return self.a * self.d - self.b * self.c\n", " \n", " def valeurs_propres (self) :\n", " det = self.determinant ()\n", " trace = self.a + self.d\n", " delta = trace ** 2 - 4 * det\n", " l1 = 0.5 * (trace - (delta ** (0.5)) )\n", " l2 = 0.5 * (trace + (delta ** (0.5)) )\n", " return l1,l2\n", " \n", "m1 = matrice_carree_2 (1.0,1e-6,1e-6,1.0)\n", "m2 = matrice_carree_2 (1.0,1e-9,1e-9,1.0)\n", "print(m1.valeurs_propres())\n", "print(m2.valeurs_propres())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["D'apr\u00e8s l'\u00e9nonc\u00e9, les valeurs propres de la matrice $M_2$ sont les sommes de celles de la matrice $I$ et de la matrice $M'_2$. Par cons\u00e9quent, ce second calcul m\u00e8ne au r\u00e9sultat suivant :\n", "\n", "```\n", "l1 = 1-1e-9 = 0.99999999900000002828\n", "l2 = 1+ 1e-9 = 1.000000001\n", "```"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La pr\u00e9cision des calculs prend sont importance ici. On d\u00e9compose la matrice $M = \\left(\\begin{array}{cc} 1 & 0 \\\\ 0 & 1 \\end{array}\\right) + \\left(\\begin{array}{cc} 0 & 10^{-9} \\\\ 10^{-9} & 0 \\end{array}\\right) = I + M'$. "]}, {"cell_type": "markdown", "metadata": {}, "source": ["On peut d\u00e9montrer que si $\\lambda$ est une valeur propre de $M'$, alors $1 + \\lambda$ est une valeur propre de $M$. Que donne le calcul des valeurs propres de $M'$ si on utilise la m\u00e9thode ``valeurs_propres`` pour ces deux matrices ?\n", "\n", "On consid\u00e8re maintenant la matrice $M'' = \\left(\\begin{array}{cc} 1 & 10^{-9} \\\\ -10^{-9} & 1 \\end{array}\\right)$. En d\u00e9composant la matrice $M''$ de la m\u00eame mani\u00e8re qu'\u00e0 la question 4, quelles sont les valeurs propres retourn\u00e9es par le programme pour la matrice $M''$ ? Quelles sont ses vraies valeurs propres ?"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La matrice $M''$ n'est en fait pas diagonalisable, c'est-\u00e0-dire que $tr(M'')^2 - 4 * \\det{M''} = 4 - 4 (1 + 10^{-18}) < 0$. Or le calcul propos\u00e9 par la question 3 aboutit au m\u00eame r\u00e9sultat faux que pour la matrice $M_2$, les deux valeurs propres trouv\u00e9es seront \u00e9gales \u00e0 1. Si on applique la d\u00e9composition propos\u00e9e :\n", "$M'' = I + \\left(\\begin{array}{cc}0&-10^{-9}\\\\10^{-9}&0\\end{array}\\right) = I + N''$\n", "Le programme calcule sans erreur le discriminant n\u00e9gatif de la matrice $N''$ qui n'est pas diagonalisable. Il est donc impossible d'obtenir des valeurs propres r\u00e9elles pour la matrice $M''$ avec cette seconde m\u00e9thode. Cette question montre qu'une erreur d'approximation peut rendre une matrice diagonalisable alors qu'elle ne l'est pas. Il faut bien choisir cette pr\u00e9cision en fonction de la destination des calculs."]}, {"cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": []}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.0"}}, "nbformat": 4, "nbformat_minor": 2}