Digamos que tienes un avión y tiene poco combustible. A menos que el avión caiga 3000 libras de peso de pasajeros, no podrá llegar al próximo aeropuerto. Para salvar el máximo número de vidas, nos gustaría lanzar primero a las personas más pesadas del avión.
Y sí, hay millones de personas en el avión, y nos gustaría un algoritmo óptimo para encontrar a los pasajeros más pesados, sin necesariamente ordenar la lista completa.
Este es un problema de proxy para algo que estoy tratando de codificar en C ++. Me gustaría hacer una "clasificación parcial" en el manifiesto del pasajero por peso, pero no sé cuántos elementos voy a necesitar. Podría implementar mi propio algoritmo "partial_sort" ("partial_sort_accumulate_until"), pero me pregunto si hay alguna manera más fácil de hacerlo utilizando STL estándar.
Respuestas:
Una forma sería usar un montón mínimo (
std::priority_queue
en C ++). Así es como lo harías, suponiendo que tuvieras unaMinHeap
clase. (Sí, mi ejemplo está en C #. Creo que tienes la idea).De acuerdo con las referencias estándar, el tiempo de funcionamiento debe ser proporcional a
n log k
, donden
es el número de pasajeros yk
es el número máximo de artículos en el montón. Si suponemos que el peso de los pasajeros generalmente será de 100 libras o más, entonces es poco probable que el montón contenga más de 30 artículos en cualquier momento.El peor de los casos sería si los pasajeros se presentan en orden de menor a mayor peso. Eso requeriría que cada pasajero se agregue al montón, y cada pasajero se elimine del montón. Aún así, con un millón de pasajeros y suponiendo que el más ligero pese 100 libras, el
n log k
resultado es un número razonablemente pequeño.Si obtiene los pesos de los pasajeros al azar, el rendimiento es mucho mejor. Utilizo algo como esto para un motor de recomendación (selecciono los 200 elementos principales de una lista de varios millones). Por lo general, termino con solo 50,000 o 70,000 artículos realmente agregados al montón.
Sospecho que verás algo bastante similar: la mayoría de tus candidatos serán rechazados porque son más ligeros que la persona más ligera que ya está en el montón. Y
Peek
es unO(1)
operación.Para obtener más información sobre el rendimiento de la selección de montón y la selección rápida, consulte Cuando la teoría se encuentra con la práctica . Versión corta: si selecciona menos del 1% del número total de elementos, entonces la selección de montón es un claro ganador sobre la selección rápida. Más del 1%, luego use la selección rápida o una variante como Introselect .
fuente
std::priority_queue
Sin embargo, esto no ayudará con su problema de proxy:
Para que 1,000,000 de pasajeros bajen 3000 libras de peso, cada pasajero debe perder (3000/1000000) = 0.003 lbs por persona. Eso podría lograrse al deshacerse de la camisa o los zapatos de todos, o probablemente incluso de recortes de uñas, salvando a todos. Esto supone una recolección y eliminación eficiente antes de que la pérdida de peso necesaria aumente a medida que el avión usa más combustible.
En realidad, ya no permiten el cortaúñas a bordo, así que eso está fuera.
fuente
A continuación se muestra una implementación bastante simple de la solución directa. No creo que haya una forma más rápida que sea 100% correcta.
Esto funciona al llenar el conjunto de "personas muertas" hasta que alcanza el umbral. Una vez que se alcanza el umbral, seguimos revisando la lista de pasajeros que intentan encontrar alguno que sea más pesado que la persona muerta más ligera. Cuando encontramos uno, los agregamos a la lista y luego comenzamos a "Guardar" a las personas más ligeras de la lista hasta que no podamos guardar más.
En el peor de los casos, esto funcionará casi igual que una especie de la lista completa. Pero en el mejor de los casos (la "lista muerta" se llena correctamente con las primeras X personas) funcionará
O(n)
.fuente
total
junto acontinue;
Aparte de eso, esta es la respuesta que iba a publicar. Solución súper rápidaAsumiendo que todos los pasajeros cooperarán: use una red de clasificación paralela . (ver también esto )
Aquí hay una demostración en vivoActualización: video alternativo (saltar a 1:00)
Pidiendo pares de personas para comparar-intercambiar: no puede ser más rápido que esto.
fuente
n
procesadores no es válida.@Blastfurnace estaba en el camino correcto. Utiliza la selección rápida donde los pivotes son umbrales de peso. Cada partición divide un conjunto de personas en conjuntos y devuelve el peso total de cada conjunto de personas. Continúa rompiendo el cubo apropiado hasta que sus cubos correspondientes a las personas de mayor peso pesen más de 3000 libras, y su cubo más bajo que está en ese conjunto tiene 1 persona (es decir, no se puede dividir más).
Este algoritmo es amortizado en tiempo lineal, pero en el peor de los casos cuadrático. Creo que es el único algoritmo de tiempo lineal .
Aquí hay una solución de Python que ilustra este algoritmo:
Salida:
fuente
Suponiendo que, al igual que los pesos de las personas, tiene una buena idea de cuáles son los valores máximos y mínimos que se utilizarán en una clasificación de radix para ordenarlos en O (n). Luego, simplemente trabaje desde el extremo más pesado de la lista hacia el más ligero. Tiempo total de ejecución: O (n). Desafortunadamente, no hay una implementación de una clasificación de radix en el STL, pero es bastante sencillo de escribir.
fuente
¿Por qué no utiliza un ordenamiento rápido parcial con una regla de cancelación diferente a "ordenada"? Puede ejecutarlo y luego usar solo la mitad superior y continuar hasta que el peso dentro de esta mitad superior ya no contenga el peso que al menos debe desecharse, luego retroceda un paso en la recursión y clasifique la lista. Después de eso, puede comenzar a echar a las personas del extremo superior de esa lista ordenada.
fuente
Clasificación masiva de torneos paralelos: -
Asumiendo un estándar de tres asientos a cada lado del ailse: -
Pida a los pasajeros en el asiento de la ventana que se muevan al asiento del medio si son más pesados que la persona en el asiento de la ventana.
Pida a los pasajeros en el asiento del medio que intercambien con el pasajero en el asiento del pasillo si son más pesados.
Pídale al pasajero en el asiento del pasillo izquierdo que cambie con el pasajero en el asiento del pasillo derecho si son más pesados.
Bubble clasifica a los pasajeros en el asiento del pasillo derecho. (Toma n pasos para n filas). - solicite a los pasajeros en el asiento del pasillo derecho que intercambien con la persona que está enfrente n -1 veces.
5 Sácalos de la puerta hasta que alcances las 3000 libras.
3 pasos + n pasos más 30 pasos si tiene una carga de pasajeros realmente delgada.
Para un avión de dos pasillos: las instrucciones son más complejas, pero el rendimiento es casi el mismo.
fuente
Probablemente usaría
std::nth_element
para dividir a las 20 personas más pesadas en tiempo lineal. Luego use un método más complejo para encontrar y eliminar el más pesado de los pesados.fuente
Puede hacer un pase sobre la lista para obtener la media y la desviación estándar, luego usar eso para aproximar el número de personas que tienen que ir. Use partial_sort para generar la lista basada en ese número. Si la suposición fue baja, use parcial_sort nuevamente en el resto con una nueva suposición.
fuente
@James tiene la respuesta en los comentarios: a
std::priority_queue
si puede usar cualquier contenedor, o una combinación destd::make_heap
ystd::pop_heap
(ystd::push_heap
) si quiere usar algo como astd::vector
.fuente
Aquí hay una solución basada en almacenamiento dinámico que utiliza el módulo integrado de almacenamiento dinámico de Python. Está en Python, así que no responde la pregunta original, pero es más limpio (en mi humilde opinión) que la otra solución publicada de Python.
Si k = el número de pasajeros a lanzar y N = el número de pasajeros, entonces el mejor caso para este algoritmo es O (N) y el peor caso para este algoritmo es Nlog (N). El peor de los casos ocurre si k está cerca de N durante mucho tiempo. Aquí hay un ejemplo del peor elenco:
Sin embargo, en este caso (arrojar personas del avión (supongo que con un paracaídas)), entonces k debe ser inferior a 3000, que es << "millones de personas". Por lo tanto, el tiempo de ejecución promedio debe ser sobre Nlog (k), que es lineal para el número de personas.
fuente