O embaralhamento que não é
O bug de randomização mais comum em JavaScript:
const shuffled = items.sort(() => Math.random() - 0.5); // ERRADO
Parece correto e está em todo lugar. Não é um embaralhamento. Comparadores de sort devem ser consistentes, e este não é, então o algoritmo de ordenação do motor produz permutações enviesadas.
Medido em quatro opções de quiz:
| Posição | Chance de a resposta correta cair ali |
|---|---|
| 0 | 28,3% |
| 1 | 28,2% |
| 2 | 24,7% |
| 3 | 18,8% |
Uniforme seria 25% cada. Na prática, sempre chutar a primeira opção superava chutar aleatoriamente — um jogador que percebesse teria vantagem, e o quiz era discretamente injusto.
A correção
Fisher–Yates, uma vez, em um helper:
function shuffle(a) {
const out = [...a];
for (let i = out.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[out[i], out[j]] = [out[j], out[i]];
}
return out;
}
Depois substitua em todos os lugares. O bug geralmente aparece mais de uma vez — um código-fonte o tinha em onze lugares: embaralhando opções de quiz, escolhendo quais perguntas apareciam, selecionando quais itens eram oferecidos, ordenando opções de menu. Cada um enviesava algo ligeiramente.
Não substitua cegamente por correspondência de padrão. (Math.random() - 0.5) também aparece legitimamente como ruído em física e jitter:
velocity += (Math.random() - 0.5) * 0.012; // deixe isso em paz
Verifique se cada ocorrência é realmente um comparador de sort.
Teste a distribuição
Não confie na intuição para avaliar aleatoriedade. Conte:
const pos = [0,0,0,0], N = 200000;
for (let i = 0; i < N; i++) {
const order = shuffle(['a','b','c','d'].map((t,i) => [t,i]));
pos[order.findIndex(o => o[1] === 0)]++;
}
pos.forEach((c,i) => console.log(i, (c/N*100).toFixed(1) + '%'));
Um desvio acima de cerca de 3 pontos percentuais entre as posições indica algo viciado.
Use um gerador adequado para o teste. Um gerador congruencial linear artesanal produziu "erros" de 1–2 pontos que pareciam um bug real, mas eram culpa do próprio teste. Math.random() é uniforme o suficiente para verificar distribuições — guarde o gerador customizado para quando precisar de sementes reproduzíveis e valide-o separadamente.
Verifique as probabilidades que você mostra às pessoas
Se você exibe probabilidades em algum lugar — uma porcentagem, uma barra de progresso, uma chance de sucesso — certifique-se de que elas correspondem ao que o código realmente sorteia. Simule o sorteio real algumas centenas de milhares de vezes e compare:
cred 0 mostrado 5,0%/40,0%/55,0% real 5,0%/40,0%/55,0%
cred 100 mostrado 40,0%/40,0%/20,0% real 40,0%/40,1%/20,0%
Erro máximo abaixo de meio ponto percentual significa que a exibição não está mentindo. Uma probabilidade mostrada que o resultado não honra é pior do que não mostrar nada, porque as pessoas tomam decisões baseadas nela.
Enquanto estiver nisso
Verifique se uma meta declarada é realmente alcançável. Um contador marcando "3 de 5" diz aos jogadores que 5 é o objetivo; se 5 ocorre 0,17% das vezes, a interface está prometendo algo que efetivamente nunca acontece. Calcule as probabilidades reais de cada marco que você exibe.