Как найти максимальный набор элементов массива, такой, что каждый элемент в больше или равен количеству элементов в ?

14

У меня есть алгоритмическая проблема.

Дан массив (или набор) из неотрицательных целых чисел. Найти максимальное множество из , что для всех ,,н STNSTaSa|S|

Например:

  1. Если T = [1, 3, 4, 1, 3, 6], то S может быть [3, 3, 6] или [3, 4, 6] или [4, 3, 6].
  2. В = [7, 5, 1, 1, 7, 4], тогда равно [7, 5, 7, 4].TS

Я пробовал эту рекурсивную функцию.

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

Есть ли какой-нибудь нерекурсивный алгоритм. (Я не проверял свой рекурсивный алгоритм, поэтому он может иметь некоторые недостатки.)

drzbir
источник

Ответы:

14

Сортировка Т. Затем взять элементы в то время как T[i] >= i+1.

Например sorted(T)=[6,4,3,3,1,1]. Затем T[0] = 6 > 1, T[1] = 4 > 2, T[2] = 3 <= 3и , наконец, T[3] = 3 < 4у нас есть S = [T[0], T[1], T[2]].

Каролис Юоделе
источник
3
Это, конечно, пропускает другое решение , но кажется, что ОП искал какое- либо решение, а не все решения. {6,3,3}
Рик Декер
2
Он получает правильное количество элементов. Мы знаем, что у нас есть решения с 3 элементами, но не с 4; в этом случае у нас есть 4 элемента ≥ 3, поэтому мы знаем, что можем выбрать любые 3 из них для решения.
gnasher729
3
Я был бы признателен за аргумент правильности.
Рафаэль
Я думаю, что вы могли бы сделать это в O (N) время с вариантом интроселекта.
user2357112 поддерживает Monica
8

Из моего комментария: «Это тесно связано с количеством, повсеместным в оценке академической производительности, индексом Хирша, более известным как -индексчас . Вкратце она определяется как число публикаций приходится таким образом, что каждый из них имеет по крайней мере час цитат (самый большой такой час ).часчасчас

Единственное, чем отличается ваша проблема, это то, что вас будет интересовать не только количество публикаций, удовлетворяющих этому критерию, но и количество их цитирований , но это тривиальная модификация. Данные уже есть, оригинальный алгоритм просто сбрасывает их.

Общепринятый расчет довольно прост и согласуется с ответом Каролис Юоделе .

Обновление: в зависимости от размера и характера ваших данных, возможно, стоит изучить методы, которые частично сортируют массив путем фильтрации данных выше и ниже центральной точки (на ум приходит быстрая сортировка). Затем, в зависимости от того, слишком мало или слишком много, отрегулируйте опору и повтор на подмножестве, которое его содержит, и так далее. Вам не нужен порядок между элементами выше , и уж точно не между элементами ниже этого. Так, например, как только вы нашли все элементы, большие или равные h 1 и их меньше h 1 , вам не нужно снова прикасаться к этому подмножеству, просто добавьте к нему. Это преобразует рекурсию, присущую быстрой сортировке, в хвостовую рекурсию и, таким образом, может быть переписано как цикл.часчас1час1

Мой Haskell немного заржавел, но это должно сделать то, что я описал выше, и, кажется, работает. Надеюсь, что это может быть понято до некоторой степени, я рад предоставить дополнительные объяснения.

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

Идея состоит в grantedтом, чтобы собрать то, что, как вы знаете, обязательно примет участие в результате, а не сортировать его дальше. Если greaterвместе с xприпадками нам повезло, в противном случае нам нужно попробовать с меньшим подмножеством. (Стержень xпросто все , что случилось с первым пунктом подсписка , который в настоящее время рассматривается.) Следует отметить , что значительное преимущество против принятия крупнейших элементов , один за другим, что мы делаем это на блоках среднего размера и не нужно сортировать их дальше.ремaяNяNграмм/2

Пример:

Давайте возьмем ваш набор [1,3,4,1,3,6].

  1. x = 1, granted = [], greater = [3,4,1,3,6]. Ой, мы встречаемся с патологическим случаем, когда стержень слишком мал (на самом деле настолько мал, что smallerпуст) прямо на первом шаге. К счастью, наш алгоритм готов к этому. Он сбрасывает xи пытается снова с greaterодним.

  2. x = 3, granted = [], greater = [4,3,6]. Вместе они образуют массив длиной 4, но у нас только это ограничено снизу 3, так что это слишком много. Повторите в greaterодиночку.

  3. x = 4, granted = [], greater = [6]. Это дает массив из 2 элементов ≥ 4 каждый, кажется, мы могли бы использовать еще для некоторых из них. Сохраните это и повторите smaller = [3].

  4. x = 3, granted = [4,6], greater = []. Это вместе дает массив из 3 элементов ≥ 3 каждый, поэтому у нас есть решение, [3,4,6]и мы можем вернуться. (Обратите внимание, что перестановка может варьироваться в зависимости от порядка ввода, но всегда будет содержать максимально возможные термины, никогда [3,3,6]или [3,3,4]для вашего примера.)

(Кстати, обратите внимание, что рекурсия действительно просто свернулась в цикл.) Сложность несколько лучше, чем быстрая сортировка из-за многих сохраненных сравнений:

N-1

О(журналN)О(N)

NО(N2)

В приведенном выше коде есть несколько ненужных сравнений, например, вычисление smallerтого, нужен он нам или нет, их можно легко удалить. (Я думаю, что ленивая оценка позаботится об этом, хотя.)

Ви
источник
6

В вашем алгоритме нет ничего плохого, и, конечно, большинство рекурсивных алгоритмов могут быть преобразованы в циклы, вот версия цикла вашего рекурсивного кода:

function(T):
    while minimum(T) <= lenght(T):
         remove minimum(T) from T
    loop
fernando.reyes
источник
6
Все рекурсивные алгоритмы могут быть преобразованы в циклы. В конце концов, машина Тьюринга ничего не знает о рекурсии.
Дэвид Ричерби
4

Любой рекурсивный алгоритм может быть переписан для использования итерации. В конце концов, машина Тьюринга ничего не знает о рекурсии, но может реализовать любой алгоритм. В принципе, вы можете переписать свою рекурсивную функцию, написав свой собственный код манипулирования стеком, чтобы запомнить значения параметров функции и любые локальные переменные, которые она может иметь. В этом конкретном случае ваша функция является хвост-рекурсивной (когда возвращается рекурсивный вызов, то, что вызвало ее, тоже немедленно возвращается), так что вам даже не нужно поддерживать стек.

Дэвид Ричерби
источник
3

Используйте минимальную кучу, чтобы выполнить частичную сортировку, так как вам не нужно сортировать весь массив.

Продолжайте жадно высовывать элементы, пока не превысите указанный порог.

user541686
источник
2
Вот и я бы оценил идею правильности.
Рафаэль