Cómo encontrar el conjunto máximo de elementos

14

Tengo un problema algorítmico

TnSTaSa|S|

Por ejemplo:

  1. Si = [1, 3, 4, 1, 3, 6], entonces puede ser [3, 3, 6] o [3, 4, 6] o [4, 3, 6].STS
  2. En = [7, 5, 1, 1, 7, 4], entonces es [7, 5, 7, 4].STS

He intentado esta función recursiva.

function(T):
    if minimum(T) >= length(T): 
        return T
    else: 
        return function(T\minimum(T))

¿Hay algún algoritmo no recursivo? (No verifiqué mi algoritmo recursivo, por lo que podría tener algún defecto).

drzbir
fuente

Respuestas:

14

Ordenar T. Luego tomar elementos mientras T[i] >= i+1.

Por ejemplo sorted(T)=[6,4,3,3,1,1]. Entonces, T[0] = 6 > 1, T[1] = 4 > 2, T[2] = 3 <= 3y, por último, T[3] = 3 < 4por lo que tenemos S = [T[0], T[1], T[2]].

Karolis Juodelė
fuente
3
Esto, por supuesto, pierde la otra solución , pero parece que el OP estaba buscando cualquier solución, en lugar de todas las soluciones. {6,3,3}
Rick Decker
2
Obtiene la cantidad correcta de elementos. Sabemos que tenemos soluciones con 3 elementos pero no con 4; en este caso tenemos 4 elementos ≥ 3, por lo que sabemos que podemos elegir 3 de esos para una solución.
gnasher729
3
Agradecería un argumento de corrección.
Raphael
Creo que probablemente podría hacerlo en O (n) tiempo con una variante de introselect.
user2357112 es compatible con Monica el
8

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 grantedlo que sabe que definitivamente participará en el resultado, y no ordenarlo más. Si greaterjunto con los xajustes, tenemos suerte, de lo contrario, debemos intentar con un subconjunto más pequeño. (El pivote xes 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].

  1. 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 smallerestá vacío) justo en el primer paso. Afortunadamente nuestro algo está listo para eso. Se descarta xe intenta nuevamente con greatersolo.

  2. 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 greatersolo.

  3. 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].

  4. 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:

n1

Caso promedio: O(logn)O(n)

nO(n2)

Hay algunas comparaciones innecesarias en el código anterior, como calcular smallersi lo necesitamos o no, se pueden eliminar fácilmente. (Creo que la evaluación perezosa se encargará de eso).

El vee
fuente
6

Su algoritmo no tiene nada de malo y, por supuesto, la mayoría de los algoritmos recursivos se pueden convertir en bucles, aquí hay una versión en bucle de su código recursivo:

function(T):
    while minimum(T) <= lenght(T):
         remove minimum(T) from T
    loop
fernando.reyes
fuente
66
Todos los algoritmos recursivos se pueden convertir en bucles. Después de todo, una máquina de Turing no sabe nada sobre la recursividad.
David Richerby
4

Cualquier algoritmo recursivo puede reescribirse para usar la iteración. Después de todo, una máquina de Turing no sabe nada sobre la recursividad, pero puede implementar cualquier algoritmo. En principio, puede reescribir su función recursiva escribiendo su propio código de manipulación de la pila para recordar los valores de los parámetros de la función y cualquier variable local que pueda tener. En este caso particular, su función es recursiva de cola (una vez que una llamada recursiva regresa, la cosa que la llamó también regresa inmediatamente) por lo que ni siquiera necesita mantener la pila.

David Richerby
fuente
3

Use un montón mínimo para hacer un montón parcial, ya que no necesita ordenar toda la matriz.

Sigue haciendo estallar elementos con avidez hasta que superes el umbral establecido.

usuario541686
fuente
2
Aquí también, agradecería una idea de corrección.
Raphael