##TP1 - Introduction à Python

## 2-Utilisation de la console (page droite)
"""
Question 1
La console peut s'assimiler à une calculatrice. Les opérateurs +,-,*,/ sont les opérateurs classiques de calcul sur les nombres.

Question 2
L'opérateur // donne le quotient de la division euclidienne et % son reste.
L'opérateur ** sert à calculer une puissance.

Question 3
Il est possible d'indiquer plusieurs calculs différents sur une même ligne en les séparant par un point-virgule.
Suivant les versions Python, soit les résultats sont affichés les uns en dessous des autres, soit seul le dernier est affiché.
"""

## 3-Variables et types simples (à traiter dans la console)
"""
Question 4
L'affectation d'une valeur à une variable se fait à l'aide du symbole =.
ATTENTION, on commence toujours par le nom de la variable, ex: x=3.

Question 5
Modifier la valeur de la variable x ne modifie pas la valeur de y !
En effet, affecter une variable d'une valeur c'est lui dédier un espace mémoire. Modifier l'espace mémoire associée à x (c'est-à-dire sa valeur) ne modifie pas l'espace mémoire associé à y.
Autrement dit, y=x n'est pas une relation d'EGALITE mais simplement l'AFFECTATION à "la boîte" y de la valeur précédemment affectée à x.

Qestion 6
La variable x est de type 'int' c'est-à-dire que c'est un entier.

Question 7
Ajouter 1 à x ne modifie pas son type.
Ajouter 0.5 si, en effet x n'est plus entier mais réel appelé 'float' en anglais.

Question 8
Si l'on commence par affecter la valeur de y à x alors on écrase la précédente valeur de x.
On pourrait créer une troisième variable z servant à sauver la valeur de x mais on utiliserai de l'espace mémoire pour rien.
Pour échanger les valeurs de deux variables en une seule instruction, il suffit de considérer le couple (x,y). Ainsi on tapera dans la console x,y=y,x.

Question 9
La variable valeur est de type 'str' c'est-à-dire 'string' en anglais, c'est une chaîne de caractères.
Les chaînes de caractères sont toujours notées entre guillements.
ATTENTION print("bonjour") affichera bonjour sans les guillemets pour permettre de faire des fonctions d'affichage jolie.

Question 10
L'opérateur (str)*(int) sur les chaînes de caractères dupliquent cette dernière le nombre de fois souhaité. Autrement dit, elle répète autant de fois qu'indiqué la chaîne de caractères en les collant les unes aux autres.

Question 11
La commande int() transforme l'objet en un entier.
ATTENTION, la variable 'valeur' est quant à elle toujours une chaîne de caractères. L'effet de int() est ponctuel.

Question 12
La commande int(valeur*2) transforme la chaîne de caractères '33' en l'entier 33. L'opérateur *2 est donc appliqué avant le changement de type (priorité aux parenthèses).

Question 13
Une variable n'a pas de type pré-défini, changer la valeur affectée à la variable change son type si nécessaire.

Question 14
Comme valeur est maintenant un entier, l'opérateur * calcule une multiplication.

Question 15
La commande str() transforme l'objet en une chaîne de caractères.
ATTENTION, cette commande ne modifie pas le type de la variable. Elle a un effet ponctuel.

Question 16
La commande str(valeur*2) transforme l'entier 12 en chaîne de caractères '12'. L'opérateur *2 est donc appliqué avant le changement de type (priorité aux parenthèses).

Question 17
La commande str(valeur)*2 transforme l'entier 6 en chaîne de caractères puis la duplique. L'opérateur *2 est donc appliqué après le changement de type.

Question 18
Pyhton répond par "True" ou "False", appelés booléens, aux questions posées. Comme 2<3 est vraie, il répond True.
L'opérateur == sert à tester l'égalité et l'opérateur != sert à tester la différence. ATTENTION, le = d'affectation est différent du == d'égalité.

Question 19
NON False vaut True ; NON True vaut False
False OU False vaut False ; True OU False vaut True ; False OU True vaut True ; True OU True vaut True
False ET False vaut False ; True ET False vaut False ; False ET True vaut False ; True ET True vaut True
"""

