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


21

Назва зазначає питання.

У якості вхідних даних у нас є список елементів, які ми можемо порівняти (визначити, який найбільший ). Жоден елемент не може бути рівним.

Ключові моменти:

  1. Порівняння не є транзитивним (подумайте ножиці з паперової папери): це може бути правдою: A> B, B> C, C> A (зауважте, що це неправдивий вклад, оскільки тут немає правильної відповіді, я лише описую, що " неперехідне порівняння "означає)
  2. Кожен вхідний масив гарантовано матиме відповідь
  3. Найбільше означає, що елемент повинен бути більшим, ніж будь-який інший елемент
  4. Зворотна властивість має місце, тобто A> B означає, що B <A

Приклад:

Input: [A,B,C,D]
A > B, B > C, C > A
D > A, D > B, D > C
Output: D

Я не можу знайти спосіб зробити це за O (n) час, найкраще рішення - O (n ^ 2).

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

Це виключає використання купи, сортування тощо.


8
Незрозуміло, як би визначався "найбільший елемент"? Наприклад, який елемент найбільший, якщо ? Чи є у вас інші правила порівняння? A>B,B>C,C>A
fade2black

6
Я не можу уявити, як ми вибрали б найбільший елемент у наборі, який, принаймні, частково не упорядкований. Будь ласка, дивіться визначення найбільшого і найменшого елемента. Відсутність транзитивності виключає частковий порядок.
fade2black

3
@ fade2black Чому ти пов'язуєш мене з іншим визначенням "найбільшого". Я прямо висловлюю визначення найбільшого для контексту цього питання. Найбільше означає, що елемент більший, ніж у всіх інших елементів. Жоден елемент не дорівнює. Це все, що там є. Це не ясно?
Джеймс Віцерба

2
Ваш останній приклад з A, B, C, D був би корисним, щоб зрозуміти ваше питання, якщо ви включили його до своєї ОП.
fade2black

3
Член команди-компілятора C # задавав це як питання інтерв'ю; це актуально, оскільки в C # алгоритм роздільної здатності перевантаження повинен вибирати найкращого члена набору з урахуванням відношення "кращості", яке зазвичай, але не обов'язково є перехідним. (Або дайте відповідну відповідь, якщо немає такого унікального найкращого члена; зв’язки можливі.) Хоча мені вдалося відповісти правильно, я ніколи не думав, що це особливо гарне питання інтерв'ю, оскільки воно покладається на "аха" розуміння, щоб отримати лінійний алгоритм.
Ерік Ліпперт

Відповіді:


38

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

Щоб було зрозуміло, під "стандартним алгоритмом" я маю на увазі наступне:

max <- a_1
for i=2 to n
   if a_i > max
      max <- a_i
output max

Для повноти я обговорю тут питання, порушені в коментарях. Встановлення у вищенаведеному обговоренні знаходить максимум відносно антисиметричного відношення , де - максимум, якщо для всіх маємо . Наведений вище алгоритм працює при припущенні, що існує максимум, але якщо цього не відомо, можна використовувати його для перевірки існування максимуму (перевірити, чи дійсно повернутий елемент більший за всі інші елементи, про це йдеться у коментарі Чи і в відповідь Ільмарі Каронен).Я J я я > J<aijiai>aj

Якщо не обов'язково є антисиметричним, то вищезгаданий алгоритм виходить з ладу (як Еміль згадував у коментарях). Якщо < - довільне відношення (тобто ми розслабляємо як транзитивність, так і антисиметрію), то не важко показати, що в лінійному часі знайти максимум неможливо. Позначимо через # a i кількість разів, коли я брав участь у запиті, ми визначаємо змагальне відношення таким чином, що максимум неможливо розкрити без достатньої кількості запитів. Дано запит a i > ? a j , відповідь a i > a j, якщо # a i<<#aiaiai>?ajai>aj і a i < a j в іншому випадку. Якщо кількість запитів дорівнює o ( n 2 ) , максимум ще не було помічено, і його можна встановити як будь-який із елементів набору.#ai<n1аi<аjо(н2)


