\chapter{Python en classe de Première}

\newpage
\pagestyle{main}

\section*{Extrait du programme de Première}

La démarche algorithmique est, depuis les origines, une composante essentielle de l'activité
mathématique. Au collège, en mathématiques et en technologie, les élèves ont appris à
écrire, mettre au point et exécuter un programme simple. La classe de seconde a permis de
consolider les acquis du cycle 4 autour de deux idées essentielles :
\begin{itemize}
\item la notion de fonction;
\item la programmation comme production d'un texte dans un langage informatique.
\end{itemize}

L'enseignement de spécialité de mathématiques de classe de première vise la consolidation des notions de variable, d'instruction conditionnelle et de boucle ainsi que l'utilisation des fonctions. La seule notion nouvelle est celle de liste qui trouve naturellement sa place dans de nombreuses parties du programme et aide à la compréhension de notions
mathématiques telles que les suites numériques, les tableaux de valeurs, les séries statistiques...

Comme en classe de seconde, les algorithmes peuvent être écrits en langage naturel ou utiliser le langage Python.

Les notions relatives aux types de variables et à l'affectation sont consolidées. Comme en classe de seconde, on utilise le symbole « $\leftarrow$ » pour désigner l'affection dans un algorithme écrit en langage naturel.

L'accent est mis sur la programmation modulaire qui permet de découper une tâche complexe en tâches plus simples.

\bigskip

\begin{attention}
Certains aspects qui interviennent en classe de Seconde ne seront pas systématiquement repris dans ce chapitre.

Il est donc conseillé de regarder les chapitres précédents avant d'aborder celui-ci.
\end{attention}

\newpage

\section{Suites numériques}

\subsection{Calcul et affichage des premiers termes}
\index{Suites numeriques@Suites numériques!Premiers termes}

Une suite numérique peut être définie de façon explicite (à l'aide d'une fonction dont la variable est l'indice du terme à calculer) ou par une relation de récurrence (qui lie le terme à calculer à son précédent).

\begin{itemize}
\item Si la suite est définie de façon explicite, alors le programme Python qui permet de calculer ses premiers termes est semblable à celui qui permet de calculer des images par une fonction.

\begin{exemple}
Programme calculant et affichant les 20 premiers termes de la suite \suite{u}{n}{} définie pour tout entier naturel $n$ par:
\[
u_n=n^2 + \frac{1}{n+1}.
\]
\begin{python}
for n in range(21):
    print( n*n + 1/(n+1) )
\end{python}
\end{exemple}

\item Si la suite est définie par récurrence, on utilise cette relation de récurrence pour calculer les termes successifs.
\begin{exemple}
Programme calculant et affichant les 20 premiers termes de la suite \suite{u}{n}{} définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0 = 5\\
u_{n+1}= u_n + 3
\end{cases}
\]
\begin{python}
u = 5 # premier terme
print(u) # on affiche le premier terme
for n in range(19): # on calcule et affiche les 19 autres termes
    u = u + 3 # relation de récurrence
    print(u)
\end{python}
\end{exemple}
\end{itemize}

\subsection{Algorithme de seuil}
\index{Suites numeriques@Suites numériques!Algorithmes de seuil}

Un \textit{algorithme de seuil} est un algorithme (ou un programme) qui permet de trouver le premier indice à partir duquel une condition sur la suite est remplie (par exemple, le premier indice $n$ à partir duquel $u_n>100$ ou $u_n\leqslant0,1$).

Dans la mesure où on cherche un indice et qu'une condition est imposée, on devra utiliser une boucle conditionnelle \og \texttt{while} \fg{} (car on ne sait pas le nombre d'itérations à exécuter pour y arriver).

\newpage

En général, l'algorithme se présente ainsi :

\begin{center}
\fbox{%
\begin{minipage}{0.6\linewidth}
\ttfamily
u $\leftarrow$ premier terme de la suite\\
n $\leftarrow$ 0\\
Tant que < contraire de la condition imposée >:\\
\hspace*{5mm}n $\leftarrow$ n + 1\\
\hspace*{5mm}u $\leftarrow$ relation de récurrence de u\\
Afficher n
\end{minipage}%
}
\end{center}
\enlargethispage*{3em}
\begin{exemple}
Soit \suite{u}{n}{} la suite définie pour tout entier naturel $n$ par: $\begin{cases}
u_0 = 50\\
u_{n+1} = 0,95u_n
\end{cases}$.

On cherche le premier indice à partir duquel $u_n\leqslant10^{-5}$.
\begin{python}
u = 50
n = 0
while u > 10**(-5):
    n = n + 1
    u = 0.95 * u
    
print(n)
\end{python}
\begin{console}
301
\end{console}
Donc $u_{300}>10^{-5}$ mais $u_{301}\leqslant10^{-5}$.
\end{exemple}

\begin{exemple}
Soit \suite{u}{n}{} la suite définie pour tout entier naturel $n$ par: $\begin{cases}
u_0 = 10\\
u_{n+1} = 1,2u_n
\end{cases}$

On cherche le premier indice à partir duquel $u_n>10^{9}$.
\begin{python}
u = 10
n = 0
while u <= 10**9:
    n = n + 1
    u = 1.2 * u
    
print(n)
\end{python}
\begin{console}
102
\end{console}
Donc $u_{101}\leqslant10^9$ mais $u_{102}>10^{9}$.
\end{exemple}

\subsection{Méthode de Newton}
\index{Newton (methode)@Newton (méthode)}
\index{Methode de Newton@Méthode de Newton}

Cette méthode est explicitée dans le livre concernant l'enseignement de spécialité en Mathématiques. Elle définit une suite \suite{u}{n}{} de premier terme $u_0$ et dont la relation de récurrence est:
\[
u_{n+1} = u_n - \dfrac{f(u_n)}{f^\prime(u_n)}.
\]
Il existe une façon élégante d'implémenter cette méthode à l'aide du module \textit{sympy}:

\medskip

\begin{python}
import sympy as sp

x = sp.symbols('x')

def newton(f,x0,eps=0.0001):
    f_prime = sp.diff(f, x)
    xn= x0+1
    while abs(xn-x0) > eps:
        x0, xn = xn, xn - f.subs(x,xn)/f_prime.subs(x,xn)
        
    return xn

# Définir la fonction
f = 0.25*x**2 + 0.4*x - 3

# On affiche la solution
print(newton(f,0))
\end{python}

\medskip

Ici, on a choisi de prendre $u_0=0$. La suite converge rapidement vers la solution de l'équation $f(x)=0$, où $f(x)=0,25x^2+0,4x-3$.

L'avantage de cette méthode d'implémentation réside dans le fait qu'il n'est pas nécessaire de déterminer l'expression de la dérivée. 


