Чому двійковий пошук швидший, ніж потрійний пошук?


49

Пошук масиву з елементів за допомогою двійкового пошуку займає, в гіршому випадку, ітерацій, оскільки на кожному кроці ми половину нашого простору пошуку. Якщо замість цього ми використовували "потрійний пошук", ми би відрізали дві третини свого пошукового простору при кожній ітерації, тож найгірший випадок повинен мати ітерацій ...Nlog2Nlog3N<log2N

Здається, що потрійний пошук швидший, то чому ми використовуємо двійковий пошук?


3
Хіба не можна було використовувати ті ж міркування щодо четвертинного пошуку? Або навіть десятковий пошук ... або щось більше, ніж 2.
d'alar'cop

4
будь ласка, прочитайте про B + Дерева
arunmoezhi

5
Лінійний пошук часто буває швидшим, ніж двійковий пошук по проблемам малого та середнього розміру на сучасному апаратному забезпеченні, оскільки він кешований і майже всі гілки прогнозовані правильно.
Псевдонім

2
Також 2 * log_3 (N) = log_3 (N ^ 2), якщо це відповідає вашій інтуїції.
PawelP

6
Поставимо це інтуїтивно. Якщо використання 3-базового пошуку швидше, оскільки воно скорочує простір пошуку при кожній ітерації, то чи швидше не використовувати мільйонний пошук? Але ви легко бачите, що в середньому вам доведеться зробити 500 000 перевірок всередині кожної ітерації, щоб визначити 1-мільйонний зріз, який містив ціль. Зрозуміло, що скорочення простору пошуку наполовину кожної ітерації та не більше дає надійну інформацію найбільшою мірою за один крок.
ErikE

Відповіді:


76

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

log2(n)+O(1)
2log3(n)+O(1)
2log3(n)+O(1)=2log(2)log(3)log2(n)+O(1)
2log(2)log(3)>1

До речі: пошук -ary може мати багато сенсу в тому випадку, якщо порівняння є досить затратними і їх можна паралелізувати, як тоді, можна застосовувати паралельні комп'ютери.n

Зауважимо, що аргумент можна легко узагальнити до -ary пошуку. Вам просто потрібно показати, що функція суворо монотонно збільшується для цілих значень .nf(k)=(k1)log(2)log(k)k


1
І LHS лінійний, і RHS є логарифмічним, тому він не допоможе жодним четвертинам чи щось більше, ніж це .... Приємні пояснення .... Спасибі
Середній квадрат

3
Тільки задля повноти: зауважте, що така абстрактна міра, як кількість порівнянь елементів, може або не може домінувати над фактичним часом виконання. Зокрема, вам, можливо, доведеться врахувати, скільки кеш-пропускань ви можете отримати на довгих масивах при будь-якому пошуку. (Тут вони співпадають. Я просто відзначаю це, тому що ОП запитує: "чому це швидше?", І відповідаю, що абстрактним заходом можна ввести в оману для деяких алгоритмів.)
Рафаель

10
У потрійному пошуку в 1/3 часу вам знадобиться лише 1 порівняння (зробіть нижнє порівняння: якщо в нижній третині вам не потрібно друге порівняння). Це робить потрійні лише на 5% повільніше замість 25% (у цьому світі, в якому ми дбаємо лише про кількість порівнянь). Я не впевнений, як узагальнити це на n-ary, хоча я підозрюю, що це ніколи не стає швидшим, ніж двійкове.
Аарон Дюфур

2
@AaronDufour: Оскільки можна було б виконати четвертинний пошук, порівнявши спочатку середній елемент, а потім ігноруючи результат інших порівнянь, єдиним способом четвертого пошуку міг би бути швидший, якби три порівняння можна було б зробити паралельно дешевше, ніж два порівняння може виконуватися послідовно.
supercat

1
@AaronDufour Але ви шукаєте амортизацію над елементами для пошуку, і мені незрозуміло, чому це нормально. У гіршому випадку обидва порівняння можуть проводитися на кожному кроці.
Сашо Ніколов

