Las respuestas ya presentes son buenas y las ampliaré un poco.
Como Benjamin sugirió, las sumas acumulativas se usan típicamente en este tipo de problema:
+------------------------+
| fruit | weight | csum |
+------------------------+
| apple | 4 | 4 |
| orange | 2 | 6 |
| lemon | 1 | 7 |
+------------------------+
Para encontrar un elemento en esta estructura, puede usar algo como el código de Nevermind. Este fragmento de código C # que suelo usar:
double r = Random.Next() * totalSum;
for(int i = 0; i < fruit.Count; i++)
{
if (csum[i] > r)
return fruit[i];
}
Ahora a la parte interesante. ¿Qué tan eficiente es este enfoque y cuál es la solución más eficiente? Mi código requiere memoria O (n) y se ejecuta en tiempo O (n) . No creo que se pueda hacer con menos de O (n) espacio, pero la complejidad del tiempo puede ser mucho menor, de hecho O (log n) . El truco consiste en utilizar la búsqueda binaria en lugar del ciclo regular for.
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];
}
También hay una historia sobre la actualización de pesos. En el peor de los casos, la actualización de peso para un elemento provoca la actualización de sumas acumulativas para todos los elementos, lo que aumenta la complejidad de la actualización a O (n) . Eso también se puede reducir a O (log n) usando un árbol indexado binario .