\section{Fonction exponentielle: méthode\\d'Euler}
\index{Euler (methode)@Euler (méthode)}
\index{Methode d'Euler@Méthode d'Euler}

\itemclass{titrecolor}

On recherche une fonction $f$ telle que, 

\begin{enumerate}
\item\label{methode-euler-1} pour tout réel $x$, $f'(x)=f(x)$ ;
\item\label{methode-euler-2} $f(0)=1$.
\end{enumerate}

On suppose que cette fonction existe.

\bigskip

Très vite, on se rend compte que cette fonction n'est pas un polynôme car le seul polynôme $P$ qui vérifie le point \ref{methode-euler-1} est $P(x)=0$ et dans ce cas, $P(0)\neq1$, ce qui ne satisfait pas le point \ref{methode-euler-2}.

Ce n'est pas non plus une fonction racine carrée ou une fraction rationnelle (quotient de deux polynômes).

\bigskip

Nous allons avant tout tenter de dessiner la courbe représentative de notre fonction. Pour cela, on va prendre des intervalles \intervFF{a}{a+h}, avec $h$ très proche de 0, et on va assimiler la courbe représentative de $f$ à sa tangente au point d'abscisse $a$. En effet, sur un intervalle assez petit \intervFF{a}{a+h}, la tangente est très proche de la courbe.

\medskip

On sait que l'équation de la tangente à la courbe au point d'abscisse $a$ est :
\[
y = f'(a)(x-a)+f(a).
\]
Posons $g(x)=f'(a)(x-a)+f(a)$. Confondre la courbe et la tangente revient à dire que\linebreak $f(a+h) \approx g(a+h)$, c'est-à-dire :
\vspace*{-0.5em}
\[
f(a+h)\approx f'(a)(a+h-a)+f(a),
\]
\vspace*{-0.5em}
soit :
\vspace*{-0.5em}
\[
f(a+h)\approx f'(a)\times h + f(a).
\]
Or, d'après le point \ref{methode-euler-1}, $f'(a)=f(a)$ donc :
\vspace*{-0.5em}
\[
f(a+h)\approx hf(a)+f(a)
\]
soit :
\vspace*{-0.5em}
\[
f(a+h)\approx (1+h)f(a).\qquad\text{(en factorisant par }f(a))
\]
$h$ peut être pris aussi petit qu'on le souhaite. Prenons par exemple $h=0,1$. Alors, 
\begin{itemize}
\item en prenant $a=0$ : $f(0+0,1)\approx(1+0,1)\times f(0)=1,1\times1=1,1$;
\item en prenant $a=0,1$ : $f(0,2)=f(0,1+0,1)\approx(1+0,1)\times f(0,1)=1,1\times1,1=1,21$;
\item en prenant $a=0,2$ : $f(0,3)=f(0,2+0,1)\approx(1+0,1)\times f(0,2)=1,1\times1,21=1,331$; etc.
\end{itemize}

Le programme Python suivant permet d'automatiser la construction des segments :

\begin{python}
from matplotlib.pyplot import plot, show
from numpy import array

xList = [0]
yList = [1]

a = 0
h = 0.1

for i in range(10):
    x = a + h
    y = (1+h) * yList[i]
    xList.extend([x])
    yList.extend([y])
    a = x

x = array(xList)
y = array(yList)
plot(x, y, marker=".")

show()
\end{python}

\begin{center}
\begin{tikzpicture}
\node[inner sep = 5mm, fill = white] (img) {\includegraphics{04-Euler01.png}};
\drawshadow{img}{black}
\end{tikzpicture}
\end{center}
Avec un pas plus petit ($h=0,01)$, on obtient la figure suivante:

\begin{center}
\begin{tikzpicture}
\node[inner sep = 5mm, fill = white] (img) {\includegraphics{04-Euler02.png}};
\drawshadow{img}{black}
\end{tikzpicture}
\end{center}

On voit alors se dessiner la courbe représentative de la fonction $f$ cherchée.

\section{Fonctions trigonométriques}
\index{Sinus et cosinus (courbes)}

Il n'y a pas grand-chose à écrire sur ce thème, si ce n'est peut-être un programme permettant de tracer les représentations des fonctions sinus et cosinus.

\begin{python}
from numpy import linspace, cos, sin, pi
from matplotlib.pyplot import plot, show, legend, axvline, axhline

x = linspace(-4*pi, 4*pi, 200)
y1 = cos(x)
y2 = sin(x)
plot(x, y1, label='cos(x)')
plot(x, y2, label='sin(x)')
legend()
axhline(y=0, color='black', linestyle='-')
axvline(x=0, color='black', linestyle='-')
show()
\end{python}

\begin{center}
\begin{tikzpicture}
\node[inner sep = 5mm, fill = white] (img) {\includegraphics[width=0.9\linewidth]{cos-sin.jpg}};
\drawshadow{img}{black}
\end{tikzpicture}
\end{center}

\newpage

\section{Vecteurs}
\index{Vecteur (representation en Python)@Vecteur (représentation en Python)}

À l'instar des points, les vecteurs peuvent être représentés par un couple de coordonnées.

\begin{exemple}
Le vecteur $\vv{u}\dbinom{-1}{3}$ peut être représenté ainsi :
\begin{console}
>>> u = (-1,3)
>>> u[0]
-1
>>> u[1]
3
\end{console}
C'est une sorte de tableau à une ligne et deux colonnes dans lequel sont mises les coordonnées du vecteur. 

Le premier nombre est indexé par 0 (\texttt{u[0]}) et le second par 1 (\texttt{u[1]}). Mais nous avons vu cela dans le chapitre précédent.
\end{exemple}

\subsection{Déterminant de deux vecteurs}

Le module \texttt{numpy} permet de calculer le déterminant de deux vecteurs:

\begin{python}
from numpy import array
from numpy.linalg import det

def determinant(u,v):
    # u = (a, b) et v = (c, d)
    vecteur1 = array([u[0], u[1]])
    vecteur2 = array([v[0], v[1]])

    matrice = array([vecteur1, vecteur2])

    return det(matrice)
\end{python}

\begin{console}
>>> u = (-3, 2)
>>> v = (5, -1)
determinant(u,v)
-6.999999999999999
\end{console}

\newpage

\subsection{Produit scalaire}

À l'instar du déterminant, le produit scalaire peut aussi être calculé à l'aide du module \texttt{numpy}:

\begin{python}
from numpy import array

def produit_scalaire(u,v):
    # u = (a, b) et v = (c, d)
    vecteur1 = array([u[0], u[1]])
    vecteur2 = array([v[0], v[1]])

    return vecteur1 @ vecteur2
\end{python}

\begin{console}
>>> u = (-3, 2)
>>> v = (5, -1)
produit_scalaire(u,v)
-17
\end{console}

\begin{rem}
On peut aussi utiliser la fonction \texttt{np.dot(vecteur1, vecteur2)} :

\begin{python}
from numpy import array, dot

def produit_scalaire(u,v):
    # u = (a, b) et v = (c, d)
    vecteur1 = array([u[0], u[1]])
    vecteur2 = array([v[0], v[1]])

    return dot(vecteur1, vecteur2)
\end{python}
\end{rem}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\exercices

\section*{Le second degré}

\begin{exocor}{}[1]
\begin{enumerate}
\item \'Ecrire une fonction Python \texttt{delta(a,b,c)} qui renvoie le discriminant du polynôme $ax^2+bx+c$.

\item \'Ecrire une fonction Python \texttt{racines(a,b,c)} qui renvoie les éventuelles racines du polynôme $ax^2+bx+c$.

\textit{On pourra s'aider de la fonction \texttt{delta(a,b,c)}}.
\end{enumerate}

\tcblower

\begin{enumerate}
\item Le discriminant d'un polynôme $ax^2+bx+c$ est $\Delta=b^2-4ac$, d'où la fonction Python suivante:
\begin{python*}
def delta(a,b,c):
    return b*b - 4*a*c
\end{python*}

\item Nous savons que le nombre de racines d'un polynôme de degré 2 dépend de la valeur de son discriminant $\Delta$.
\begin{itemize}
\item Si $\Delta<0$ alors il n'y a pas de racine.
\item Si $\Delta=0$ alors il n'y a qu'une racine, égale à $-\dfrac{b}{2a}$.
\item Si $\Delta>0$ alors il y a deux racines, qui sont $\dfrac{-b-\sqrt{\Delta}}{2a}$ et $\dfrac{-b+\sqrt{\Delta}}{2a}$.
\end{itemize}
On peut donc proposer la fonction Python suivante:
\begin{python}
def racines(a,b,c):
    D = delta(a,b,c)
    if D < 0: return 'Pas de racine.'
    elif D == 0: return -b/(2*a)
    else: return (-b-D**0.5)/(2*a) , (-b+D**0.5)/(2*a)
\end{python}
Je rappelle que \texttt{b**0.5} permet de calculer $\sqrt{b}$.
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{}[2]
\'Ecrire une fonction Python qui retourne les deux nombres $a$ et $b$ tels que $a+b=S$ et $ab = P$, où $S$ et $P$ sont connus.

\tcblower

D'après le cours, on sait que si $a+b=S$ et $ab=P$ alors $a$ et $b$ sont racines de $x^2-Sx+P$, dont le discriminant est $\Delta=S^2-4P$.

On peut alors proposer la fonction suivante:
\begin{python}
def f(S,P):
    delta = S*S - 4*P
    if delta > 0:
        return (S - delta**0.5)/2 , (S + delta**0.5)/2
    elif delta == 0:
        return S/2
    else:
        return 'Pas de solution.'
\end{python} 
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section*{Dérivation}

\begin{exocor}{}[1]
On considère le programme Python suivant:
\begin{python}
def f(x):
    return -0.3*x*x + 2*x + 1

def coef(a):
    return ( f(a+10**(-10)) - f(a) ) / 10**(-10)
\end{python}

On a alors en mode console:
\begin{console}
>>> coef(1)
1.4000001158365194
\end{console}

\begin{enumerate}
\item Expliquer ce que représente le nombre renvoyé par la fonction \texttt{coef(a)}.
\item En théorie, que devrait renvoyer \texttt{coef(1)} ?

Comparer avec le résultat affiché.
\end{enumerate}

\tcblower

\begin{enumerate}
\item Dans ce programme, il y a deux fonctions Python:
\begin{itemize}
\item la première renvoie l'image d'un nombre $x$ par la fonction $f(x)=-0,3x^2+2x+1$;
\item dans la seconde, on renvoie le nombre $\dfrac{f(a+h)-f(a)}{h}$, avec $h=10^{-10}$, qui est le taux d'accroissement de la fonction $f$ entre $a$ et $a+h$, correspondant à une valeur approchée de $f'(a)$ car $h$ est très proche de 0.
\end{itemize}

La fonction \texttt{coef(a)} renvoie donc une valeur approchée de $f'(a)$.

\item $f'(x)=-0,6x+2$ donc $f'(1) = -0,6+2=1,4$. Cette valeur correspond bien (à peu près) à celle affichée en mode console.
\end{enumerate}
\end{exocor}



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section*{Suites}

\begin{exocor}{vérifier la nature d'une suite}[1]
\begin{enumerate}
\item On considère la liste \texttt{u} définie par:
\begin{console}
u = [ 5 , 8 , 11 , 14 , 17 , 20 , 23 , 26 , 29 , 31 , 34 , 37 , 40 , 43 ]
\end{console}

\medskip

\'Ecrire un programme qui vérifie si cette liste contient les premiers termes d'une suite arithmétique.

\item On considère une liste \texttt{v} définie par:
\begin{console}
v = [ 100 , 50 , 25 , 12.5 , 6.25 , 3.125 , 1.5625 , 0.78125 , 0.375625 , 0.1878125 , 0.09390625 , 0.046953125 ]
\end{console}

\'Ecrire un programme qui vérifie si cette liste contient les premiers termes d'une suite géométrique.
\end{enumerate}


\tcblower

\begin{enumerate}
\item Voici une proposition de programme:
\begin{python}
u = [5,8,11,14,17,20,23,26,29,31,34,37,40,43]
r = u[1] - u[0]
a = True
for n in range(2,len(u)):
    if u[n] - u[n-1] != r:
        a = False
        
print(a)
\end{python}

Explications:

\begin{itemize}
\item on commence (ligne 2) par définir une variable \texttt{r} qui représente la valeur de l'éventuelle raison. Par défaut, elle vaut $u_1-u_0$;

\item ensuite, on définit (ligne 3) une variable booléenne qui vaut par défaut \og True \fg{} et qui signifie que la suite est arithmétique;

\item on crée une boucle itérative (ligne 4) pour parcourir la liste \texttt{u} à partir de $u_2$;

\item dans cette boucle (ligne 5), on effectue un test : si \og $u_n-u_{n-1} \neq r$ \fg, cela signifie que la différence entre le terme courant et son précédent n'est pas égal à la raison, ce qui signifie que la suite n'est pas arithmétique (donc \texttt{a} prend la valeur \og False \fg);

\item à la fin de la boucle, on affiche la valeur de \texttt{a}: si c'est \og True \fg, cela signifie que toutes les différences calculées dans la boucle sont égales à $r$ et donc que la suite est arithmétique. 
\end{itemize}

Quand on exécute ce programme, il s'affiche:
\begin{console}
False
\end{console}
En effet, la séquence \og 26 , 29 , 31  \fg{} ne correspond pas à une suite arithmétique car la différence est d'abord égale à 3, puis à 2.

\item \'Ecrivons sur le même modèle que précédemment un programme qui permet de vérifier si \texttt{v} représente une suite géométrique:
\begin{python}
v = [ 100 , 50 , 25 , 12.5 , 6.25 , 3.125 , 1.5625 , 0.78125 , 0.375625 , 0.1878125 , 0.09390625 , 0.046953125 ]

q = v[1] / v[0]
g = True
for n in range(2,len(v)):
    if v[n] / v[n-1] != q:
        g = False
        
print(g)
\end{python}

On commence par calculer l'éventuelle raison $q=\dfrac{v_1}{v_0}$, puis on vérifie si tous les $\dfrac{u_n}{u_{n-1}}$ sont égaux à $q$.

Le résultat est sans appel :
\begin{console}
False
\end{console}
La suite n'est donc pas géométrique. En effet, $\dfrac{50}{100}=\dfrac{1}{2}$ et $\dfrac{0,375625}{0,78125}\neq\dfrac{1}{2}$.
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{calcul du n-ième terme}[1]
\'Ecrire une fonction Python \texttt{u(n)} qui renvoie le $n$-ième terme de la suite définie par :
\[
\begin{cases}
u_0 = 7\\
u_{n+1}= u_n + 3,27
\end{cases}
\]

\tcblower

\smallskip

Il y a plusieurs façons d'aborder cette question :

\begin{enumerate}
\item On peut s'inspirer de la formule de récurrence et faire une boucle itérative pour calculer les termes successifs $u_1$, $u_2$, ... , $u_n$:
\begin{python}
def u(n):
    v = 7 # on ne peut pas nommer le terme 'u' car cette lettre est déjà prise par la fonction
    for k in range(1,n+1):
        v = v + 3.2
        
    return v
\end{python}
\begin{console}
>>> u(8)
32.599999999999994
\end{console}
On s'aperçoit alors que le résultat n'est pas exact (car Python a une façon bien particulière de calculer et de représenter les nombres à virgules... Il fait très souvent des approximations !)

\newpage

\item On peut s'aider de la formule qui donne le terme général d'une suite arithmétique (car il s'agit bien d'une suite arithmétique de premier terme $u_0=7$ et de raison $r=3,2$ :
\[
u_n = u_0 + nr = 7 + 3,2n.
\]
Dans ce cas, la fonction est très simple:
\begin{python*}
def u(n):
    return 7 + 3.2*n
\end{python*}
\begin{console}
>>> u(8)
32.6
\end{console}

\item Il y a une autre possibilité (plus compliquée au prime abord):
\begin{python}
def u(n):
    if n == 0:
        return 7
    else:
        return u(n-1) + 3.2
\end{python}
\begin{console}
>>> u(8)
32.599999999999994
\end{console}
\begin{rem}
Cette dernière façon est la \textit{forme récursive} du programme: dans la fonction \texttt{u(n)}, on fait appel à la fonction elle-même, mais avec un argument différent.

Pour que cette forme fonctionne (sans jeu de mot), il faut penser à \og arrêter \fg{} la fonction à un moment donné: ici, c'est quand on atteint $n=0$, auquel cas on retourne le terme initial. 
\end{rem}

\item En utilisant une boucle conditionnelle :
\begin{python}
def u(n):
    v, k = 7 , 0
    while k < n:
        v = v + 3.2
        k = k + 1       
    return v
\end{python}
\begin{console}
>>> u(8)
32.599999999999994
\end{console}
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{calcul du n-ième terme}[1]
\'Ecrire une fonction \texttt{u(n)} qui renvoie le $n$-ième terme de la suite définie par:
\[
\begin{cases}
u_0 = 7\\
u_{n+1} = 0,3u_n
\end{cases}
\]

\tcblower

\smallskip

Comme dans l'exercice précédent, il y a plusieurs façons de voir la fonction:

\begin{enumerate}
\item On sait que la suite est géométrique, de premier terme $u_0=7$ et de raison $q=0,3$, donc son terme général est :
\[
u_n = u_0 \times q^n = 7 \times 0,3^n.
\]
Ce qui donne la fonction suivante:
\begin{python*}
def u(n):
    return 7 * 0.3**n
\end{python*}
\begin{console}
>>> u(8)
0.00045926999999999985
\end{console}

\item On peut s'inspirer de la relation de récurrence en créant une boucle:
\begin{python}
def u(n):
    v = 7 
    for k in range(1,n+1):
        v = v * 0.3      
    return v
\end{python}
\begin{console}
>>> u(8)
0.00045926999999999996
\end{console}

\item De même, avec la forme récursive:
\begin{python}
def u(n):
    if n == 0: return 7
    else: return u(n-1) * 0.3
\end{python}

\item Avec une boucle conditionnelle:
\begin{python}
def u(n):
    v, k = 7, 0
    while k < n:
        v = v * 0.3 
        k = k + 1       
    return v
\end{python}
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\enlargethispage*{2em}
\begin{exocor}{calcul du n-ième terme}[1]
\'Ecrire une fonction \texttt{u(n)} qui renvoie le $n$-ième terme de la suite définie par:
\[
\begin{cases}
u_0 = 7\\
u_{n+1} = 0.5u_n + 2
\end{cases}
\]

\tcblower

La suite proposée n'est ni une suite arithmétique, ni une suite géométrique. On ne peut donc pas proposer une fonction qui retourne le terme explicite directement (à moins d'avoir étudié mathématiquement cette suite pour trouver l'expression du terme général). 

\begin{enumerate}
\item Solution avec boucle itérative:

\begin{python}
def u(n):
    v = 7
    for k in range(1,n+1):
        v = 0.5*v + 2
        
    return v
\end{python}

\item Solution sous forme récursive:

\begin{python}
def u(n):
    if n == 0: return 7
    else: return 0.5 * u(n-1) + 2
\end{python}

\item Solution avec une boucle conditionnelle:

\begin{python}
def u(n):
    v = 7 
    k = 0
    while k < n:
        v = 0.5 * v + 2
        k = k + 1
        
    return v
\end{python}
\end{enumerate}

Les trois solutions renvoient le même résultat:

\bigskip

\begin{console}
>>> u(8)
4.01171875
\end{console}

\begin{rem}
La solution avec boucle conditionnelle est à mes yeux la moins performante.

En effet, elle introduit une variable \texttt{k} en plus des variables \texttt{u} et \texttt{n}. En terme informatique, cela implique un espace mémoire occupé supplémentaire.
\end{rem}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{algorithme de seuil}[2]
On considère la suite \suite{u}{n}{} définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0 = 9\\
u_{n+1} = 1,2u_n - 1
\end{cases}
\]
Compléter le programme suivant afin qu'il affiche la première valeur de $n$ pour laquelle\linebreak $u_n \geqslant \numprint{1000}$.

\begin{python}
u = ...
n = ...
while u < ...:
    n = ...
    u = ...

print(...)
\end{python}

\tcblower

Le programme complété est le suivant:

\begin{python}
u = 9
n = 0
while u < 1000:
    n = n + 1
    u = 1.2*u - 1

print(n)
\end{python}

Explications:
\begin{itemize}
\item on affecte d'abord la valeur \og 9 \fg{} à la variable \texttt{u}: elle représente le premier terme de la suite \suite{u}{n}{};
\item ensuite, on affecte à \texttt{n} la valeur \og 0 \fg, qui est la valeur de l'indice du premier terme de la suite;
\item on souhaite déterminer la première valeur de $n$ pour laquelle $u_n\geqslant\numprint{1000}$, donc on crée une boucle \textit{conditionnelle} avec pour condition le contraire de ce que l'on veut : \texttt{u<1000}. En effet, \textit{tant que} $u_n<\numprint{1000}$, il faut calculer les termes successifs de la suite: $u_1$, $u_2$,...;
\item ainsi, dans la boucle, on commence par augmenter de 1 la valeur de \texttt{n}, puis on affecte à \texttt{u} la valeur \texttt{1.2*u-1}, qui représente le terme suivant (selon la relation de récurrence de la suite).
\end{itemize}
Une fois la boucle finie, cela signifie que la condition \texttt{u<1000} n'est plus remplie, et donc que \texttt{u} contient une valeur plus grande ou égale à \numprint{1000}. On affiche alors la valeur stockée dans \texttt{n}, qui correspond au rang pour lequel $u_n\geqslant\numprint{1000}$.
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{algorithme de seuil}[2]
On considère la suite \suite{u}{n}{} définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0 = 95\\
u_{n+1} = 0.95u_n + 1
\end{cases}
\]
Compléter le programme suivant afin qu'il affiche la première valeur de $n$ pour laquelle\linebreak $u_n < 20+10^{-9}$.

\begin{python}
u = ...
n = ...
while ...:
    n = ...
    u = ...

print(...)
\end{python}

\tcblower

Le programme complété est le suivant:

\begin{python}
u = 95
n = 0
while u >= 20 + 10**(-9):
    n = n + 1
    u = 0.95*u + 1
print(n)
\end{python}

Explications:
\begin{itemize}
\item on affecte d'abord la valeur \og 95 \fg{} à la variable \texttt{u}: elle représente le premier terme de la suite \suite{u}{n}{};
\item ensuite, on affecte à \texttt{n} la valeur \og 0 \fg, qui est la valeur de l'indice du premier terme de la suite;
\item on souhaite déterminer la première valeur de $n$ pour laquelle $u_n<20+10^{-9}$, donc on crée une boucle \textit{conditionnelle} avec pour condition le contraire de ce que l'on veut : \texttt{u>=20+10**(-9)}. En effet, \textit{tant que} $u_n\geqslant20+10^{-9}$, il faut calculer les termes successifs de la suite: $u_1$, $u_2$,...;
\item ainsi, dans la boucle, on commence par augmenter de 1 la valeur de \texttt{n}, puis on affecte à \texttt{u} la valeur \texttt{0.95*u+1}, qui représente le terme suivant (selon la relation de récurrence de la suite).
\end{itemize}
Une fois la boucle finie, cela signifie que la condition \texttt{u>=20+10**(-9)} n'est plus remplie, et donc que \texttt{u} contient une valeur plus grande ou égale à $20+10^{-9}$. On affiche alors la valeur stockée dans \texttt{n}, qui correspond au rang pour lequel $u_n<20+10^{-9}$.
\end{exocor}




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newpage
\begin{exocor}{algorithme de seuil}[2]
\'Ecrire un programme Python qui détermine le premier entier $n$ tel que $u_n < 4,0001$, où \suite{u}{n}{} est la suite définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0 = 7\\
u_{n+1} = 0.5u_n + 2
\end{cases}
\]

\tcblower

Voici un programme satisfaisant la question:

\begin{python}
n, u = 0, 7 # n prend la valeur 0 et u prend la valeur 7
while u >= 4.0001:
    u = 0.5 * u + 2
    n = n + 1
print(n)
\end{python}

On commence par mettre \og 0 \fg{} dans \texttt{n} (premier indice de la suite) et \og 7 \fg{} dans \texttt{u} (premier terme),  puis on calcule les termes successifs en multipliant la valeur stockée dans \texttt{u} par 0,5 et en ajoutant 2, ce qui donne la nouvelle valeur de \texttt{u} (et qui correspond au terme suivant), tout ceci \textit{tant que} la valeur de \texttt{u} est plus grande ou égale au seuil imposé (4,0001) car on souhaite que ces calculs se fassent jusqu'à ce que $u_n < 4,0001$.

Ainsi, quand on sort de la boucle, cela signifie que la condition n'est plus remplie et donc que $u_n<4,0001$, et la valeur de $n$ affichée correspond à l'indice du premier terme terme pour lequel $u_n<4,0001$.

On obtient:
\begin{console}
15
\end{console}
ce qui signifie que $u_{14}\geqslant4,0001$ mais que $u_{15}<4,0001$.
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{algorithme de seuil}[2]
On considère la suite définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0 = 2\\
u_{n+1} = 1,05u_n + 1
\end{cases}
\]
\'Ecrire un programme Python permettant d'afficher le premier entier $n$ à partir duquel \mbox{$u_n \geqslant 10^9$}.

