De mi comentario original: Esto está estrechamente relacionado con una cantidad ubicua en la evaluación de la productividad académica, el índice Hirsh, mejor conocido como el índice h . En resumen, se define como el número de publicaciones uno tiene de tal manera que cada una de ellas tiene al menos h citas (la mayor h ).hhh
La única forma en que su problema difiere es que le interesaría no solo cuántas publicaciones satisfacen el criterio sino también cuáles son sus recuentos de citas , sino que es una modificación trivial. Los datos ya están allí, el algoritmo original simplemente lo deja caer.
El cálculo generalmente implementado es bastante sencillo y está de acuerdo con la respuesta de Karolis Juodelė .
Actualización: Dependiendo del tamaño y el carácter de sus datos, puede valer la pena explorar métodos que ordenen parcialmente la matriz filtrando los datos por encima y por debajo de un punto crucial (me viene a la mente la clasificación rápida). Luego, dependiendo de si hay muy poco o demasiados, ajuste el pivote y rehaga el subconjunto que lo contiene, y así sucesivamente. No necesita un orden entre elementos superiores a , y ciertamente no entre aquellos inferiores a eso. Entonces, por ejemplo, una vez que encuentre todos los elementos mayores o iguales a h 1 y haya menos de h 1 de ellos, no necesita tocar ese subconjunto nuevamente, solo agréguelo. Esto convierte la recursividad inherente a quicksort en una recursión de cola y, por lo tanto, puede reescribirse como un bucle.hh1h1
Mi Haskell está un poco oxidado, pero esto debería hacer lo que describí anteriormente y parece funcionar. Espero que se pueda entender hasta cierto punto, me complace proporcionar una explicación más detallada.
-- just a utility function
merge :: [a] -> [a] -> [a]
merge [] ys = ys
merge (x:xs) ys = x : merge xs ys
-- the actual implementation
topImpl :: [Int] -> [Int] -> [Int]
topImpl [] granted = granted
topImpl (x:xs) granted
| x == (1 + lGreater + lGranted) = x : merge greater granted
| x > (1 + lGreater + lGranted) = topImpl smaller (x : merge greater granted)
| otherwise = topImpl greater granted
where smaller = [y | y <- xs, y < x]
greater = [y | y <- xs, y >= x]
lGreater = length greater
lGranted = length granted
-- starting point is: top of whole array, granted is empty
top :: [Int] -> [Int]
top arr = topImpl arr []
La idea es recopilar granted
lo que sabe que definitivamente participará en el resultado, y no ordenarlo más. Si greater
junto con los x
ajustes, tenemos suerte, de lo contrario, debemos intentar con un subconjunto más pequeño. (El pivote x
es simplemente lo que pasó a ser el primer elemento de la sublista que se considera actualmente). Tenga en cuenta que la ventaja significativa de tomar elementos más grandes uno por uno es que hacemos esto en bloques de tamaño promedio y no es necesario ordenarlos más.remaining/2
Ejemplo:
Tomemos tu set [1,3,4,1,3,6]
.
x = 1
, granted = []
, greater = [3,4,1,3,6]
. Ay, llegamos a un caso patológico cuando el pivote es demasiado pequeño (en realidad tan pequeño que smaller
está vacío) justo en el primer paso. Afortunadamente nuestro algo está listo para eso. Se descarta x
e intenta nuevamente con greater
solo.
x = 3
, granted = []
, greater = [4,3,6]
. Juntos, forman una matriz de longitud 4, pero solo tenemos eso limitado desde abajo por 3, así que eso es demasiado. Repita greater
solo.
x = 4
, granted = []
, greater = [6]
. Esto da una matriz de 2 elementos ≥ 4 cada uno, parece que podríamos haber usado algunos más. Mantenga esto y repita smaller = [3]
.
x = 3
, granted = [4,6]
, greater = []
. Esto en conjunto proporciona una matriz de 3 elementos ≥ 3 cada uno, por lo que tenemos nuestra solución [3,4,6]
y podemos regresar. (Tenga en cuenta que la permutación puede variar según el orden de la entrada, pero siempre contendrá los términos más altos posibles, nunca [3,3,6]
o [3,3,4]
para su ejemplo).
(Por cierto, tenga en cuenta que la recursión de hecho simplemente colapsó en un ciclo). La complejidad es algo mejor que la clasificación rápida debido a las muchas comparaciones guardadas:
n−1
Caso promedio: O(logn)O(n)
nO(n2)
Hay algunas comparaciones innecesarias en el código anterior, como calcular smaller
si lo necesitamos o no, se pueden eliminar fácilmente. (Creo que la evaluación perezosa se encargará de eso).