As respostas já presentes são boas e eu as expandirei um pouco.
Como Benjamin sugeriu, somas cumulativas são normalmente usadas neste tipo de problema:
+------------------------+
| fruit | weight | csum |
+------------------------+
| apple | 4 | 4 |
| orange | 2 | 6 |
| lemon | 1 | 7 |
+------------------------+
Para encontrar um item nessa estrutura, você pode usar algo como o pedaço de código do Nevermind. Este pedaço de código C # que eu costumo usar:
double r = Random.Next() * totalSum;
for(int i = 0; i < fruit.Count; i++)
{
if (csum[i] > r)
return fruit[i];
}
Agora para a parte interessante. Qual é a eficiência dessa abordagem e qual é a solução mais eficiente? Meu pedaço de código requer O (n) de memória e é executado em O (n) tempo. Eu não acho que isso possa ser feito com menos de O (n) espaço, mas a complexidade do tempo pode ser muito menor, O (log n) na verdade. O truque é usar a pesquisa binária em vez do loop for regular.
double r = Random.Next() * totalSum;
int lowGuess = 0;
int highGuess = fruit.Count - 1;
while (highGuess >= lowGuess)
{
int guess = (lowGuess + highGuess) / 2;
if ( csum[guess] < r)
lowGuess = guess + 1;
else if ( csum[guess] - weight[guess] > r)
highGuess = guess - 1;
else
return fruit[guess];
}
Há também uma história sobre a atualização de pesos. Na pior das hipóteses, a atualização do peso para um elemento causa a atualização de somas cumulativas para todos os elementos, aumentando a complexidade da atualização para O (n) . Isso também pode ser reduzido para O (log n) usando a árvore indexada binária .