26

DCTLib правильно, але забудьте математику на секунду.

За вашою логікою, n -ary повинен бути найшвидшим. Але якщо ви задумаєтесь, n -ary точно дорівнює звичайному пошуку ітерації (просто повторення через список 1 на 1, але у зворотному порядку). Спочатку вибираєте останній (або поруч з останнім) елемент у списку і порівнюєте це значення зі значенням порівняння. Потім ви вилучаєте цей елемент зі свого списку, а потім вибираєте останній елемент у новому списку, який є лише наступним за останнім значенням у масиві. Кожного разу ви будете вилучати лише одне значення за один раз, поки не знайдете своє значення.

Натомість вам слід думати про це так - як я вилучаю найбільші значення зі списку кожної ітерації? Під час двійкового пошуку ви завжди виключаєте половину списку. Під час потрійного пошуку існує ймовірність (33,33% шансу, насправді) ви зможете усунути 2/3 списку, але є ще більший шанс (66,66%), що ви вилучите лише 1/3 списку. для того, щоб обчислити O (n), потрібно подивитися на найгірший сценарій, який становить 1/3, менше 1/2. По мірі наближення і наближення до n вона стає ще гіршою.

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

(P_lower) x (частина, яку ми можемо видалити, якщо нижня) + (P_higher) x (частина, яку ми можемо видалити, якщо вище) = E

Для двійкового пошуку це .5x.5 + .5x.5 = .5 (ми завжди видаляємо половину списку). Для потрійних пошуків це значення становить .666x.333 + .333x.666 = 0.44, або на кожному кроці ми, ймовірно, в середньому видалимо лише 44% списку, що робить його менш ефективним, ніж двійковий пошук. Це значення досягає максимальної позначки 1/2 (половина списку) і зменшується ближче до досягнення n (зворотна ітерація) та 0 (регулярна ітерація).

Гаразд, так я збрехав ... тут мало математики, але я сподіваюся, що це допомагає!


1
Це чудова відповідь.
The_Sympathizer

Аналіз кордонів Ya допомагає зрозуміти важку математику! n-ary послідовний пошук має однакову вартість лінійного пошуку O (n).
shuva

-2

Зверніть увагу, що аргумент порівняння log (N) проти 2 log (N) заснований на наївній інтерпретації алгоритму. Якби я насправді сідав і писав це в складі x86, результати були б перевернуті. Проблема полягає у використанні цілих чисел для тестових випадків у поєднанні з недостатньо розумним компілятором, який не може усунути зайві порівняння. Повторіть з рядками та відповідною функцією порівняння рядків, і кодуйте її, щоб викликати функцію порівняння один раз за цикл, і ви побачите, що потрійний пошук знову швидший.


2
Звичайно, потрійний пошук був би швидшим, якби ви могли зробити це лише одним порівнянням за ітерацію. Але, незалежно від того, чи це рядки чи цілі числа, ви не можете.
FrankW

Порівняння не були б зайвими, і проблема не має нічого спільного з компілятором. Для того щоб розділити простір пошуку на три частини, вам потрібно 2 порівняння. У двійковому пошуку вам потрібно лише порівняти із середнім елементом, і тоді ви дізнаєтеся, на якій половині пошукового простору лежатиме результат. При потрійному пошуку вам потрібно буде порівняти з елементом 1/3 шляху через список І одна 2/3 шляху через список. Який тип даних ви порівнюєте або яку мову ви використовуєте, не має значення. Звичайно, якщо товар знаходиться в 1-му 3-му, ви можете зупинитись після 1 порівняння.
reirab

2
На деяких платформах потрійний пошук міг би бути швидшим за рахунок надання процесору більше часу для отримання операндів з оперативної пам’яті, перш ніж потребувати їх для порівняння. Але це повністю залежить від використовуваної платформи та її затримок та кеш-пам'яті.
jpa

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