1
@JamesWierzba (я думаю) він просто означає "пропущений" елемент - це той, який не перевищує вашого поточного макс. Розглянемо стандартний алгоритм: ви перевіряєте кожне значення у своєму списку на поточний максимум. Ви сказали, що в кожному списку є найбільший елемент. У якийсь момент ви порівняєте це з вашим поточним максимумом, а оскільки воно більше, це значення стане вашим новим максимумом. Оскільки це значення більше, ніж усе в списку, ви ніколи не знайдете більшого елемента, і ваше найбільше значення не буде замінено. Після ваших nпорівнянь вашим теперішнім максимумом має стати відповідь
Lord Farquaad

1
Цей алгоритм, відредагований для наочності, не передбачає транзитивності. Якщо вам важко повірити, дотримуйтесь деталей доказу коректності (припустимо з метою суперечності, що повернене значення не є максимальним, і використовуйте ідею з першого абзацу).
Аріель

7
Це спирається на припущення 2 у питанні: у масиві завжди є максимум. Якби це не так, maxміг бути лише максимум підматриці. Однак, навіть без припущення 2, можна знайти орієнтовний максимум, а потім перевірити його на всьому масиві, використовуючи друге сканування, в межах O (n).
чі

7
Ця відповідь передбачає, що і B > A не можуть утримуватися одночасно. Наскільки я бачу, це не виключається у питанні. А>ББ>А
Еміль Йерабек підтримує Моніку

4
@ oconnor0 Це не випливає. Для конкретного прикладу припустимо A> B, A> C, B> A і C> B. Тоді A більший, ніж будь-який інший елемент у наборі (і є єдиним елементом з цією властивістю), але якщо елементи є зустрічаються в порядку A, B, C, алгоритм виведе C.
Еміль Йеребек підтримує Моніку

24

Як зазначає Аріель , стандартний алгоритм максимального пошуку, наведений нижче:

def find_maximum(a):
    m = a[0]
    for x in a:
        if x > m: m = x
    return m

насправді буде працювати без змін, поки:

  • будь-яку пару елементів можна порівняти, і
  • гарантовано, що вхід містить максимальний елемент, тобто елемент, парний більший, ніж будь-який інший елемент у вході.

(Перше припущення вище може насправді бути розслабленим, навіть не змінюючи алгоритм, якщо ми припускаємо, що максимальний елемент можна порівняти з усіма іншими елементами, і x > yце завжди помилково, якщо елементи xі yнепорівнянні.)

Зокрема, ви заявляєте, що:

[…] Щоб бути певним у відповіді, елемент потрібно чітко порівнювати з усіма іншими елементами (оскільки порівняння не є транзитивним).

не відповідає дійсності припущених вище припущень. Насправді, щоб довести, що алгоритм, наведений вище, завжди знайде максимальний елемент, достатньо зауважити, що:

  1. оскільки цикл перетворюється на всі вхідні елементи, на деякій ітерації xбуде максимальним елементом;
  2. оскільки максимальний елемент вдвічі більший, ніж будь-який інший елемент, випливає, що в кінці цієї ітерації mбуде максимальним елементом; і
  3. оскільки жоден інший елемент не може бути вдвічі більшим за максимальний елемент, з цього випливає, що mвін не зміниться ні на одній з наступних ітерацій.

Тому в кінці циклу mзавжди буде максимальний елемент, якщо вхід містить його.


Пс. Якщо вхід не обов'язково завжди містить максимальний елемент, то для підтвердження цього факту дійсно буде потрібно тестування відповіді кандидата проти кожного іншого елемента, щоб переконатися, що він дійсно максимальний. Однак ми все ще можемо це зробити за O ( n ) час після запуску алгоритму максимального знаходження вище:

def find_maximum_if_any(a):
    # step 1: find the maximum, if one exists
    m = a[0]
    for x in a:
        if x > m: m = x

    # step 2: verify that the element we found is indeed maximal
    for x in a:
        if x > m: return None  # the input contains no maximal element
    return m  # yes, m is a maximal element

