Як знайти максимальний набір елементів


14

У мене є алгоритмічна проблема.

Дано масив (або набір) з n неотримних цілих чисел. Знайдіть максимальний набір S з T таким, що для всіх a S , a | S | .TnSTaSa|S|

Наприклад:

  1. Якщо = [1, 3, 4, 1, 3, 6], то S може бути [3, 3, 6] або [3, 4, 6] або [4, 3, 6].TS
  2. У = [7, 5, 1, 1, 7, 4], тоді S дорівнює [7, 5, 7, 4].TS

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

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

Чи є нерекурсивний алгоритм. (Я не перевіряв свій рекурсивний алгоритм, тому він може мати якийсь недолік.)

Відповіді:


14

Сортувати T. Потім взяти елементи 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 підтримує Моніку

8

З мого коментаря спочатку: Це тісно пов'язане з великою кількістю, всюдисущою в оцінці академічної продуктивності, індексом Гірша, більш відомим як -indexh . Коротше кажучи, вона визначається як кількість публікацій яка має таке, що кожне з них має принаймні h цитат (найбільший з таких h ).hhh

Єдиний спосіб, по якому ваша проблема відрізняється, це те, що вас зацікавило б не тільки те, скільки публікацій відповідає критерію, а й те, що їх кількість цитованих , але це тривіальна модифікація. Дані вже є, оригінальний алгоритм просто скидає їх.

Загально реалізований розрахунок досить простий і погоджується з відповіддю Кароліс Юоделі .

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

Мій 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- це просто те, що сталося, є першим пунктом підспілу, який зараз розглядається.) Зауважте, що суттєвою перевагою перед прийняттям найбільших елементів по черзі є те, що ми робимо це на блоках середнього розміру і не потрібно їх сортувати далі.remaining/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]для вашого прикладу.)

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

н-1 порівняння

О(журналн)О(н) порівняння в загальній складності

нO(n2)

У наведеному вище коді є декілька непотрібних порівнянь, як, наприклад, підрахунок, smallerпотрібен він нам чи ні, їх можна легко усунути. (Я думаю, що ледача оцінка допоможе про це.)


6

З вашим алгоритмом немає нічого поганого, і, звичайно, більшість рекурсивних алгоритмів можна перетворити на цикли, ось циклічна версія вашого рекурсивного коду:

function(T):
    while minimum(T) <= lenght(T):
         remove minimum(T) from T
    loop

6
Всі рекурсивні алгоритми можуть бути перетворені в петлі. Адже машина Тьюрінга нічого не знає про рекурсію.
Девід Річербі

4

Будь-який рекурсивний алгоритм може бути переписаний для використання ітерації. Зрештою, машина Тьюрінга нічого не знає про рекурсію, але може реалізувати будь-який алгоритм. В принципі, ви можете переписати свою рекурсивну функцію, написавши власний код маніпулювання стеком, щоб запам'ятати значення параметрів функції та будь-які локальні змінні, які вони можуть мати. У цьому конкретному випадку ваша функція є рекурсивною (як тільки повертається рекурсивний виклик, те, що викликало його, негайно повертається), тому вам навіть не потрібно підтримувати стек.


3

Використовуйте міні-купу, щоб зробити частковий набір, оскільки для сортування не потрібен весь масив.

Тримайте стискаючі елементи жадібно, поки не перевищите заданий поріг.


2
Тут також я би вдячний ідеєю правильності.
Рафаель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.