\tcblower

Voici une proposition de programme:

\begin{python}
u, n = 1, 0 # u prend la valeur 1, n prend la valeur 0
while u < 10**9:
    u = 1.05*u + 1
    n = n + 1
print(n)
\end{python}

On commence par définir le premier $n$ et le premier terme $u_0$, que l'on stocke dans \texttt{u}.

Ensuite, on calcule les termes successifs de la suite \textit{tant que} l'on obtient une valeur plus petite que $10^9$. On n'oublie pas d'ajouter 1 à $n$ à chaque fois car on souhaite afficher le premier $n$ pour lequel on sort de la boucle.

En exécutant ce programme, on obtient:
\begin{console}
363
\end{console}
ce qui signifie que $u_{362}\leqslant10^9$ mais $u_{363}>10^9$.
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{algorithme de seuil}[2]
On considère le programme Python suivant:
\begin{python}
u = -8
s = -8
n = 0

while s < 10**9:
    n = n + 1
    u = u + 1.5
    s = s + u    

print(n)
\end{python}

\begin{enumerate}
\item Expliquer ce que représente la valeur affichée par ce programme.
\item Retrouver cette valeur par le calcul.
\end{enumerate}

\tcblower

\begin{enumerate}
\item Dans ce programme, on commence par initialiser trois variables: \texttt{u}, \texttt{s} et \texttt{n}.