(Я припускаю, що тут відношення >є нерефлексивним, тобто жоден елемент не може бути більшим за себе. Якщо це не обов'язково, порівняння x > mна кроці 2 слід замінити на x ≠ m and x > m, де позначається порівняння ідентичності. Або ми могли просто застосувати оптимізацію зазначено нижче.)

Щоб довести правильність цього варіанту алгоритму, розглянемо два можливі випадки:

  • Якщо вхід містить максимальний елемент, то етап 1 знайде його (як показано вище), а крок 2 підтвердить його.
  • Якщо вхід не містить максимального елемента, тоді крок 1 закінчить вибір якогось довільного елемента як m. Не має значення, який це елемент, оскільки він у будь-якому випадку буде не максимальним, а тому крок 2 виявить це і повернеться None.

Якщо ми зберегли індекс mв вхідному масиві a, ми могли б на самому ділі оптимізувати крок 2 , щоб перевірити тільки ті елементи , які приходять , перш ніж mв пам'яті a, оскільки будь-які наступні елементи вже по порівнянні з mв кроці 1. Але ця оптимізація не змінює асимптотичну складність часу алгоритму, який все ще є O ( n ).


3
Насправді ОП пропускає багато деталей. Наприклад, нічого не сказано про рефлексивність відношення, і якщо воно не є рефлексивним, то if x > m:воно не визначене.
fade2black

4

О(н)

Якщо ви перейдете до списку порівнюючих елементів, будь-який елемент, який "втрачає" порівняння, можна негайно відкинути, оскільки, щоб бути найбільшим, він повинен бути більшим за ВСІ інші елементи, тому одна втрата дискваліфікує його.

н-1

Це рішення увімкнено тонкістю: "Жоден елемент не може бути рівним" у поєднанні з тим, що завжди буде найбільший елемент. Якщо ми відображаємо відносини виграшів як спрямований графік, то зрозуміло, що ми можемо досягти найбільшого елемента просто слідуючи на виграші.


1
" Ациклічний спрямований графік " - неправильна модель: натомість він повинен бути " турнірним ". Цикли дозволяються, і дуже важливо, щоб кожен край існував саме в одному напрямку.
Пітер Тейлор

@PeterTaylor Ви абсолютно праві, я думав лише про виграші, які ведуть до "найбільшого" елемента, інші перемоги менш релевантні, але, можливо, ви пройдетеся по шляху до відкриття найбільшого, щоб ви мали рацію, що зможете " не знижуватимуться
Даніков

3

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

Якщо деякі елементи не можна порівняти, діє наступна процедура

max = nil
For i=1 to n
   if max is nil then
      max = a[i]
   if max and a[i] are not comparable then
      max = nil
   else if max < a[i] then
      max = a[i]
End

А,Б,С,D

А>Б,Б>С,С>А
D>А,D>Б,D>С


i=1: макс=А
i=2: макс=АА>Б
i=3: макс=СА<С
i=4: макс=DD>С

м>аа<мамм<аамам


Я не думаю, що перше else ifпотрібно. Він не може спрацьовувати, якщо maxмаксимум, а якщо максимум ще не зустрічається, не має значення, яке значення maxмає.
rici

Так, це перший. Інший - другий :)
rici

Ви маєте на увазі залишити ifs без elses? Це просто звичка: з elses ми навіть не порівнюємо. :)
fade2black

1
Чи не було б простіше просто ініціалізуватися maxна будь-якому елементі списку, а всередині циклу робити if max and a[i] are comparable and max < a[i] then max = a[i](де першу частину умови можна опустити, якщо припустити, що спроба порівняння двох незрівняних елементів завжди призводить до помилки)?
Ільмарі Каронен

1
@badallen ОП припускає, що завжди є найбільшим елементом. У вашому прикладі немає найбільшого елемента.
fade2black

2

А<ББ<А

А1...АнАi<Аj

н2

Аi<Аjj

Отримане відношення завжди буде таким, що існує деяке для якого A i < A j 0j0Аi<Аj0ijijАi<АjiijАij<Аjj0ij

Сподіваюся, це дещо зрозуміло. Сміливо запитайте в коментарях чи редагуйте.

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

А<Анн2-н


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