#########################################
######## TP informatique bio-spé ########
########        2025-2026        ########
#########################################


#########################################
####### Thème 12 : VAR discrètes ########
#########################################

import random as rd
import numpy as np

## Q1 : estimation d'une probabilité

# a) proba d'obtenir 'Pile'
def probaPile(N) :
    compteur = 0
    piece = 'pf'
    for _ in range(N) :
        compteur += rd.choice(piece) == 'p'
    return compteur / N

# b) proba d'obtenir 2 avec un dé
def probaDeux(N) :
    compteur = 0
    for _ in range(N) :
        compteur += rd.randint(1,6) == 2
    return compteur / N

# c) proba d'obtenir 8 avec deux dé
def probaHuit(N) :
    compteur = 0
    for _ in range(N) :
        compteur += rd.randint(1,6) + rd.randint(1,6) == 8
    return compteur / N

# d) proba d'obtenir 1 dans une urne de 10 boules
def probaUn(N) :
    compteur = 0
    for _ in range(N) :
        compteur += rd.randint(1,10) == 1
    return compteur / N

# e) proba d'obtenir au moins un 10 en 5 tirages
def proba10(N) :
    compteur = 0
    for _ in range(N) :
        succes = False
        for _ in range(5) :
            if rd.randint(1,10) == 10 :
                succes = True
        compteur += succes
    return compteur / N

# f) proba d'obtenir exactement deux 3 en 5 tirages
def probaDouble3(N) :
    compteur = 0
    for _ in range(N) :
        trois = 0 # :-)
        for _ in range(5) :
            trois += rd.randint(1,10) == 3
        compteur += trois == 2 # :-)
    return compteur / N

# g) proba de tirer au moins 5 fois pour avoir 10
def proba10en5essais(N) :
    compteur = 0
    for _ in range(N) :
        tirage = 1
        while rd.randint(1,10) != 10 :
            tirage += 1
        compteur += tirage >= 5
    return compteur / N

# h) proba lors de tirages sans remise
def proba10sansRemise(N) :
    compteur = 0
    for _ in range(N) :
        urne = [k for k in range(1,11)]
        pioche = []
        for _ in range(5) :
            i = rd.randint(0,len(urne)-1)
            pioche.append(urne.pop(i))
        compteur += 10 in pioche
    return compteur / N

# i) proba de tirages croissants
def probaCroissant(N) :
    compteur = 0
    for _ in range(N) :
        urne = [k for k in range(1,11)]
        pioche = []
        for _ in range(5) :
            i = rd.randint(0,len(urne)-1)
            pioche.append(urne.pop(i))
        croit = True
        for i in range(4) :
            if pioche[i] > pioche[i+1] :
                croit = False
        compteur += croit
    return compteur / N

## Q2 : urne grandissante
def pasBlanc(c,s,N) :
    compteur = 0
    for _ in range(N) :
        b,n = 2,2
        tirages = 1
        while tirages <= s :
            boule = rd.randint(1,b+n)
            if boule <= b :
                compteur += 1
                tirages = s+1
            else :
                n += c
                tirages += 1
    return 1-compteur / N

## Q3 : la brebis galeuse
def brebis(n) :
    lancers = 1
    while sum([rd.randint(0,1) for _ in range(n)]) not in [1,n-1] :
        lancers += 1
    return lancers

## Q4 : simulation d'une loi de Bernoulli
def Bernoulli(p) :
    return rd.random() < p

## Q5 : nombre de tirages pour voir 2 couleurs
def X(p) :
    tirages = 0
    L = [0,0]
    while L[0]*L[1] == 0 :
        boule = int(rd.random()<p)
        L[boule] += 1
        tirages += 1
    return tirages

## Q6 : simulation d'une loi binomiale
def binome(n,p) :
    succes = 0
    for _ in range(n) :
        succes += rd.random() < p
    return succes

## Q7 : simulation de 30 tirages
def nblanches() :
    return binome(30,5/8)

## Q8 : rang d'apparition du premier 'Pile'
def G() :
    rang = 1
    while rd.randint(0,1) == 0 :
        rang += 1
    return rang

## Q9 à Q12 : simulation de VARD quelconque

# soit r un réel dans [0,1[. L'unique entier k
# tel que r appartient à Ik est le plus petit
# entier k tel que r < beta_k.

# et beta_k est la somme pour k variant de 0 à k des p_j

def sumcum(L) :
    S = [L[0]]
    for i in range(1,len(L)) :
        S.append(L[i]+S[-1])
    return S

def simul(X,P) :
    r = rd.random()
    L = sumcum(P)
    k = 0
    while L[k] < r :
        k += 1
    return X[k]

## Q13 : un exemple de loi quelconque

X = [0,1,2]
P = [1/2,1/3,1/6]
print(simul(X,P))

## Q14 : avec une loi de Poisson

# N suit une loi de Poisson de paramètre 15
# X suit une loi binomiale de paramètres N,1/3

def Poisson(mu) :
    r = rd.random()
    a = np.exp(-mu)
    S = a
    k = 0
    while r > S :
        k += 1
        a *= mu/k
        S += a
    return k

def morsures() :
    mu = 15
    p = 1/3
    N = Poisson(mu)
    return binome(N,p)

# rq : on peut démontrer que morsures suit
# une loi de Poisson de paramètre mu*p = 5.