Ensuite, on initialise une boucle \textit{conditionnelle}; la condition est que la valeur contenue dans \texttt{s} doit être strictement inférieure à $10^9$ pour que les instructions suivantes s'exécutent.

Les instructions de la boucle sont les suivantes:
\begin{itemize}
\item d'abord, on ajoute 1 à la valeur contenue dans \texttt{n};
\item ensuite, on ajoute à la valeur contenue dans \texttt{s} celle contenue dans \texttt{u};
\item pour finir, on ajoute 1,5 à la valeur contenue dans \texttt{u}.
\end{itemize}

Faisons un tableau pour représenter les valeurs des variables pas à pas.

\textbf{Astuce du chef:} nous allons disposer les variables dans le même ordre où elles apparaissent dans la boucle: d'abord \texttt{n}, suivie de \texttt{u}, puis de \texttt{s}.

\begin{center}
\begin{tabular}{|>{\ttfamily}Sc|*5{Sc|}}
\hline
n & 0    & 1                 & 2                  & 3     & $\cdots$ \\
\hline
u & $-8$ & $-8+1,5=-6,5$      & $-6,5+1,5=-5$     & $-5+1,5=-3,5$  & $\cdots$ \\
\hline
s & $-8$ & $-8+(-6,5)=-14,5$ & $-14,5+(-5)=-19,5$ & $-19,5+(-3,5)=-23$ & $\cdots$ \\
\hline
\end{tabular}
\end{center}

