def undercut_gain(c: int, r: float = 2.0) -> float:
"""Gain a devier de c vers c-1 quand l'autre tient c : u1(c-1,c) - u1(c,c).
Independe de c : vaut (c-1+r) - c = r - 1. C'est le moteur de la spirale.
"""
return u1(c - 1, c, r) - u1(c, c, r)
def elimination_reaches_floor(r: float = 2.0, c_max: int = C_MAX) -> bool:
"""L'elimination de haut en bas atteint-elle le plancher pour ce bonus r ?
Reutilise le raisonnement de la cellule 3 (domination FAIBLE de la plus
haute restante par son precedesseur), en propageant r partout.
"""
def weakly_dominated_by_prev(x, remaining):
if x - 1 not in remaining:
return False
weakly = all(u1(x - 1, y, r, c_max) >= u1(x, y, r, c_max) for y in remaining)
strictly = any(u1(x - 1, y, r, c_max) > u1(x, y, r, c_max) for y in remaining)
return weakly and strictly
remaining = list(range(C_MIN, c_max + 1))
while True:
candidates = [x for x in remaining
if x > C_MIN and weakly_dominated_by_prev(x, remaining)]
if not candidates:
break
remaining.remove(max(candidates))
return remaining == [C_MIN]
# Le moteur du paradoxe : le gain de sous-cotage r-1, et le seuil r*=1.
rs = [0.5, 1.0, 1.5, 2.0, 5.0, 10.0]
print(" r | gain de sous-cotage (r-1) | la spirale atteint (2,2) ?")
for r in rs:
g = undercut_gain(50, r)
ok = elimination_reaches_floor(r)
print(f"{r:>4} | {g:>8} | {'OUI' if ok else 'NON'}")
print()
# Cas a part r = 1 : meilleure reponse multi-valuee + equilibres diagonaux
def best_response_set(y: int, r: float, c_max: int = C_MAX) -> list[int]:
"""TOUTES les meilleures reponses a y (pas seulement le plus petit argmax)."""
vals = {x: u1(x, y, r, c_max) for x in range(C_MIN, c_max + 1)}
top = max(vals.values())
return [x for x, v in sorted(vals.items()) if v == top]
def is_nash_diagonal(y: int, r: float) -> bool:
"""(y, y) est-il un equilibre de Nash ? Aucun des deux ne gagne a devier."""
me = u1(y, y, r)
return all(u1(x, y, r) <= me for x in range(C_MIN, C_MAX + 1))
for y in [100, 55, 3, 2]:
print(f"r=1 :: BR({y}) = {best_response_set(y, 1.0)}")
diag_r1 = [y for y in range(C_MIN, C_MAX + 1) if is_nash_diagonal(y, 1.0)]
diag_r2 = [y for y in range(C_MIN, C_MAX + 1) if is_nash_diagonal(y, 2.0)]
print(f"r=1 :: equilibres de Nash diagonaux : {len(diag_r1)} au total, de {diag_r1[0]} a {diag_r1[-1]}")
print(f"r=2 :: equilibres de Nash diagonaux : {diag_r2}")
print()
print("Lecture : le gain de sous-cotage vaut r-1, independant du point de depart c.")
print(" -> r < 1 : sous-coter ne rapporte rien (gain negatif) -> grimper n'est pas puni,")
print(" la spirale ne demarre pas -> le paradoxe est VIVANT (humains jouent haut).")
print(" -> r = 1 : egalites partout -- BR multi-valuee {y-1, y}, chaque (y,y) est un")
print(" equilibre de Nash ; l'elimination faible n'atteint le plancher que")
print(" par convention d'ordre. PAS d'equilibre unique.")
print(" -> r > 1 : sous-coter devient strictement rentable -> la spirale descend")
print(" forcement vers (2,2), unique equilibre -> le paradoxe SE DISSOUT.")