
# -*- coding: utf-8 -*-
"""
Correction des exercices sans préparation — BCPST 2A 2025/2026
Oral Agro/Véto et G2E

Support de démonstration à exécuter dans Spyder pendant le cours.

Astuce Spyder : le fichier est découpé en cellules délimitées par « # %% ».
  - Ctrl + Entrée  : exécuter la cellule courante (idéal pour dérouler une question)
  - F5             : exécuter tout le fichier
Les versions 2 (plus astucieuses) sont laissées en commentaire sous chaque fonction.
"""

import random as rd


# %% ===================== Exercice 1 =====================

# 1) est_mixte : True si la liste contient à la fois des 0 et des 1.
def est_mixte(L):
    for k in range(1, len(L)):
        if L[k] != L[0]:
            return True
    return False

# Version 2 (liste de 0/1) :
#   def est_mixte(L):
#       return 0 < sum(L) < len(L)

print("--- Exercice 1.1 : est_mixte ---")
print("est_mixte([0, 0, 0]) =", est_mixte([0, 0, 0]))        # False (que des 0)
print("est_mixte([1, 1, 1]) =", est_mixte([1, 1, 1]))        # False (que des 1)
print("est_mixte([0, 1, 1, 0]) =", est_mixte([0, 1, 1, 0]))  # True
print("est_mixte([]) =", est_mixte([]))                      # False (liste vide)
print()


# 2) plus_longue_suite : longueur de la plus longue suite de 1.
def plus_longue_suite(L):
    c = 0
    m = 0
    for x in L:
        if x == 1:
            c += 1
        elif x == 0 and c > 0:
            m = max(m, c)
            c = 0
    m = max(m, c)            # cas où la liste se termine par une suite de 1
    return m

print("--- Exercice 1.2 : plus_longue_suite ---")
print("[0,1,1,1,0,1,1,0,0,0,0] ->", plus_longue_suite([0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0]))  # 3
print("[0,0,1,1,1] ->", plus_longue_suite([0, 0, 1, 1, 1]))   # 3 (suite en fin de liste)
print("[0,0,0] ->", plus_longue_suite([0, 0, 0]))             # 0
print()


# %% ===================== Exercice 2 =====================

# 1) tester_H : tous les éléments sont des entiers de [0, n-1].
def tester_H(L):
    for x in L:
        if type(x) != int or not (0 <= x < len(L)):
            return False
    return True

print("--- Exercice 2.1 : tester_H ---")
print("[0, 2, 1, 3] ->", tester_H([0, 2, 1, 3]))      # True
print("[0, 4, 1, 2] ->", tester_H([0, 4, 1, 2]))      # False (4 hors de [0, 3])
print("[0, 1.5, 2, 3] ->", tester_H([0, 1.5, 2, 3]))  # False (1.5 n'est pas un entier)
print()


# 2) tester_permutation : L (vérifiant H) est une permutation de [0, n-1].
def tester_permutation(L):
    if not tester_H(L):
        return False
    for k in range(len(L)):
        if L[k] in L[:k]:
            return False
    return True

# Version 2 :
#   def tester_permutation(L):
#       return tester_H(L) and len(set(L)) == len(L)

print("--- Exercice 2.2 : tester_permutation ---")
print("[2, 0, 3, 1] ->", tester_permutation([2, 0, 3, 1]))  # True
print("[0, 0, 1, 2] ->", tester_permutation([0, 0, 1, 2]))  # False (doublon)
print("[0, 1, 5] ->", tester_permutation([0, 1, 5]))        # False (echoue deja sur H)
print()


# 3) Bonus : permutation reciproque.
def inverse(L):
    n = len(L)
    M = [0] * n
    for i in range(n):
        M[L[i]] = i
    return M

print("--- Exercice 2.3 : inverse ---")
L = [2, 0, 3, 1]
print("inverse([2, 0, 3, 1]) =", inverse(L))                 # [1, 3, 0, 2]
print("inverse(inverse(L)) == L :", inverse(inverse(L)) == L)  # True (verification)
print()


# %% ===================== Exercice 3 =====================

# 1) paquet : 3 entiers distincts au hasard entre 0 inclus et 100 exclu.
def paquet():
    S = []
    V = [k for k in range(100)]
    for i in range(3):
        a = rd.choice(V)
        V.remove(a)
        S.append(a)
    return S

# Version 2 :
#   def paquet():
#       return rd.sample(range(100), 3)

print("--- Exercice 3.1 : paquet ---")
print("paquet() =", paquet())   # ex. [12, 87, 3] (aleatoire, 3 valeurs distinctes)
print("paquet() =", paquet())
print()


# 2) coller : remplit l'album aux positions du paquet (modification en place).
def coller(paquet, album):
    for x in paquet:
        album[x] = True

print("--- Exercice 3.2 : coller ---")
album = [False] * 10
coller([2, 5, 6], album)
print("album apres coller([2, 5, 6]) :", album)   # True en positions 2, 5 et 6
print()


# %% --------- Exercice 3 : bonus (simulation) ---------
# Nombre moyen de paquets a acheter pour remplir un album de 100 vignettes.
# (Probleme du collectionneur ; le resultat tourne autour de 172.)

N = 2000          # augmenter N pour une estimation plus precise (et plus lente)
c = 0
for k in range(N):
    album = [False] * 100
    while not all(album):
        c += 1
        coller(paquet(), album)

print("--- Exercice 3.3 : estimation ---")
print("Nombre moyen de paquets (N =", N, ") :", c / N)
print()


# %% ===================== Exercice 4 =====================

# Chaines ne contenant que les lettres de Alphabet.
Alphabet = " abcdefghijklmnopqrstuvwxyz"   


# 1) rang : position d'un caractere dans Alphabet.
def rang(caractere):
    for i in range(len(Alphabet)):
        if Alphabet[i] == caractere:
            return i

# Version 2 :
#   def rang(caractere):
#       return Alphabet.index(caractere)

print("--- Exercice 4.1 : rang ---")
print("rang('a'), rang('c'), rang('z') =", rang('a'), rang('c'), rang('z'))   # 0 2 25
print()


# 2) memes_caracteres : les deux chaines ont le meme ensemble de caracteres.
def memes_caracteres(s1, s2):
    for c in s1:
        if c not in s2:
            return False
    for c in s2:
        if c not in s1:
            return False
    return True

# Version 2 :
#   def memes_caracteres(s1, s2):
#       return set(s1) == set(s2)

print("--- Exercice 4.2 : memes_caracteres ---")
print('"abba" et "ab"  ->', memes_caracteres("abba", "ab"))   # True (memes caracteres)
print('"abc" et "abd"  ->', memes_caracteres("abc", "abd"))   # False
print()


# 3) anagrammes : memes caracteres avec les memes occurrences.
def anagrammes(s1, s2):
    compte1 = [0] * len(Alphabet)
    compte2 = [0] * len(Alphabet)
    for c in s1:
        compte1[rang(c)] += 1
    for c in s2:
        compte2[rang(c)] += 1
    return compte1 == compte2

# Version 2 :
#   def anagrammes(s1, s2):
#       return sorted(s1) == sorted(s2)

print("--- Exercice 4.3 : anagrammes ---")
print('"chien" et "niche" ->', anagrammes("chien", "niche"))  # True
print('"abba" et "ab"     ->', anagrammes("abba", "ab"))      # False (memes lettres, pas memes nombres)