La valeur affichée par le programme est donc la première valeur de \texttt{n} pour laquelle la condition $s<10^9$ n'est pas remplie, à savoir la première valeur de $n$ pour laquelle la valeur stockée dans \texttt{s} est supérieure ou égale à $10^9$.

\item Si on examine de plus près le tableau précédent, on peut imaginer que la variable \texttt{u} représente les termes d'une suite arithmétique de premier terme $u_0=-8$ et de raison $r=1,5$, et \texttt{s} représente $s_n=u_0+u_1+\cdots+u_n$.

Ainsi, lorsque les variables \texttt{u}, \texttt{s} et \texttt{n} sont initialisées, elles représentent respectivement $u_0$, $s_0$ et le premier indice $n=0$.

Vérification pour les premières valeurs:
\begin{itemize}
\item $u_1=u_0+1,5=-8+1,5=-6,5$, qui correspond à la première valeur de \texttt{u} trouvée dans le tableau (après initialisation);
\item $s_1=u_0+u_1=-8+(-6,5)=-14,5$,  qui correspond à la première valeur de \texttt{s} trouvée dans le tableau (après initialisation);

\medskip

\item $u_2=u_1+1,5=-6,5+1,5=-5$, qui correspond à la valeur suivante de \texttt{u};
\item $s_2=u_0+u_1+u_2=-8+(-6,5)+(-5)=-19,5$, qui correspond à la valeur suivante de \texttt{s}.
\end{itemize} 

Ainsi, la valeur de \texttt{n} affichée par le programme est la première valeur de $n$ pour\linebreak laquelle $s_n \geqslant 10^9$.

Or, le cours nous donne une valeur de $s_n$:
\begin{align*}
s_n & = u_0 + u_1 + u_2 + \cdots + u_n\\
& = (n+1)\times\dfrac{u_0+u_n}{2}\\
& = (n+1)\times\dfrac{u_0+(u_0+nr)}{2}\\
& = (n+1)\times\dfrac{2u_0+nr}{2}\\
& = (n+1)\left(u_0 + \frac{nr}{2}\right)\\
& = (n+1)(0,75n-8)\\
& = 0,75n^2-7,25n-8.
\end{align*}

On souhaite donc résoudre l'inéquation:
\[
0,75n^2-7,25n-8 \geqslant 10^9  \iff 0,75n^2-7,25n-\numprint{1000000008} \geqslant0.
\]
Le discriminant du polynôme $0,75n^2-7,25n-\numprint{1000000008}$ est :
\[
\Delta = (-7,25)^5-4\times0,75\times(-\numprint{1000000008})= \numprint{3000000076,5625}.
\]
Donc $0,75n^2-7,25n-\numprint{1000000008}\geqslant0$ pour $n\geqslant\dfrac{-b+\sqrt{\Delta}}{2a}$, soit pour\linebreak $n\geqslant \numprint{36519,6709663}$.

Le premier $n$ est donc $n=\numprint{36520}$, et c'est bien ce qu'affiche le programme (après\linebreak vérification sur une machine).
\end{enumerate}
\end{exocor}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newpage
\begin{exocor}{somme}[2]
On considère la suite \suite{u}{n}{} définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0 = 5\\
u_{n+1} = 0,3u_n
\end{cases}
\]
\'Ecrire un programme Python permettant de calculer la somme :
\[
u_0 + u_1 + \cdots + u_{\numprint{100}}.
\]

\tcblower

Voici une proposition de programme :

\begin{python}
u = 5
s = 5
for n in range(100):
    u = 0.3 * u # terme suivant
    s = s + u # on ajoute le nouveau 'u' à la somme précédente
    
print(s)
\end{python}

En lignes 1 et 2, on définit le premier terme de la suite et le terme initial de la somme (égal à $u_0$).