##4-Modules et fonctions (à traiter dans l'éditeur)
"""
Question 20
Python ne connaît pas la fonction sin !

Question 21
La commande 'import math' sert à importer (charger) le module math. Vous prévenez python que vous allez faire appel aux fonctions pré-enregistrées de ce module.

Question 22
Dans la console, taper sin(1) mène à une erreur, Python ne connaît pas la fonction sin. En effet, on a chargé le module math mais on n'a pas dit à Python que la fonction sin en fait partie. On tape dans la console math.sin(1).

Question 23
Le module math ne contient pas que des fonctions. Il contient aussi des constantes pré-définies comme pi.

Question 24
Pour connaître ce que contient un module ou la syntaxe a utilisée, on utilise la commande help(math).
ATTENTION à bien lire les explications et définitions ! On peut aussi utiliser l'aide interactive de l'interface pyzo (propre à cette interface).

La commande 'from math import *' permet d'importer toutes les fonctions et constantes du module math. Ainsi, plus besoin de préciser que les fonctions viennent du module math, Python les reconnaîtra.
ATTENTION, Python a chargé toutes fonctions du module mais pas le module en lui-même. Il ne connaît donc pas math. Impossible d'utiliser help(math) si vous n'avez pas utilisé la commande de la question 20.

Question 25
Pour évaluer cette expression, il suffit de la copier dans la console !

Question 26
La racine carré se note sqr ('square root' en anglais). Ici on veut donc calculer :
sqr(1+ sqr(1+ sqr(1+ sqr(1+ sqr(1+ sqr(2)))))).
"""

## 5-Introduction à l'écriture de fonctions
"""
Question 27
"""
def carre(x):
    return x*x
"""
ATTENTION, les copier-coller mènent à des erreurs dûs à des mauvaises polices d'écriture ou des espaces !

Question 28
ATTENTION, si on ne compile pas ce que l'on a écrit dans l'éditeur, la console (calculatrice) ne peut pas l'utiliser !
Pour compiler la totalité du script, on peut utiliser le raccourci Ctrl+E.
Pour ne compiler qu'une partie du script, on peut sélectionner la partie du code à éxecuter et utiliser le raccourci Alt+Entrée.

Pensez aussi à sauvegarder régulièrement, dans votre espace personnel, votre travail. Vous pouvez utiliser le raccourci Ctrl+S.

Si on compile la question 26, rien ne s'affiche !

Question 29
Même si rien ne s'est affiché, Python connaît maintenant notre fonction valeurAbsolue. Si on appelle la fonction c'est-à-dire
-si on tape dans la console carre(5)
-ou si on tape dans l'éditeur print(carre(5)),
Python nous renvoie 25 etc.

Question 30
"""
def sommeCarre(x,y):
    return carre(x)+carre(y)
"""
Question 31
Pour pouvoir tester sommeCarre(-2,3), il faut compiler son fichier !
Pyhton nous renvoie bien 13.

Question 32
Return STOPPE la fonction (et donc n'éxecute pas les instructions suivantes, si il y en a) et affiche la valeur demandée.
"""
def sommeCarre2(x,y):
    print(carre(x)+carre(y))

"""
Même résultat avec des print à la place des return. Mais un print NE stoppe PAS une fonction (le programme continuera d'éxecuter les instructions si il en reste).
"""

"""
Question 33
Un entier est pair si il est multiple de 2.
"""
def est_pair(n) :
    return n%2==0

print(est_pair(4))
print(est_pair(-3))

"""
Question 34
1. On teste d'abord si il est multiple de 3, ensuite si il est multiple de 5. On accepte les deux cas, on fait une disjonction.
"""
def est_multiple3o5(n) :
    return (n%3==0) or (n%5==0) #multiple de 3 OU de 5

print(est_multiple3o5(6))
print(est_multiple3o5(10))
print(est_multiple3o5(2))
"""
2.On teste d'abord si il est multiple de 3, ensuite si il n'est pas multiple de 5. On veut les deux en même temps, on fait une conjonction.
"""
def est_multiple3p5(n) :
    return (n%3==0) and (n%5!=0) #multiple de 3 MAIS PAS de 5

print(est_multiple3p5(6))
print(est_multiple3p5(30))
print(est_multiple3p5(2))
"""
3. Comme 3 et 5 sont premiers entre eux, un multiple de 3 et 5 est un multiple de 15.
"""
def est_multiple35(n) :
    return (n%15==0)  #multiple de 3 et 5 donc de 15

