Знаходження k'-го найменшого елемента із заданої послідовності лише з O (k) пам'яттю O (n) час


11

Припустимо, що ми читаємо послідовність з nn чисел, по одному. Як знайти kk '-й найменший елемент просто за допомогою використання клітинної пам'яті O ( k )O(k) та за лінійним часом ( O ( n )O(n) ). Я думаю , що ми повинні зберегти перші Kk члени послідовності і коли отримаємо K + 1k+1 "й член, видалити термін , який ми впевнені , що вона не може стати доk » й найменший елемент , а потім зберегти до + 1k+1 "й член. Отже, у нас повинен бути індикатор, який показує цей непридатний термін на кожному кроці, і цей показник повинен бути оновлений на кожному кроці швидко. Я почав з "max"; але він не може швидко оновитись; Це означає, що якщо ми вважаємо max, то при першому видаленні ми пропускаємо max, і нам слід шукати max в O ( k )O(k) та його причині ( n - k ) × O ( k ),(nk)×O(k) що це не лінійно. Можливо, нам слід розумніше зберегти перші kk терміни послідовності.

Як вирішити цю проблему?


1
Вас цікавить онлайн-алгоритм, чи хотів би якийсь алгоритм?
Yuval Filmus

Якщо k = θ ( n ),k=θ(n) ви можете це зробити, використовуючи алгоритм статистики замовлень. Якщо k = o ( n ),k=o(n) ви можете зробити це пам'ять O ( k )O(k) та час O ( n log k ),O(nlogk) використовуючи дерева з урівноваженою висотою.
Шрееш

Це називається проблемою вибору en.wikipedia.org/wiki/Selection_algorithm
xavierm02

Існують лінійні алгоритми на місці, які можна гуглювати, але вони дещо складні.
Yuval Filmus

@ xavierm02 це не проблема вибору однаково. Тому що існує обмеження обмеження пам'яті.
Shahab_HK

Відповіді:


16

Створіть буфер розміром 2 k . Читайте в 2 K елементів з масиву. Використовуйте алгоритм вибору лінійного часу, щоб розділити буфер, щоб першими були найменші k елементи; це займає час O ( k ) . Тепер прочитайте в буфер ще один k елементів з масиву, замінивши k найбільших елементів у буфері, розділіть буфер, як раніше, і повторіть.2k2kkO(k)kk

Це займає O ( k n / k ) = O ( n ) час і O ( k ) простір.O(kn/k)=O(n)O(k)


+1, це відповідає заданій асимптотиці. Якщо говорити, я не вірю, що це швидше, ніж робити єдиний алгоритм вибору лінійного часу ... за винятком випадків, коли k - мала константа, то це забезпечує цікаву перспективу. Наприклад, для k = 1 цей алгоритм виробляє функцію. kk=1min
orlp

1
Іноді алгоритм вибору лінійного часу використовує занадто багато місця. Наприклад, він не підходить для використання в потоковому контексті або коли вхідний масив є незмінним.
jbapple

Це дійсні бали.
orlp

3

Ви можете зробити це в пам'яті O ( k ) та O ( n log k ) , створивши фіксований розмір max-heap з перших k елементів за O ( k ) час, потім повторити і решту масиву та натиснувши новий елемент, а потім вискакує для O ( log k ) для кожного елемента, даючи загальний час O ( k + n log k ) = O ( n log k ) .O(k)O(nlogk)kO(k)O(logk)O(k+nlogk)O(nlogk)

Ви можете зробити це в допоміжній пам’яті O ( log n ) та O ( n ) час, використовуючи алгоритм вибору медіани медіанів, вибираючи при k і повертаючи перші k елементи. Без зміни асимптотики ви можете використовувати інтроселект для прискорення середнього випадку. Це канонічний спосіб вирішити вашу проблему.O(logn)O(n)kk

Зараз технічно O ( log n ) і O ( k ) є незрівнянними. Однак я стверджую, що O ( log n ) є кращим на практиці, оскільки він фактично постійний, враховуючи, що жодна комп'ютерна система не має більше 2 64 байт пам'яті, log 2 64 = 64 . Тим часом k може вирости таким же великим, як n .O(logn)O(k)O(logn)264log264=64kn


Зауважте, що ви можете поліпшити складність алгоритму на основі купи до O ( n × log min ( k , n - k ) ) , змінивши порядок, який використовує купа, коли це цікаво. O(n×logmin(k,nk))
xavierm02

@ xavierm02 O ( m i n ( k , n - k ) ) = O ( k ) . Доведення: найгірший випадок для k є n . Найгірший випадок для m i n ( k , n - k ) - nO(min(k,nk))O(k)knmin(k,nk)2 . Вони однакові в межах постійного коефіцієнта, таким чином O(min(k,n-k))=O(k). n2O(min(k,nk))O(k)
orlp

@ xavierm02 Як це сказати, це все-таки приємна швидкість :)
orlp

u n , k = k - O ( k ), але це не O ( min ( k , n - k ) ) . Припустимо, це так. Тоді є деякі C і деякі M, так що для кожного M k n ми маємо k C ( n - k ) , що явно помилково (тому що ми можемо взяти n = k + ) .un,k=kO(k)O(min(k,nk))CMMknkC(nk)n=k+). Отже O ( хв ( k , n - k ) ) O ( k ) . O(min(k,nk))O(k)
xavierm02

@ Xavierm02 Я не знайомий з ˙U п , К нотації. Щоб бути справедливим, я взагалі зовсім НЕ знайомі з багатовимірними big- O нотації, особливо якщо врахувати , що розміри п , до не пов'язані.
orlp
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.