Ensuite, on crée une boucle qui exécutera 100 fois les instructions des lignes 4 et 5: à chaque passage dans la boucle, on calcule le terme suivant de la suite et on l'ajoute à la valeur précédente de la somme.

\newpage

On obtient :
\begin{console}
7.142857142857144
\end{console} 

\begin{rem}
On aurait aussi pu utiliser la formule du cours qui nous donne la somme en fonction de $q$ et $n$:
\[
u_0+u_1+\cdots+u_n=u_0\times\dfrac{q^{n+1}-1}{q-1}
\]
ce qui donne le programme suivant:
\begin{python}
u0 = 5
q = 0.3
s = u0 * ( (q**101 - 1) / (q - 1) )
    
print(s)
\end{python}
Mais c'est quand-même moins \textit{fun} n'est-ce pas ?
\end{rem}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{somme}[2]
\'Ecrire un programme Python permettant de calculer la somme des \numprint{1000} premiers termes de la suite \suite{u}{n}{} définie pour tout entier naturel $n$ par:
\[
\begin{cases}
u_0=5\\
u_{n+1}=\frac{1}{2}u_n + 2n-3
\end{cases}
\]

\tcblower

Avant tout, je vais légèrement modifier la relation de récurrence de la suite:
\[
\begin{cases}
u_0=5\\
u_{n}=\frac{1}{2}u_{n-1} + 2(n-1)-3 = \frac{1}{2}u_{n-1} + 2n - 5
\end{cases}
\]
L'avantage de cette relation de récurrence est de faire apparaître la relation entre le terme courant ($u_n$) et son précédent ($u_{n-1}$), ce qui peut nous aider pour le programme Python.

\begin{python}
u = 5
s = 5

for n in range(1,1000):
    u = 0.5*u + 2*n - 5
    s = s + u
    
print(s)
\end{python}

\medskip

