#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
from scipy.special import binom 

# valeurs trouvées par le calcul littéral

def a(n):
    return (n+1)/(2**(n+1)-1)

def E(n):
    return (2**n*(n-1)+1)/(2**(n+1)-1)

def Ebis(n):
    # valeur calculée pour E[X(X+1)]
    return 2**(n-1)*n*a(n)

def Var(n):
    a_th = a(n)
    return 2**(n-1)*(n+2)*a_th - a_th**2*4**n

# fonction de test

def test(f, n_max=10):
    for k in range(2, n_max):
        print(f(k))

# valeurs numériques 

def loi(n, verifier=False):
    p = np.zeros(n+1)
    for k in range(n+1):
        p[k] = binom(n, k)/(k+1)
    a_num = 1/np.sum(p)
    if verifier:
        print(a_num==a(n))
    return a_num*p

# A.N. : vérifié pour 2≤n≤10
print("Vérification du calcul de a")
test(lambda n: loi(n, True))

def esperance(n):
    E_num = np.sum([k*pk for (k,pk) in enumerate(loi(n))])
    E_th  = E(n)
    return 100*(E_th-E_num)/E_num

# A.N. : vérifié pour 2≤n≤10
print("\nVérification du calcul de l'espérance")
test(esperance)

def variance(n):
    V_th  = Var(n)
    E_XX  = np.sum([k**2*pk for (k,pk) in enumerate(loi(n))])
    E_X   = E(n) # puisque la formule littérale a été vérifiée ! 
    V_num = E_XX - E_X**2
    return 100*(V_th-V_num)/V_num

# A.N. : vérifié pour 2≤n≤10
print("\nVérification du calcul de la variance")
test(variance)