print(est_multiple3p5(6))
print(est_multiple3p5(30))
print(est_multiple3p5(2))

"""
Question 35
Deux nombres ont le même signe si leur produit est positif.
"""
def signe(x,y) :
    return x*y>=0  #si x et y sont de même signes, xy>=0

print(signe(1,5))
print(signe(-1,5))
print(signe(-1,-5))

"""
Question 36
1. Python affiche :
1
1
2
3
5
8

2.Python est d'accord ! Il ajoute un None pour spécifier qu'il n'y a pas de valeur de retour (return).
"""
def fonction() :
    a=1; print(a)
    b=1; print(b)
    c=a+b; print(c)
    d=b+c; print(d)
    e=c+d; print(e)
    f=d+e; print(f)

print(fonction())
"""
3. La fonction calcule les 6 premiers termes de la suite de Fibonacci.


Question 37
1. Il suffit de faire l'opération 1-a.
"""
def non(a) :
    return 1-a

print(non(1))
print(non(0))
"""
2.Le et est vrai si et seulement si a et b sont vrais en même temps. Autrement dit, le résultat doit être égal à 1 si et seulement si a=b=1, et il vaut 0 sinon. C'est la multiplication !
"""
def et(a,b) :
    return a*b

print(et(0,0))
print(et(0,1))
print(et(1,0))
print(et(1,1))
"""
3.Le ou est vrai si l'un au moins est vrai. On a envie de faire une addition. ATTENTION, si a=b=1, a+b=2, il faut retrancher leur produit a*b=1!
"""
def ou(a,b) :
    return a+b-a*b

print(ou(0,0))
print(ou(0,1))
print(ou(1,0))
print(ou(1,1))

"""
Question 38
1. a implique b est équivalent à non(a) ou b. Pour vérifier le résultat, il suffit de comparer les tables de vérités de ces deux expressions.
"""
def implique(a,b) :
    return ou(non(a),b)

print(implique(0,0))
print(implique(0,1))
print(implique(1,0))
print(implique(1,1))
"""
2.(a) a équivaut à b est équivalent à a implique b et b implique a
"""
def equivalent(a,b) :
    return et(implique(a,b),implique(b,a))

print(equivalent(0,0))
print(equivalent(0,1))
print(equivalent(1,0))
print(equivalent(1,1))
"""2.(b) Comme précédemment, a équivaut à b est équivalent à (non a ou b) et (a ou non b). A vérifier sur les tables de vérités !
"""
def equivalent2(a,b) :
    return et(ou(non(a),b),ou(a,non(b)))

print(equivalent2(0,0))
print(equivalent2(0,1))
print(equivalent2(1,0))
print(equivalent2(1,1))


##6-Extra
"""
Question 39
La suite de Fibonacci est définie par u0 = 1, u1 = 1 et u(n+1)=u(n) +u(n-1).
"""
#fibonnacci prend en entrée un entier n et renvoie le nème terme de la suite de Fibonacci
def fibonacci(n) : #calcule le terme d'indice n de la suite
    fibo0 = 1 #f0 = 0ème terme
    fibo1 = 1 #f1 = 1er terme
    for i in range(2,n+1) : #pour i allant de 2 à n
        fibo0, fibo1 = fibo1, fibo0+fibo1 #fibo0 contient f(i-1) et fibo1 contient fi
    return fibo1 #on renvoit le nème terme contenu dans fibo1

print(fibonacci(5))
print(fibonacci(20))
"""OU BIEN"""
def fibonaccirec(n) : #calcule le terme d'indice n de la suite
    if n==0 or n==1 : #Cas de base
        return 1
    else:
        return fibonaccirec(n-1) + fibonaccirec(n-2) #formule de récurrence

print(fibonaccirec(5))
print(fibonaccirec(20))

#Fibonacci prend en entrée un entier n et affiche les n premiers termes de la suite de Fibonacci
def Fibonacci(n) :
    for i in range(0,n+1): #Pour i allant de 0 à n
        print(fibonacci(i)) #On affiche le ième terme de la suite

Fibonacci(5)
Fibonacci(20)