N'oublions pas que dans l'écriture \og \texttt{u = 0.5*u + 2*n - 5} \fg, le \texttt{u} de gauche représente $u_n$ (le terme que l'on calcule) et le \texttt{u} de droite représente le terme précédent (donc $u_{n-1}$).

\medskip

En exécutant le programme, on obtient:
\begin{console}
1984038.0
\end{console}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{}[2]
On considère le programme suivant:
\begin{python}
def somme(n):
    s = 0
    for k in range(1,n+1):
        s = s + 1 / k**2
        
    return s
\end{python}

\begin{enumerate}
\item Que renvoie cette fonction ?
\item On obtient en mode console:
\begin{console}
>>> somme(1000)
1.6439345666815615
>>> somme(10000)
1.6448340718480652
>>> somme(10**7)
1.6449339668472596
\end{console}
Quelle conjecture peut-on faire à la vue de ces résultats ?
\end{enumerate}

\tcblower

\begin{enumerate}
\item Dans la fonction \texttt{somme(k)}, on initialise la variable \texttt{s} à 0.

Ensuite, on crée une boucle itérative  (\texttt{k} prendra des valeurs de 1 à \texttt{n}) dans laquelle on stocke dans \texttt{s} la somme de la valeur qu'il y avait précédemment et de $\dfrac{1}{k^2}$.

Ainsi, la fonction calcule et renvoie la somme:
\[
S_n = 0 + \dfrac{1}{1^2} + \dfrac{1}{2^2} + \dfrac{1}{3^2} + \cdots + \dfrac{1}{n^2}
\]
c'est-à-dire:
\[
S_n =1 + \dfrac{1}{2^2} + \dfrac{1}{3^2} + \cdots + \dfrac{1}{n^2}=\sum_{k=1}^n\dfrac{1}{k^2}.
\]

\item À la vue des résultats successifs affichés pour \texttt{somme(1000)}, \texttt{somme(10000)} et \texttt{somme(10**7)}, on constate que la somme $S_n$ se rapproche de plus en plus d'un nombre dont une valeur approchée est $1,644$.

On pourrait alors conjecturer que $S_n$ vaut à peu près 1,644 quand $n$ tend vers l'infini.
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{}[3]
\begin{enumerate}
\item \'Ecrire un programme Python permettant de calculer la somme:
\[
1 - \dfrac{1}{3} + \dfrac{1}{5} - \dfrac{1}{7} + \dfrac{1}{9} - \dfrac{1}{11} + \cdots + \dfrac{1}{\numprint{2021}}=\sum_{p=0}^{\numprint{1010}}\dfrac{(-1)^{p}}{2p+1}.
\]

\item \'Ecrire un programme en Python permettant de calculer la somme:
\[
\sum_{p=1}^{\numprint{2021}} \dfrac{1}{3p-1}.
\]
\end{enumerate}

\tcblower

\begin{enumerate}
\item Voici un programme possible :
\begin{python}
s = 0
for p in range(1011):
    s = s + (-1)**p / (2*p + 1)
print(s)
\end{python}

La formule à insérer dans la boucle est clairement dictée par celle qu'il y a dans le symbole de sommation de l'énoncé: 
\[
\sum_{p=0}^{\numprint{1010}}\dfrac{(-1)^{p}}{2p+1}.
\]
Cette façon d'écrire est très utile car la fonction Python reprend les indices: on voit que la somme part de $p=0$ et va jusqu'à $p=\numprint{1010}$; comme nous utilisons la fonction \texttt{range}, il ne faut pas oublier d'indiquer le \og nombre limite\fg{} auquel on ajoute 1.

\item Un programme possible est le suivant:
\begin{python}
s = 0
for p in range(1,2022):
    s = s + 1 / (3*p - 1)  
print(s)
\end{python}
Je me suis là encore inspiré de la somme donnée en énoncé avec le symbole de\linebreak sommation.
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section*{Produit scalaire}

\begin{exocor}{}[1]
\textit{Dans cet exercice, on n'utilisera pas le module \texttt{numpy}.}
\begin{enumerate}
\item \'Ecrire une fonction Python \texttt{produit(u,v)} qui renvoie le produit scalaire de deux vecteurs $\vv{u}\dbinom{x}{y}$ et $\vv{v}\dbinom{x'}{y'}$, représentés respectivement par les variables \texttt{u = (x , y)} et\linebreak \texttt{v = (x' , y')}.

\item \'Ecrire une fonction Python \texttt{isOrth(u,v)} qui renvoie \og True \fg{} dans le cas où  $\vv{u}$ et $\vv{v}$ sont orthogonaux, et \og False \fg{} dans le cas contraire.
\end{enumerate}

\tcblower

\begin{enumerate}
\item Le produit scalaire de deux vecteurs $\vv{u}\dbinom{x}{y}$ et $\vv{v}\dbinom{x'}{y'}$ est égal à $xx' + yy'$, d'où la fonction Python suivante:
\begin{python*}
def produit(u,v):
    return u[0] * v[0] + u[1] * v[1]
\end{python*}

\item On sait que $\vv{u}$ et $\vv{v}$ sont orthogonaux si et seulement si $\prodscal{u}{v}=$, d'où la fonction suivante:

\begin{python*}
def isOrth(u,v):
    return produit(u,v) == 0
\end{python*}

que l'on peut aussi écrire sous la forme suivante:

\begin{python*}
def isOrth(u,v):
    if produit(u,v) == 0: return True
    else: return False
\end{python*}

On a par exemple, en mode console :

\begin{console}
>>> isOrth( (-1,3) , (3,1) )
True
>>> isOrth( (-1,3) , (3,-1) )
False
\end{console}
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{}[2]
\'Ecrire une fonction Python \texttt{centre\_rayon(cercle)} qui renvoie les coordonnées du centre et le rayon d'un cercle dont l'équation cartésienne est sous la forme $x^2+y^2+ax+by+c=0$.

L'argument \texttt{cercle} est alors sous la forme d'un triplet \texttt{(a,b,c)}. Par exemple, 
\begin{center}
\texttt{cercle = (-1, 2, -8)}
\end{center}
représente le cercle dont une équation cartésienne est:
\[
x^2 + y^2 - x + 2y - 8 = 0
\]

\tcblower

Il est nécessaire de faire un travail mathématique avant toute chose.
\begin{align*}
x^2+y^2+ax+by+c=0 
& \iff (x^2 + ax) + (y^2 + by) + c =0\\
& \iff \left[\left(x + \frac{a}{2}\right)^2-\left(\frac{a}{2}\right)^2\right] + \left[\left(y + \frac{b}{2}\right)^2-\left(\frac{b}{2}\right)^2\right] + c = 0\\
& \iff \left(x + \frac{a}{2}\right)^2 + \left(y + \frac{b}{2}\right)^2 = \left(\frac{a}{2}\right)^2 + \left(\frac{b}{2}\right)^2 - c
\end{align*}

On a ainsi une équation de la forme:
\[
(x-x_C)^2 + (x-y_C)^2 = r^2
\]
avec:
\[
x_C = -\frac{a}{2},\qquad
y_C = -\frac{b}{2}\]
et
\[
r=\sqrt{\left(\frac{a}{2}\right)^2 + \left(\frac{b}{2}\right)^2 - c}\quad\text{si }\left(\frac{a}{2}\right)^2 + \left(\frac{b}{2}\right)^2 - c\geqslant0.
\]
Ceci nous donne alors les coordonnées du centre $C$ du cercle, ainsi que son éventuel rayon $r$.

D'où la fonction Python suivante:

\begin{python}
def centre_rayon( cercle ):
    xC = -cercle[0] / 2 # correspond à '-a/2'
    yC = -cercle[1] / 2 # correspond à '-b/2'
    
    R = xC**2 + yC**2 - cercle[2] # correspond à (a/2)^2 + (b/2)^2 - c
    
    if R >= 0:
        r = R**0.5 # racine carrée de R
        return (xC, yC) , r
    else:
        return "Ce n'est pas une équation cartésienne de cercle."
\end{python}

Testons en mode console:

\begin{console}
>>> centre_rayon( (-1,2,-8) )
((0.5, -1.0), 3.0413812651491097)
\end{console}

Ceci nous indique que si on considère le cercle d'équation $x^2+y^2-x+2y-8=0$, son centre a pour coordonnées \coord{\frac{1}{2}}{-1} et pour rayon $r\approx3,04$.

\bigskip

Pour éviter d'avoir à écrire une double paire de parenthèses lors de l'appel à la fonction \texttt{centre\_rayon}, on pourrait ajouter une astérisque devant l'argument de la fonction et écrire:
\begin{python}
def centre_rayon( *cercle ):
    xC = -cercle[0] / 2 # correspond à '-a/2'
    yC = -cercle[1] / 2 # correspond à '-b/2'
    
    R = xC**2 + yC**2 - cercle[2] # correspond à (a/2)^2 + (b/2)^2 - c
    
    if R >= 0:
        r = R**0.5 # racine carrée de R
        return (xC, yC) , r
    else:
        return "Ce n'est pas une équation cartésienne de cercle."
\end{python}
Ainsi, on n'aurait plus qu'à écrire :
\begin{console}
>>> centre_rayon(-1,2,-8)
((0.5, -1.0), 3.0413812651491097)
\end{console}
Mais cela dépasse largement les capacités attendues en mathématiques concernant Python.
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section*{Probabilités}

\begin{exocor}{méthode de Monte-Carlo en Python}[2]
Dans un carré de côté 1, on trace la parabole d'équation $y=x^2$.
\begin{center}
\begin{tikzpicture}[scale=5]
\draw (0,0) rectangle (1,1);
\fill[orange!20] (0,0) plot[domain=0:1,samples=100] (\x,{\x*\x}) -- (1,0) -- cycle;
\draw[orange] plot[domain=0:1,samples=100] (\x,{\x*\x});
\end{tikzpicture}
\end{center}

L'objectif de cet exercice est d'estimer l'aire sous la courbe (représentée en couleur) à l'aide d'un programme en Python.

Pour cela, nous allons utiliser la méthode de Monte-Carlo qui consiste à exécuter l'algorithme suivant :
\begin{enumerate}
\item on choisit un point au hasard à l'intérieur du carré;
\item s'il est sous la courbe, on incrémente un compteur; sinon, on laisse le compteur tel qu'il est;
\item on répète ceci un certain nombre de fois;
\item à la fin, on calcule la fréquence de points sous la courbe : c'est l'estimation souhaitée.
\end{enumerate}

\'Ecrire un programme Python affichant une estimation de l'aire en prenant par exemple \numprint{50000} points au hasard.

\tcblower

\smallskip

Un programme possible est le suivant :

\begin{python}
from random import random
n = 0

for i in range(5000):
    x = random()
    y = random()
    if (y < x*x):
        n = n + 1

print(n/5000)
\end{python}

Explications:
\begin{itemize}
\item on commence par importer le module \textit{random} qui permet de simuler l'aléatoire;
\item on initialise la variable \texttt{n} à 0 : c'est la variable qui va compter le nombre de points en dessous de la courbe;
\item on crée une boucle de sorte à répéter \numprint{5000} fois l'expérience consistant à choisir au hasard deux nombres \texttt{x} et \texttt{y}, qui vont jouer le rôle des coordonnées du point;
\item on teste si $y<x^2$ : si tel est le cas, cela signifie que le point est bien en dessous de la courbe et on incrémente \texttt{n} (on lui ajoute 1);
\item une fois la boucle finie, on affiche la valeur de $\dfrac{n}{\numprint{5000}}$, qui correspond à la fréquence de points obtenus en dessous de la courbe.
\end{itemize}


Voici trois résultats successifs: $0,3358\qquad;\qquad0,3326\qquad;\qquad0,3308$.

On peut modifier légèrement le programme afin qu'il simule plus de choix:

\begin{python}
from random import random
n = 0
c = int(input('Nombre de choix : '))
for i in range(c):
    x = random()
    y = random()
    if (y < x*x):
        n = n + 1
        
print(n/c)
\end{python}

On peut ainsi choisir le nombre de choix. Il semble que plus le nombre de choix augmente, plus la fréquence se rapproche de 0,333333...
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{}[2]
À l'intérieur d'un carré de côté 2, on trace un disque de rayon 1.
\begin{center}
\begin{tikzpicture}[scale=3]
\draw (0,0) rectangle (2,2);
\filldraw[fill=orange!20,draw=orange] (1,1)  circle(1cm);
\end{tikzpicture}
\end{center}
On sait que l'aire du disque est égale à $\pi$.

L'objectif de cet exercice est d'obtenir une approximation de $\pi$ à l'aide d'un programme\linebreak Python qui simule l'expérience suivante:
\begin{enumerate}
\item on choisit au hasard un point dans le carré;
\item si ce point est à l'intérieur du disque, on incrémente un compteur; sinon, on laisse ce compteur tel qu'il est;
\item on répète $n$ fois;
\item à la fin, on calcule la fréquence de points obtenus dans le disque, et on en déduit une valeur approchée de $\pi$.
\end{enumerate}

\'Ecrire une fonction Python \texttt{approxPi(n)}, admettant en argument le nombre de points souhaités pour cette expérience, qui retourne une valeur approchée de $\pi$ en suivant cette expérience.

\tcblower

\smallskip

Voici une proposition de fonction Python permettant de donner une approximation de $\pi$ suivant l'expérience décrite dans l'énoncé :

\begin{python}
from random import random

def approxPi(n):
    c = 0
    for k in range(n):
        x = 2 * random()
        y = 2 * random()
        if ( (x-1)**2 + (y-1)**2 )**0.5 <= 1:
            c = c + 1
            
    f = c / n # fréquence de points à l'intérieur du disque
    print( 4*f )

\end{python}
\begin{console}
>>> approxPi(10**6)
3.1428
>>> approxPi(5*10**6)
3.1418528
\end{console}
Cette fonction a tout de même ses limites... En particulier, pour des valeurs de $n$ supérieures à $10^7$, le temps d'exécution est très long, et ce pour un résultat très approximatif (uniquement 3 chiffres corrects après la virgule).

Il est donc clairement évident que l'on ne peut pas envisager une telle fonction pour avoir de très bonnes approximations de $\pi$.
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section*{Variables aléatoires}



\begin{exocor}{}[1]
On considère la variable aléatoire $X$ dont la loi de probabilité est donnée dans le tableau ci-dessous:
\begin{center}
\begin{tabular}{|Sl|*7{Sc|}}
\hline
$X=x_i$ & 1 & 2 & 3 & 4 & 5 & 6 & 7\\
\hline
$P(X=x_i)$ & 0,05 & 0,12 & 0,21 & 0,14 & 0,17 & 0,27 & 0,04\\
\hline
\end{tabular}
\end{center}

On modélise en Python cette loi de probabilité par la variable \texttt{X} ci-dessous:
\begin{python*}
X = [ (1,0.05) , (2,0.12) , (3,0.21) , (4,0.14), (5,0.17) , (6,0.27) , (7,0.04) ]
\end{python*}

On considère la variable $Y=-3X+5$.

\medskip

\'Ecrire un script  Python permettant de créer la liste \texttt{Y} modélisant la loi de probabilité de $Y$.

\tcblower

\smallskip

Pour construire la variable \texttt{Y}, on peut procéder ainsi:

\begin{python}
X = [ (1,0.05) , (2,0.12) , (3,0.21) , (4,0.14), (5,0.17) , (6,0.27) , (7,0.04) ]
Y = [ ]
for c in X:
    Y.append( ( -3*c[0]+5 , c[1] ) )
\end{python}

\textit{Explications:}
\begin{itemize}
\item on commence par initialiser la variable \texttt{Y} comme étant une liste vide (crochets sans rien à l'intérieur);
\item on parcourt la liste \texttt{X} avec l'instruction \og \texttt{for c in X:} \fg{}: ici, chaque valeur que va prendre la variable \texttt{c} sera une valeur contenue dans la liste \texttt{X}; donc \texttt{c} est un couple, où \texttt{c[0]} représentera la valeur de $X$ et \texttt{c[1]} sa probabilité;
\item on insère dans la liste \texttt{Y} le couple \texttt{(-3*c[0]+5,c[1])} car $Y=-3X+5$, ce qui signifie que chaque valeur de $Y$ est obtenue en multipliant par $-3$ chaque valeur que prend $X$ et en ajoutant 5, et que chaque probabilité reste inchangée.
\end{itemize}

\begin{rem}
On peut aussi envisager de construire la liste \texttt{Y} par \textit{compréhension}, où la syntaxe est plus concise:
\begin{python}
X = [ (1,0.05) , (2,0.12) , (3,0.21) , (4,0.14), (5,0.17) , (6,0.27) , (7,0.04) ]
Y = [ ( -3*c[0]+5 , c[1] ) for c in X]
\end{python}
\end{rem}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newpage

\begin{exocor}{}[2]
On considère la variable aléatoire $X$ dont la loi de probabilité est donnée dans le tableau ci-dessous:
\begin{center}
\begin{tabular}{|Sl|*7{Sc|}}
\hline
$X=x_i$ & 1 & 2 & 3 & 4 & 5 & 6 & 7\\
\hline
$P(X=x_i)$ & 0,05 & 0,12 & 0,21 & 0,14 & 0,17 & 0,27 & 0,04\\
\hline
\end{tabular}
\end{center}

On modélise en Python cette loi de probabilité par la variable \texttt{X} ci-dessous:
\begin{python*}
X = [ (1,0.05) , (2,0.12) , (3,0.21) , (4,0.14), (5,0.17) , (6,0.27) , (7,0.04) ]
\end{python*}

\begin{enumerate}
\item \'Ecrire une fonction Python \texttt{esperance(X)} qui renvoie la valeur de l'espérance de $X$.
\item \'Ecrire une fonction Python \texttt{variance(X)} qui renvoie la valeur de la variance de $X$.
\end{enumerate}

\tcblower

\begin{enumerate}
\item Voici une proposition de fonction pour calculer l'espérance de $X$:

\begin{python}
def esperance(X):
    s = 0
    for c in X:
        s = s + c[0]*c[1]
        
    return s
\end{python}

Pour écrire cette fonction, je me suis inspiré de la formule permettant de calculer l'espérance:
\[
E(X)=\sum_{i=1}^n x_i \times pi
\]
On doit donc ajouter tous les produits $x_i \times p_i$, représentés en Python par \texttt{c[0]*c[1]}.

\item La variance de $X$ est l'espérance de la variable aléatoire $(X - E(X))^2$. On peut donc écrire la fonction suivante:
\begin{python}
def variance(X):
    E = esperance(X)
    Y = [ ]
    for c in X:
        Y.append( ( (c[0]-E)**2 , c[1] ) )
    return esperance(Y)
\end{python}

Si vous êtes à l'aise avec les listes par compréhension, on peut aussi considérer le programme page suivante.

\newpage

\begin{python}
def variance(X):
    E = esperance(X)
    # on construit Y par compréhension
    Y = [ ((c[0]-E)**2 , c[1]) for c in X ]
    
    return esperance(Y)
\end{python}

\begin{console}
>>> esperance(X), variance(X)
4.23, 2.6971
\end{console}
\end{enumerate}
\end{exocor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{exocor}{formule de König-Huygens}[2]
On considère la variable aléatoire $X$ dont la loi de probabilité est donnée dans le tableau ci-dessous:
\begin{center}
\begin{tabular}{|Sl|*7{Sc|}}
\hline
$X=x_i$ & 1 & 2 & 3 & 4 & 5 & 6 & 7\\
\hline
$P(X=x_i)$ & 0,05 & 0,12 & 0,21 & 0,14 & 0,17 & 0,27 & 0,04\\
\hline
\end{tabular}
\end{center}

On modélise en Python cette loi de probabilité par la variable \texttt{X} ci-dessous:
\begin{python*}
X = [ (1,0.05) , (2,0.12) , (3,0.21) , (4,0.14), (5,0.17) , (6,0.27) , (7,0.04) ]
\end{python*}

La formule de König-Huygens nous donne un moyen de calculer la variance de $X$ en connaissance son espérance:
\[
V(X)=E(X^2)-\big[E(X)\big]^2.
\]

\'Ecrire une fonction \texttt{variance(X)} calculant la variance de $X$ en utilisant cette formule et qui renvoie cette valeur.

Pour cela, on suppose que l'on a déjà une fonction \texttt{esperance(X)} qui renvoie l'espérance de $X$ (comme présentée dans l'exercice \ref{exercice:\thechapter.22}).

\tcblower

Une fonction possible est la suivante:
\begin{python}
def variance(X):
    Y = [ ]
    for c in X:
        Y.append( ( c[0]**2 , c[1] ) )
    return esperance(Y) - esperance(X)**2
\end{python}

que l'on peut aussi écrire de façon plus concise comme suit (en construisant la liste \texttt{Y} par compréhension):

\begin{python}
def variance(X):
    Y = [ ( c[0]**2 , c[1] ) for c in X ]
    return esperance(Y) - esperance(X)**2
\end{python}

\begin{rem}
Vous voyez ici l'importance de créer des fonctions pour calculer des quantités bien définies (comme l'espérance) dans un programme. En effet, on faisant ainsi, on peut se servir de ces fonctions dans d'autres fonctions pour aller plus vite et que pour le programme soit plus lisible.
\end{rem}
\end{exocor}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\corriges
