Creo que hay una manera de encontrar el elemento kth más grande en una matriz sin clasificar de longitud n en O (n). O tal vez es "esperado" O (n) o algo así. ¿Cómo podemos hacer esto?
performance
algorithm
big-o
MrDatabase
fuente
fuente
Respuestas:
Esto se llama encontrar la estadística de orden k . Hay un algoritmo aleatorio muy simple (llamado selección rápida ) que toma el
O(n)
tiempo promedio, elO(n^2)
peor de los casos, y un algoritmo no aleatorio bastante complicado (llamado introselect ) que toma elO(n)
peor de los casos. Hay información en Wikipedia , pero no es muy buena.Todo lo que necesita está en estas diapositivas de PowerPoint. Solo para extraer el algoritmo básico del algoritmo delO(n)
peor de los casos (introselect):También está muy bien detallado en el libro Introducción a los algoritmos de Cormen et al.
fuente
Si desea un
O(n)
algoritmo verdadero , en lugar deO(kn)
o algo así, entonces debe usar la selección rápida (es básicamente un ordenamiento rápido donde arroja la partición que no le interesa). Mi profesor tiene una gran crítica, con el análisis de tiempo de ejecución: ( referencia )El algoritmo QuickSelect encuentra rápidamente el k-ésimo elemento más pequeño de una matriz de
n
elementos sin clasificar . Es un algoritmo aleatorio , por lo que calculamos el peor tiempo de ejecución esperado .Aquí está el algoritmo.
¿Cuál es el tiempo de ejecución de este algoritmo? Si el adversario lanza monedas por nosotros, podemos encontrar que el pivote es siempre el elemento más grande y
k
siempre es 1, dando un tiempo de ejecución dePero si las opciones son realmente aleatorias, el tiempo de ejecución esperado viene dado por
donde estamos haciendo la suposición no totalmente razonable de que la recursión siempre aterriza en el mayor de
A1
oA2
.Supongamos que
T(n) <= an
para algunosa
. Entonces tenemosy ahora de alguna manera tenemos que obtener la horrenda suma a la derecha del signo más para absorber el
cn
de la izquierda. Si lo limitamos como , nos ponemos más o menos . Pero esto es demasiado grande: no hay espacio para exprimir un extra . Entonces, expandamos la suma usando la fórmula de la serie aritmética:2(1/n) ∑i=n/2 to n an
2(1/n)(n/2)an = an
cn
donde aprovechamos que n es "suficientemente grande" para reemplazar los
floor(n/2)
factores feos con el mucho más limpio (y más pequeño)n/4
. Ahora podemos continuar conproporcionado
a > 16c
.Esto da
T(n) = O(n)
. Está claroOmega(n)
, así que lo tenemosT(n) = Theta(n)
.fuente
k > length(A) - length(A2)
?A
enA1
yA2
alrededor del pivote, lo sabemoslength(A) == length(A1)+length(A2)+1
. Entonces,k > length(A)-length(A2)
es equivalente ak > length(A1)+1
, lo cual es cierto cuandok
está en algún lugarA2
.Un rápido Google sobre eso ('kth mayor elemento de matriz') devolvió esto: http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17
(fue específicamente para 3d más grande)
y esta respuesta:
fuente
Te gusta la clasificación rápida. Elija un elemento al azar y empuje todo más alto o más bajo. En este punto, sabrá en qué elemento eligió realmente, y si es el elemento kth lo que ha hecho, de lo contrario repita con el bin (más alto o más bajo), en el que el elemento kth caería. Estadísticamente hablando, el tiempo se necesita para encontrar el elemento kth crece con n, O (n).
fuente
El análisis de algoritmo que acompaña a un programador proporciona una versión que es O (n), aunque el autor afirma que el factor constante es tan alto, que probablemente preferiría el método ingenuo de ordenar la lista y luego seleccionar.
Respondí la carta de tu pregunta :)
fuente
La biblioteca estándar de C ++ tiene casi exactamente esa llamada de función
nth_element
, aunque modifica sus datos. Ha esperado un tiempo de ejecución lineal, O (N), y también realiza una ordenación parcial.fuente
Aunque no está muy seguro acerca de la complejidad de O (n), pero seguramente estará entre O (n) y nLog (n). También asegúrese de estar más cerca de O (n) que nLog (n). La función está escrita en Java
fuente
Implementé encontrar kth minimimum en n elementos sin clasificar usando programación dinámica, específicamente el método de torneo. El tiempo de ejecución es O (n + klog (n)). El mecanismo utilizado se enumera como uno de los métodos en la página de Wikipedia sobre el Algoritmo de selección (como se indica en una de las publicaciones anteriores). Puede leer sobre el algoritmo y también encontrar el código (java) en la página de mi blog Finding Kth Minimum . Además, la lógica puede ordenar parcialmente la lista: devolver los primeros K min (o max) en el tiempo O (klog (n)).
Aunque el código proporcionó el resultado kth mínimo, se puede emplear una lógica similar para encontrar el kth máximo en O (klog (n)), ignorando el trabajo previo realizado para crear el árbol del torneo.
fuente
Puede hacerlo en O (n + kn) = O (n) (para k constante) para el tiempo y O (k) para el espacio, haciendo un seguimiento de los k elementos más grandes que ha visto.
Para cada elemento de la matriz, puede escanear la lista de k más grande y reemplazar el elemento más pequeño con el nuevo si es más grande.
Sin embargo, la solución de almacenamiento prioritario de Warren es más ordenada.
fuente
O(n log k)
... todavía se degenera en O (nlogn) en caso de una gran k. Sin embargo, creo que funcionaría bien para valores pequeños de k ... posiblemente más rápido que algunos de los otros algoritmos mencionados aquí [???]Selección rápida sexy en Python
fuente
a1 = [i for i in arr if i > arr[r]]
ya2 = [i for i in arr if i < arr[r]]
, devolverá el kth elemento más grande .numpy.sort
pornumpy array
osorted
para las listas), que utilizar esta aplicación manual.Encuentre la mediana de la matriz en tiempo lineal, luego use el procedimiento de partición exactamente como en el ordenamiento rápido para dividir la matriz en dos partes, los valores a la izquierda de la mediana son menores (<) que a la mediana y a la derecha mayores que (>) mediana , eso también se puede hacer en tiempo lineal, ahora, vaya a esa parte de la matriz donde se encuentra el elemento kth, ahora la recurrencia se convierte en: T (n) = T (n / 2) + cn que me da O (n) en general.
fuente
A continuación se muestra el enlace a la implementación completa con una explicación bastante extensa sobre cómo funciona el algoritmo para encontrar el elemento Kth en un algoritmo no ordenado. La idea básica es dividir la matriz como en QuickSort. Pero para evitar casos extremos (por ejemplo, cuando se elige el elemento más pequeño como pivote en cada paso, de modo que el algoritmo se degenere en O (n ^ 2) tiempo de ejecución), se aplica una selección de pivote especial, llamada algoritmo de mediana de medianas. Toda la solución se ejecuta en tiempo O (n) en el peor y en el caso promedio.
Aquí hay un enlace al artículo completo (se trata de encontrar el elemento Kth más pequeño , pero el principio es el mismo para encontrar el Kth más grande ):
Encontrar el elemento más pequeño de Kth en una matriz sin clasificar
fuente
Según este documento, Encontrar el Kth ítem más grande en una lista de n ítems, el siguiente algoritmo llevará
O(n)
tiempo en el peor de los casos.Análisis: Como se sugiere en el documento original:
¿Por qué el tamaño de partición se toma 5 y no 3?
Como se menciona en el documento original :
Ahora he intentado implementar el algoritmo anterior como:
Solo por completar, otro algoritmo hace uso de Priority Queue y lleva tiempo
O(nlogn)
.Ambos algoritmos se pueden probar como:
Como resultado esperado es:
18 18
fuente
¿Qué tal este enfoque?
Mantenga a
buffer of length k
y atmp_max
, obteniendo tmp_max es O (k) y se hace n veces así que algo comoO(kn)
¿Es correcto o me estoy perdiendo algo?
Aunque no supera el caso promedio de selección rápida y el peor caso del método de estadística mediana, es bastante fácil de entender e implementar.
fuente
iterar a través de la lista. Si el valor actual es mayor que el valor más grande almacenado, guárdelo como el valor más grande y baje el 1-4 y 5 caiga de la lista. Si no, compárelo con el número 2 y haga lo mismo. Repita, verificándolo con los 5 valores almacenados. esto debería hacerlo en O (n)
fuente
me gustaría sugerir una respuesta
si tomamos los primeros k elementos y los clasificamos en una lista vinculada de k valores
ahora para cualquier otro valor, incluso para el peor de los casos, si hacemos una ordenación por inserción para el resto de valores nk, incluso en el peor de los casos, el número de comparaciones será k * (nk) y para que los valores k anteriores se ordenen, déjelo ser k * (k- 1) entonces resulta ser (nk-k) que es o (n)
salud
fuente
La explicación del algoritmo de mediana de medianas para encontrar el k-ésimo entero más grande de n se puede encontrar aquí: http://cs.indstate.edu/~spitla/presentation.pdf
La implementación en c ++ es la siguiente:
fuente
También existe el algoritmo de selección de Wirth , que tiene una implementación más simple que QuickSelect. El algoritmo de selección de Wirth es más lento que QuickSelect, pero con algunas mejoras se vuelve más rápido.
Con más detalle. Utilizando la optimización MODIFIND de Vladimir Zabrodsky y la selección de pivote de mediana de 3 y prestando atención a los pasos finales de la parte de partición del algoritmo, se me ocurrió el siguiente algoritmo (imaginablemente llamado "LefSelect"):
En los puntos de referencia que hice aquí , LefSelect es un 20-30% más rápido que QuickSelect.
fuente
Solución Haskell:
Esto implementa la mediana de soluciones medianas utilizando el método withShape para descubrir el tamaño de una partición sin calcularla realmente.
fuente
Aquí hay una implementación en C ++ de Randomized QuickSelect. La idea es elegir aleatoriamente un elemento pivote. Para implementar una partición aleatoria, usamos una función aleatoria, rand () para generar un índice entre l y r, intercambiamos el elemento en un índice generado aleatoriamente con el último elemento y finalmente llamamos al proceso de partición estándar que usa el último elemento como pivote.
La complejidad de tiempo en el peor de los casos de la solución anterior sigue siendo O (n2). En el peor de los casos, la función aleatoria siempre puede elegir un elemento de esquina. La complejidad temporal esperada de la selección rápida aleatorizada anterior es Θ (n)
fuente
Llamar encuesta () k veces.
fuente
Esta es una implementación en Javascript.
Si libera la restricción de que no puede modificar la matriz, puede evitar el uso de memoria adicional utilizando dos índices para identificar la "partición actual" (en el estilo clásico de clasificación rápida: http://www.nczonline.net/blog/2012/ 11/27 / computer-science-in-javascript-quicksort / ).
Si desea probar cómo funciona, puede usar esta variación:
El resto del código es solo para crear un patio de recreo:
Ahora, ejecuta tus pruebas unas pocas veces. Debido a Math.random () producirá cada vez resultados diferentes:
Si lo prueba varias veces, puede ver incluso empíricamente que el número de iteraciones es, en promedio, O (n) ~ = constante * ny el valor de k no afecta el algoritmo.
fuente
Se me ocurrió este algoritmo y parece ser O (n):
Digamos k = 3 y queremos encontrar el tercer elemento más grande de la matriz. Crearía tres variables y compararía cada elemento de la matriz con el mínimo de estas tres variables. Si el elemento de matriz es mayor que nuestro mínimo, reemplazaríamos la variable min con el valor del elemento. Continuamos lo mismo hasta el final de la matriz. El mínimo de nuestras tres variables es el tercer elemento más grande de la matriz.
Y, para encontrar el elemento más grande de Kth necesitamos K variables.
Ejemplo: (k = 3)
¿Alguien puede revisar esto y decirme lo que me falta?
fuente
Aquí está la implementación del algoritmo sugerido por eladv (también puse aquí la implementación con pivote aleatorio):
fuente
es similar a la estrategia quickSort, donde elegimos un pivote arbitrario y llevamos los elementos más pequeños a su izquierda y los más grandes a la derecha
fuente
Ir al final de este enlace: ...........
http://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array-set-3-worst-case-linear-time/
fuente
Puedes encontrar el késimo elemento más pequeño en O (n) tiempo y espacio constante. Si consideramos que la matriz es solo para enteros.
El enfoque es hacer una búsqueda binaria en el rango de valores de la matriz. Si tenemos un min_value y un max_value ambos en rango entero, podemos hacer una búsqueda binaria en ese rango. Podemos escribir una función de comparación que nos dirá si algún valor es el kth-más pequeño o más pequeño que kth-más pequeño o más grande que kth-más pequeño. Haga la búsqueda binaria hasta llegar al número k-más pequeño
Aquí está el código para eso
Solución de clase:
fuente
También hay un algoritmo que supera el algoritmo de selección rápida. Se llama algoritmo Floyd-Rivets (FR) .
Artículo original: https://doi.org/10.1145/360680.360694
Versión descargable: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.309.7108&rep=rep1&type=pdf
Artículo de Wikipedia https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm
Traté de implementar el algoritmo de selección rápida y FR en C ++. También los comparé con las implementaciones estándar de la biblioteca C ++ std :: nth_element (que es básicamente un híbrido introselect de quickselect y heapselect). El resultado fue selección rápida y nth_element se ejecutó de manera comparable en promedio, pero el algoritmo FR se ejecutó aprox. dos veces más rápido en comparación con ellos.
Código de muestra que utilicé para el algoritmo FR:
fuente
Lo que haría es esto:
Simplemente puede almacenar punteros al primer y último elemento en la lista vinculada. Solo cambian cuando se realizan actualizaciones a la lista.
Actualizar:
fuente
Primero podemos construir un BST a partir de una matriz no ordenada que toma tiempo O (n) y desde el BST podemos encontrar el késimo elemento más pequeño en O (log (n)) que, en general, cuenta en un orden de O (n).
fuente