"""
Question 40
"""
def puissance2(n) :
    k=0 #initialisation, k=0 minimum
    while 2**(k+1) <= n: #tant que 2^(k+1) est plus petit que n
        k=k+1 #on augmente k
    return k

print(puissance2(2))
print(puissance2(49))

"""
Question 41
"""
#est_premier prend en entrée un entier n et renvoie un booléen indiquant si n est premier
def est_premier(n) :
    if n ==0 or n==1 : #Cas de base
        return False
    for i in range(2,n/2): #Pour tout i entier inférieur strict à n/2
        if n%i == 0 : #si n est divisible par i
            return False #n n'est pas premier
    return True #sinon, aucun entier inférieur strict à n/2 divise n donc n est premier

print(est_premier(2))
print(est_premier(1))
print(est_premier(49))
print(est_premier(47))
print(est_premier(99))

def pgpremier(n) :
    if n<2 : #Cas de base
        return "pas de nombre premier inférieur à 1"
    k=n
    while not(est_premier(k)) : #tant que k n'est pas premier
        k=k-1 #on passe à l'entier précédent
    return k #k est le plus grand entier premier inférieur à n

print(pgpremier(2))
print(pgpremier(100))
"""OU BIEN"""
#une version plus efficace car effectuant moins de calculs
def pgpremier2(n) :
    if n<2 : #Cas de base
        return "pas de nombre premier inférieur à 1"
    elif n==2 : # 2 est le seulk nombre premier inférieurs ou égales à 2 donc a fortiori le plus grand
        return 2
    elif n%2==0 : #si n est pair mais pas égale à 2, le premier candidat possible est n-1
        k=n-1
    else :  #sinon n est impair et pas égale à 1, il est le premier candidat possible
        k=n
    while not(est_premier(k)) : #tant que k n'est pas premier
        k=k-2 #les nombres pairs ne sont pas premier, on passe au prochain impair
    return k #k est le plus grand entier impair premier inférieur à n (n != 2)

print(pgpremier2(2))
print(pgpremier2(100))


"""
Question 42 : Somme de 3 carrés
Comme un carré est positif et que 100**2=10000, les nombres inférieurs à 10000 s'écrivant comme somme de trois carrés font intervenir des carrés d'entier <=100.
"""
#appartient prend en entrée un élément x et une liste l et renvoie un booléen indiquant si l'élément x apparaît dans la liste l.
def appartient(x,l) :
    for i in range(len(l)): #on parcourt l
        if x==l[i] : #si x est le ième élément
            return True #on l'a trouvé
    return False

def somme3carres() :
    count=0  #compteur servant à compter les entier s'écrivant comme somme de 3 carrés
    carres = [i**2 for i in range(101)]  #liste des carrées des entiers de 1 à 100 inclus
    somme3 = []  #liste destinée à stocker les nombres s'écrivant comme somme de 3 carrés
    for i in range(101): #on teste tous les candidats possibles pour le premier carré
        for j in range(i, 101): #pour le deuxième
            for k in range(j,101): #pour le troisième
                if carres[i]+carres[j]+carres[k] <=10000 and not appartient(carres[i]+carres[j]+carres[k],somme3): #teste si la somme est <=10000 et si l'entier a déjà été ajouté à la liste somme3
                    count=count+1 #on en a trouvé un de plus
                    somme3.append(carres[i]+carres[j]+carres[k]) #on l'ajoute à la liste
    return 10000-count #on veut les entiers qui ne s'écrivent pas comme 3 carrés

print(somme3carres())

"""
Question 43 : Le jardin des Hespérides
"""
def Hesperide(N):
    S=0   #Somme des pommes que l'on remporte
    for i in range(1,N+1): #on parcourt tous les étages (pas d'étage 0)
        if i**2%3==0: #teste si le nombre de pommes de l'étage est multiple de 3
            S=S+i**2
    return S

print(Hesperide(50))

def Hesperide2(N):
    S=0   #Somme des pommes que l'on remporte
    for i in range(1,N+1): #on parcourt tous les étages (pas d'étage 0)
        if i%3==0: #teste si le numéro de l'étage et donc son carré sont multiples de 3
            S=S+i**2
    return S

print(Hesperide2(50))