"""Estatística do benchmark: médias, IC bootstrap, confronto direto e Elo.""" from __future__ import annotations import math def media(valores: list[float]) -> float: return sum(valores) / len(valores) if valores else 0.0 def desvio(valores: list[float]) -> float: if len(valores) < 2: return 0.0 m = media(valores) return math.sqrt(sum((v - m) ** 2 for v in valores) / (len(valores) - 1)) def _lcg(seed: int): estado = seed & ((1 << 64) - 1) while True: estado = (estado * 6364136223846793005 + 1442695040888963407) % (1 << 64) yield estado / float(1 << 64) def ic_bootstrap(valores: list[float], seed: int = 1, n: int = 1000, conf: float = 0.95) -> tuple[float, float]: """Intervalo de confiança por bootstrap (percentil).""" if not valores: return (0.0, 0.0) if len(valores) == 1: return (valores[0], valores[0]) rng = _lcg(seed) k = len(valores) amostras = [] for _ in range(n): acc = 0.0 for _ in range(k): acc += valores[int(next(rng) * k) % k] amostras.append(acc / k) amostras.sort() cauda = (1 - conf) / 2 return (amostras[int(cauda * n)], amostras[min(n - 1, int((1 - cauda) * n))]) def empate_tecnico(a: tuple[float, float], b: tuple[float, float]) -> bool: """Diferença menor que a média das meias-amplitudes dos intervalos.""" ha = (a[1] - a[0]) / 2 hb = (b[1] - b[0]) / 2 return abs(media(a) - media(b)) < (ha + hb) / 2 def bradley_terry(vitorias: dict[tuple[str, str], int], iteracoes: int = 200) -> dict[str, float]: """Ajusta forças comparativas (0..1) a partir da matriz de confrontos.""" jogadores = sorted({p for par in vitorias for p in par}) if not jogadores: return {} forca = {j: 1.0 for j in jogadores} for _ in range(iteracoes): novo = {} for j in jogadores: num = 0.0 den = 0.0 for k in jogadores: if k == j: continue w = vitorias.get((j, k), 0) l = vitorias.get((k, j), 0) if w + l == 0: continue num += w den += (w + l) / (forca[j] + forca[k]) novo[j] = (num / den) if den > 0 else forca[j] soma = sum(novo.values()) or 1.0 forca = {j: v / soma for j, v in novo.items()} return forca def elo_de_forca(forca: dict[str, float], base: float = 1500.0, amplitude: float = 400.0) -> dict[str, float]: """Converte força BT em escala Elo aproximada.""" if not forca: return {} valores = sorted(forca.values()) minimo, maximo = valores[0], valores[-1] if maximo - minimo < 1e-12: return {j: base for j in forca} return { j: base + amplitude * (2 * (v - minimo) / (maximo - minimo) - 1) for j, v in forca.items() } def ngramas(texto: str, n: int = 8) -> set[tuple]: palavras = texto.lower().split() if len(palavras) < n: return {tuple(palavras)} if palavras else set() return {tuple(palavras[i:i + n]) for i in range(len(palavras) - n + 1)} def sobreposicao_ngramas(a: str, b: str, n: int = 8) -> float: """Sobreposição de Jaccard entre n-gramas (detecção de contaminação).""" ga, gb = ngramas(a, n), ngramas(b, n) if not ga or not gb: return 0.0 return len(ga & gb) / len(ga | gb)