Алгоритм об'єднання двох відсортованих масивів із мінімальною кількістю порівнянь


24

Дано два відсортовані масиви a , b типу T з розмірами n та m . Я шукаю алгоритм, який об'єднує два масиви в новий масив (максимального розміру n + m).

Якщо у вас дешева операція порівняння, це досить просто. Просто візьміть з масиву найнижчий перший елемент до повного проходження одного або обох масивів, а потім додайте решту елементів. Щось подібне /programming/5958169/how-to-merge-two-sorted-arrays-into-a-sorted-array

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

У цьому випадку потрібно об'єднати два масиви з мінімальною кількістю порівнянь елементів . Ось кілька прикладів, коли ви повинні бути в змозі зробити набагато краще, ніж простий алгоритм злиття:

a = [1,2,3,4, ... 1000]
b = [1001,1002,1003,1004, ... 2000]

Або

a = [1,2,3,4, ... 1000]
b = [0,100,200, ... 1000]

Є деякі випадки, коли простий алгоритм злиття буде оптимальним, наприклад

a = [1,3,5,7,9,....,999]
b = [2,4,6,8,10,....,1000]

Тож алгоритм повинен ідеально деградувати і виконувати максимум n + m-1 порівнянь у випадку, якщо масиви переплетені, або принаймні не будуть значно гіршими.

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

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

Будь-які ідеї?

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

А з початкової публікації я написав допис у блозі про те, як це працює.


2
Немає можливості зробити менше порівнянь, ніж у "простому алгоритмі злиття". Ви можете спробувати розглянути крайові випадки, як перший, про який ви згадуєте, але це погіршить середній випадок.
Мефі

5
@Mephy: просвіти нас і дай нам офіційний доказ, будь ласка. Або якщо ви не можете, спробуйте видалити (або принаймні уточнити) ваш коментар.
Doc Brown

4
@DocBrown, якби у мене був офіційний доказ, я би дав відповідь, а не коментар. У будь-якому випадку це досить очевидна лінійна проблема, тому що для пошуку кращого, ніж лінійного рішення знадобиться хоча б лінійний час.
Мефі

4
@Mephy: Я пропоную вам знайти час, щоб прочитати відповідь нижче, і подумати двічі над тим, що ви написали.
Док Браун

4
@Mephy Більшість очевидних речей ("ви не можете робити множення менше ніж O (n ^ 2)", "якщо я зміню, яку двері я вибрав, я не поліпшу мої шанси виграти ціну" , "ви можете 'сортувати менше, ніж O (n log n) ", ..) помиляються. Наприклад, використовуючи бінарний підхід у коротшому списку, можливо, покращити середній випадок.
Во

Відповіді:


31

Нормальний алгоритм сортування злиття - крок злиття із звичайним застосуванням порівнянь n + m -1, де один список має розмір n, а інший - розмір m. Використання цього алгоритму - це найпростіший підхід для об'єднання двох відсортованих списків.

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

Давайте зупинимося на мінімізації витрат на порівняння. Ви і тільки ви можете вирішити, чи можна порівняти дані, які ви порівнюєте, чи ні. Якщо ви можете їх кількісно оцінити, це форма реалізації хеш-методу, яка зберігає впорядкування. Наприклад, якщо ваші дані порівнюються з ім'ям, потім з першим іменем, ... ви можете взяти перший до символів імені "Klaehn, Ruediger" і зменшити / квантувати ваш елемент даних до "Kl.Ru", якщо порівнювати його до "Packer, The" Ви зберігаєте замовлення "Pa.Th" - тепер ви можете застосувати більш дешевий алгоритм порівняння, порівнюючи зменшені значення. Але якщо ви знайдете інший "Kl.Ru", тепер у вас є близьке значення, і ви можете зараз перейти до більш дорогого підходу, порівнюючи ці елементи.

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

Я також згадав інший спосіб мінімізувати ваші порівняння.

Я ознайомився з класичною книгою TAOCP - Том 3 сортування та пошуку, (стор. 97-207, розділ 5.3.2), що містить 10 сторінок на цю тему. Я знайшов два посилання на алгоритми, які швидше порівняння n + m-1.

По-перше, існує алгоритм злиття Хван-Ліна, а другий - вдосконалення Глена К Манахера - обидва цитуються TAOCP, а також алгоритм Крістен, який наближається до нижньої межі необхідних порівнянь, на спеціальних умовах на довжині n і m списків.

Алгоритм Манахера був представлений у Journal of the ACM Vol. 26 Число 3 на сторінках 434-440: "Значні вдосконалення алгоритму злиття" Хван-Ліна ". список з m елементами та список з n елементами можуть бути різної довжини, але вони також повинні бути задані кількістю елементів, які вони містять m <= n

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

Середній аналіз випадків злиття алоритму Хванґ та Ліна (Вега, Фриз, Санта) у розділі 2 можна знайти псевдокодом HL-алгоритму. Що набагато краще, ніж мій опис. І ви можете зрозуміти, чому порівнянь менше, - алгоритм використовує двійковий пошук, щоб знайти індекс, куди вставити елемент із коротшого списку.

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


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

4
Після того, як ви покращили свою відповідь, усі, хто звернувся до вас, отримаєте шанс знову звернути вас ;-)
Doc Brown

+1 за те, що якщо розміри сильно відрізняються, то стандартне злиття не є оптимальним.
Флоріан F

1

Припустимо, що два масиви мають N та M елементів, N ≥ M, і всі елементи різні.

Якщо відсортований масив містить елемент x з N, за яким йде елемент y M або навпаки, тоді x і y повинні були порівнюватися, інакше ми б не знали, в якому порядку вони належать. (Не може бути ланцюжок інших елементів, скажімо, a, b, c, звідки ми знаємо, що x <a <b <c <y, наприклад, тому що між x і y немає елементів. Отже, x і y повинні бути порівняні безпосередньо.

Якщо N> M, то можливо мати масив, де кожному елементу M є передує і за ним елемент N, що означає принаймні 2M порівняння - навіть якщо ви використовуєте недетермінований алгоритм сортування, який може зробити ідеальний здогад, які цифри для порівняння. (Що це означає: Припустимо, у вас N великих, M = 1. Двійковий пошук виконує кроки O (log2 N); недетермінований алгоритм здогадається, до яких двох елементів належить один елемент другого масиву, і зробить два порівняння з підтвердити здогад).

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