Como encontrar o conjunto máximo de elementos

14

Eu tenho um problema algorítmico.

Dada uma matriz (ou um conjunto)TnSTaSa|S|

Por exemplo:

  1. Se = [1, 3, 4, 1, 3, 6], então S pode ser [3, 3, 6] ou [3, 4, 6] ou [4, 3, 6].TS
  2. Em T = [7, 5, 1, 1, 7, 4], então S é [7, 5, 7, 4].

Eu tentei essa função recursiva.

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

Existe algum algoritmo não recursivo. (Eu não verifiquei meu algoritmo recursivo, portanto, ele pode ter alguma falha.)

drzbir
fonte

Respostas:

14

Classifique T. Depois, pegue os elementos while T[i] >= i+1.

Por exemplo sorted(T)=[6,4,3,3,1,1]. Então, T[0] = 6 > 1, T[1] = 4 > 2, T[2] = 3 <= 3e, finalmente, T[3] = 3 < 4por isso temos S = [T[0], T[1], T[2]].

Karolis Juodelė
fonte
3
Obviamente, isso perde a outra solução , mas parece que o OP estava procurando por qualquer solução, em vez de todas as soluções. {6,3,3}
Rick Decker
2
Acerta o número de elementos. Sabemos que temos soluções com 3 elementos, mas não com 4; neste caso, temos 4 elementos ≥ 3, portanto sabemos que podemos escolher 3 para uma solução.
gnasher729
3
Eu apreciaria um argumento de correção.
Raphael
Eu acho que você provavelmente poderia fazê-lo em O (n) tempo com uma variante de introseleção.
user2357112 suporta Monica
8

Do meu comentário originalmente: Isto está intimamente relacionado a uma onipresente quantidade na avaliação acadêmica produtividade, o índice Hirsh, mais conhecido como o -Indexh . Em suma, é definida como o número de publicações uma tem de tal modo que cada um deles tem pelo menos h citações (o maior tal h ).hhh

A única maneira de o seu problema diferir é que você estaria interessado não apenas em quantas publicações atendem ao critério, mas também em qual é a citação delas , mas essa é uma modificação trivial. Os dados já estão lá, o algoritmo original simplesmente os solta.

O cálculo geralmente implementado é bastante direto e concorda com a resposta de Karolis Juodelė .

Atualização: Dependendo do tamanho e do caráter dos seus dados, pode valer a pena explorar métodos que classificam parcialmente a matriz, filtrando os dados acima e abaixo de um ponto crucial (o quicksort vem à mente). Então, dependendo se há muito pouco ou muitos, ajuste o pivô e refaça o subconjunto que o contém e assim por diante. Você não precisa de uma ordem entre elementos maiores que , e certamente não entre aqueles inferiores a isso. Por exemplo, quando você encontrar todos os elementos maiores ou iguais a h 1 e houver menos de h 1 deles, não precisará tocar nesse subconjunto novamente, basta adicioná-lo. Isso converte a recursão inerente ao quicksort em uma recursão de cauda e, portanto, pode ser reescrita como um loop.hh1h1

Meu Haskell está um pouco enferrujado, mas isso deve fazer o que descrevi acima e parece funcionar. Espero que possa ser entendido até certo ponto, estou feliz em fornecer mais explicações.

-- 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 []

A idéia é coletar o grantedque você sabe que definitivamente participará do resultado e não o classificará mais. Se, greaterjuntamente com xataques, tivermos sorte, caso contrário, precisamos tentar com um subconjunto menor. (O pivô xé simplesmente o que aconteceu para ser o primeiro item da sub-lista que é actualmente considerado.) Note-se que a vantagem significativa contra a tomada de maiores elementos, um por um é que fazemos isso em blocos de tamanho médio e não precisa classificá-los ainda mais.remumaEunEung/2

Exemplo:

Vamos levar o seu set [1,3,4,1,3,6].

  1. x = 1, granted = [], greater = [3,4,1,3,6]. Ai, chegamos a um caso patológico quando o pivô é muito pequeno (na verdade, tão pequeno que smallerestá vazio) logo no primeiro passo. Felizmente, nosso algo está pronto para isso. Ele descarta xe tenta novamente greatersozinho.

  2. x = 3, granted = [], greater = [4,3,6]. Juntos, eles formam uma matriz de comprimento 4, mas temos apenas o limite inferior de 3 por isso é demais. Repita o procedimento greatersozinho.

  3. x = 4, granted = [], greater = [6]. Isso fornece uma matriz de 2 elementos ≥ 4 cada, parece que poderíamos usar para mais alguns deles. Mantenha isso e repita smaller = [3].

  4. x = 3, granted = [4,6], greater = []. Juntos, isso fornece uma matriz de 3 elementos ≥ 3 cada, para que tenhamos nossa solução [3,4,6]e possamos retornar. (Observe que a permutação pode variar dependendo da ordem da entrada, mas sempre conterá os termos mais altos possíveis, nunca [3,3,6]ou [3,3,4]para o seu exemplo.)

(Observe que a recursão realmente caiu em um ciclo.) A complexidade é um pouco melhor que a classificação rápida, devido às muitas comparações salvas:

n-1

O(registron)O(n)

nO(n2)

Existem algumas comparações desnecessárias no código acima, como calcular smallerse precisamos ou não, elas podem ser facilmente removidas. (Eu acho que a avaliação preguiçosa vai cuidar disso.)

The Vee
fonte
6

Não há nada de errado com seu algoritmo e, é claro, a maioria dos algoritmos recursivos pode ser convertida em loops, aqui uma versão em loop do seu código recursivo:

function(T):
    while minimum(T) <= lenght(T):
         remove minimum(T) from T
    loop
fernando.reyes
fonte
6
Todos os algoritmos recursivos podem ser convertidos em loops. Afinal, uma máquina de Turing não sabe nada sobre recursão.
David Richerby
4

Qualquer algoritmo recursivo pode ser reescrito para usar a iteração. Afinal, uma máquina de Turing não sabe nada sobre recursão, mas pode implementar qualquer algoritmo. Em princípio, você pode reescrever sua função recursiva escrevendo seu próprio código de manipulação de pilha para lembrar os valores dos parâmetros da função e quaisquer variáveis ​​locais que ela possa ter. Nesse caso específico, sua função é recursiva de cauda (uma vez que uma chamada recursiva retorna, o que a chamou imediatamente também retorna), assim você nem precisa manter a pilha.

David Richerby
fonte
3

Use um min-heap para fazer um heapsort parcial, pois você não precisa que toda a matriz seja classificada.

Continue aparecendo os elementos avidamente até exceder o limite especificado.

user541686
fonte
2
Aqui também, eu apreciaria uma idéia de correção.
Raphael