Чи постійний час та амортизований постійний час ефективно вважаються рівнозначними?


16

Мені потрібно написати чергу RandomQueue, яка дозволяє додавати та випадкове видалення за постійний час (O (1)).

Моя перша думка полягала в тому, щоб підтримувати його якимось масивом (я вибрав ArrayList), оскільки масиви мають постійний доступ через індекс.

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

Чи амортизовані постійний час і постійний час ефективно однакові, чи мені потрібно дивитись на якусь структуру, що не потребує повного перерозподілу кожного додавання?

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

  • Будь-яке дерево на основі матиме в кращому випадку O (log n) доступ
  • Зв'язаний список потенційно може мати додатки O (1) (якщо зберігається посилання на хвіст), але випадкове видалення має бути в кращому випадку O (n).

Ось повне запитання; на випадок, якщо я засклив деякі важливі деталі:

Розробити та реалізувати RandomQueue. Це реалізація інтерфейсу черги, в якій операція видалення () видаляє елемент, який вибирається навмання рівномірно серед усіх елементів, що знаходяться в черзі. (Подумайте про RandomQueue як про мішок, в який ми можемо додати елементи або досягти і сліпо вилучити якийсь випадковий елемент.) Операції add (x) та delete () у RandomQueue повинні виконуватись у постійному часі за одну операцію.


Чи визначає завдання, як виконуються випадкові видалення? Вам дається індекс для видалення або посилання на елемент черги?

Це не дає ніякої конкретики. Вимоги - це лише структура, яка реалізує інтерфейс черги і має доповнення та видалення O (1).
Carcigenicate

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

ArrayLists використовує останнє правильно?
Carcigenicate

1
Автор питання (я) думав про амортизоване рішення постійного часу. Я уточню це в наступному виданні. (Хоча тут можна домогтися найгіршого постійного часу, використовуючи техніку деамортизації .)
Пат Морін,

Відповіді:


10

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

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

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


1
Чорт, хороший пункт про видалення; Я не вважав цього. А оскільки ми випадково видаляємо елементи, чи це технічно не означає, що це більше не черга в цьому сенсі?
Carcigenicate

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

2
Я очікую, що я можу RandomQueueреалізувати Queueінтерфейс, а для наданого removeметоду - випадковим чином видалити, а не вискакувати голову, тому не повинно бути ніякого способу розраховувати на конкретне замовлення. Я думаю, враховуючи випадковий характер його тоді, що користувач не повинен сподіватися, що він дотримуватиметься певного порядку. Я процитував завдання у своєму питанні для уточнення. Дякую.
Carcigenicate

2
Так, здається, що у вас все буде добре, якщо ви просто переконайтеся, що видалення предмета зроблено так, як я запропонував.
Майк Накіс

Останнє, якщо ви не заперечуєте. Я подумав про це більше, і не здається, що можливо мати як "справжні" доповнення O (1), так і "справжні" O (1) випадкове видалення; це буде компроміс між 2. Ви або маєте окремо виділену структуру (як масив), яка видаляє, але не additon, або структуру, виділену фрагментом, як Linked-List, який дає доповнення, але не видалення. Це правда? Знову дякую.
Carcigenicate

14

Здається, питання конкретно задає постійний час, а не амортизований постійний час . Отже, стосовно цитованого питання, ні, вони фактично не однакові *. Чи є вони в реальному світі?

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

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

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

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

* Тут хоч трохи проблеми. Для того, щоб будь-який контейнер загального призначення був постійним часом для вставки, одна з двох речей повинна містити:

  • Контейнер повинен мати фіксований максимальний розмір; або
  • можна припустити, що розподіл пам'яті окремих елементів є постійним часом.

"Печінковий сервер" здається дивним словосполученням тут. Ви маєте на увазі "живий сервер", можливо?
Пітер Геркенс

6

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

  • Системи, чутливі до затримок, потребують постійної продуктивності. Для такого сценарію ми повинні підкреслити поведінку системи в гіршому випадку. Прикладами є м'які системи реального часу, такі як ігри, які хочуть досягти послідовного частоти кадрів, або веб-сервери, які повинні надсилати відповідь у певні строгі часові рамки: витрачати циклічні процесори краще, ніж запізнюватися.
  • Системи, оптимізовані пропускною здатністю, не цікавляться випадковими стійлами, якщо максимальна кількість даних може бути оброблена в довгостроковій перспективі. Тут нас насамперед цікавлять амортизовані показники. Це, як правило, стосується розбивання чисел та інших завдань з пакетної обробки.

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

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


На жаль, у мене дуже мало досвіду. Питання закінчується так: "Операції додавання (x) та видалення () у RandomQueue повинні працювати в постійному часі на одну операцію".
Carcigenicate

2
@Carcigenicate, якщо ви не знаєте про факт, що система чутлива до затримки, використання амортизованої складності для вибору структури даних повинно бути абсолютно достатнім.
amon

У мене складається враження, що це може бути вправа з програмування або тест. І, звичайно, не з легких. Абсолютно вірно, що це дуже рідко має значення.
gnasher729

1

Якщо вас просимо для алгоритму «амортизуються постійне час», ваш алгоритм може іноді зайняти багато часу. Наприклад, якщо ви використовуєте std :: vector в C ++, такий вектор, можливо, виділив простір для 10 об’єктів, і коли ви виділяєте 11-й об'єкт, виділяється простір для 20 об'єктів, копіюються 10 об'єктів і додається 11-й, який займає чималий час. Але якщо додати мільйон об’єктів, у вас може бути 999 980 швидких та 20 повільних операцій, при цьому середній час буде швидким.

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

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