Постійний амортизований час


Відповіді:


776

Амортизований час пояснюється простими словами:

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

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

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

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


61
Просто примітка з точки зору позначення: Амортизований постійний час виконання O (n) часто записується як O (n) +, на відміну від просто O (n). Додавання знаку плюс вказує на те, що час виконання не гарантується як O (n) і може фактично перевищувати цей час виконання.
Jeffpowrs

1
Що стосується розподілу місця, то це з купи?
відданийросійський

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

2
@Jeffpowrs Я думав, що O (n) - лінійний час, а O (1) - постійний час . Так це означає, що O (1) + буде амортизуватися постійним часом, а O (n) + буде амортизуватися лінійним часом?
Джон Мейєр

1
@JohnMeyer Так.
Артелій

55

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

Важливо те, що алгоритм гарантує, що після послідовності операцій дорогі операції будуть амортизовані і тим самим надавати всю операцію O (1).

Або більш строго,

Існує константа c, така що для кожної послідовності операцій (також однієї, що закінчується дорогою операцією) довжиною L, час не більше c * L (спасибі Рафалу Даугірду )


11
"після достатньо великої кількості операцій" - постійний амортизований час не потребує цієї умови. Існує константа c, така що для кожної послідовності операцій (також однієї, що закінчується дорогою операцією) довжиною L, час не більше c * L.
Rafał Dowgird

Звідки це виділення вдвічі більшої суми ? Чи не варто нам виділяти один запис? Або це гіпотетичний приклад?
talekeDskobeDa

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

23

Щоб розробити інтуїтивно зрозумілий спосіб мислення про це, розгляньте можливість вставки елементів у динамічний масив (наприклад, std::vectorу C ++). Побудуємо графік, який показує залежність кількості операцій (Y), необхідних для вставки N елементів у масив:

сюжет

Вертикальні частини чорного графіка відповідають перерозподілу пам'яті з метою розширення масиву. Тут ми можемо побачити, що цю залежність можна приблизно представити як лінію. І це рівняння прямої Y=C*N + b( у нашому випадку Cє постійним, b= 0). Тому ми можемо сказати, що нам потрібно витратити C*Nоперації в середньому, щоб додати N елементів до масиву, або C*1операції, щоб додати один елемент (амортизований постійний час).


14

Нижче я знайшов пояснення у Вікіпедії корисним після повторного читання 3 рази:

Джерело: https://en.wikipedia.org/wiki/Amortized_analysis#Dynamic_Array

"Динамічний масив

Амортизований аналіз операції Push для динамічного масиву

Розглянемо динамічний масив, який збільшується в розмірі, оскільки до нього додається більше елементів, таких як ArrayList на Java. Якби ми почали з динамічного масиву розміром 4, для просунення на нього чотирьох елементів знадобиться постійний час. Однак натискання п’ятого елемента на цей масив займе більше часу, оскільки масив повинен створити новий масив, подвійний поточному розміру (8), скопіювати старі елементи на новий масив, а потім додати новий елемент. Наступні три операції натискання так само потребуватимуть постійного часу, і тоді наступне додавання вимагатиме ще одного повільного подвоєння розміру масиву.

Загалом, якщо ми враховуємо довільну кількість натискань n на масив розміром n, ми помічаємо, що операції натискання займають постійний час, за винятком останнього, який потребує часу O (n) для виконання операції подвоєння розміру. Оскільки було всього n операцій, ми можемо взяти середнє значення цього і виявити, що для натискання елементів на динамічний масив потрібно: O (n / n) = O (1), постійний час ".

Наскільки я розумію як просту історію:

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

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

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

Іншими словами, це було легко до N, всього 1 операція, але коли нам потрібно переїхати до більшої кімнати, ми зробили N операцій. Інакше кажучи, якщо ми середньо оцінимо, це 1 вставка на початку, а ще 1 переміщення під час переміщення до іншої кімнати. Всього 2 операції, одна вставка, один хід.

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

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

Отже, у нас є N2 (який включає N попереднього, оскільки ми переміщуємо все від малого до великого приміщення)

Нам все ще потрібно лише 2 операції: одна вставляється у більшу кімнату, потім інша операція переміщення переходить у ще більшу кімнату.

Так, навіть для N2 (1 мільярд) це 2 операції на кожну. що знову нічого. Отже, це постійна, або O (1)

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


Тепер припустимо, у вас N як 1, дуже маленьке, кількість грошей невелике, і у вас дуже маленька кімната, в яку поміститься лише 1 кількість грошей.

Як тільки ви заповнюєте гроші в кімнаті, кімната заповнюється.

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

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

Після 100 разів нова кімната підходить до 100 грошей від попереднього та ще 1 грошей, у якому вона вміститься. Це O (N), оскільки O (N + 1) - O (N), тобто ступінь 100 або 101 однакова, обидві сотні, на відміну від попередньої історії, - мільйони, а одні - мільярди .

Отже, це неефективний спосіб розподілу номерів (або пам'яті / оперативної пам’яті) за наші гроші (змінні).


Отже, хорошим способом є виділення більшої кількості приміщень, що мають 2 повноваження.

Розмір 1-ї кімнати = підходить 1 кількість грошей
2-й розмір кімнати = підходить 4 кількість грошей
3-й розмір кімнати = підходить 8 кількість грошей
4-й розмір кімнати = підходить 16 кількість грошей
5-й розмір кімнати = підходить 32 кількість грошей
6 розмір кімнати = підходить 64 кількість грошей
7 розмір кімнати = підходить 128 кількість грошей
8 розмір кімнати = підходить 256 кількість грошей
9 розмір кімнати = підходить 512 кількість грошей
10 розмір кімнати = підходить 1024 кількість грошей
11 розмір кімнати = підходить 2,048 кількість грошей
. ..
розмір 16-ї кімнати = підходить 65,536 кількість грошей
...
32-й розмір кімнати = підходить 4,294,967,296 кількість грошей
...
64-й розмір кімнати = підходить 18,446,744,073,709,551,616 кількість грошей

Чому це краще? Тому що, схоже, вона росте повільно на початку, а швидше пізніше, тобто порівняно з обсягом пам'яті в нашій ОЗУ.

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

Однак в останньому випадку потужність 2, вона зростає в межах нашої ОЗУ. Отже, збільшуючи потужність у 2, обидва аналізи «Armotized» залишаються постійними, і це сприятливо для обмеженої оперативної пам’яті, яку ми маємо на сьогодні.


2
Так, це O (найгірший випадок / # операцій). Мені подобається ця відповідь найкраще.
ядерніт

1

Пояснення вище стосуються агрегованого аналізу, ідеї взяття "середнього" за кілька операцій. Я не впевнений, як вони застосовуються до методів Банкіра або Методів фізики амортизованого аналізу.

Тепер. Я не точно впевнений у правильній відповіді. Але це стосується принципової умови обох методів Фізика + Банкіра:

(Сума амортизованої вартості операцій)> = (сума фактичної вартості операцій).

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

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

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

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

АБО

У нас може бути ще одна гіпотеза, що причини амортизованих витрат такі:

  1. Я знаю, що дорогої операції передують МНОГО ОПИСАННЯ операцій.

  2. Для аналізу я збираюся переплатити деякі недорогі операції, ТАКІ, ЩО ЇХ АСИМПТОТИЧНІ ВАРТІСТЬ НЕ ЗМІНІ.

  3. Завдяки цим збільшенням низьких витрат операцій, я можу довести, що ЕКСПЕРСИВНА ОПЕРАЦІЯ має меншу асимптотичну вартість.

  4. Таким чином, я покращив / зменшив АСИМПТОТИЧНИЙ БУНД вартості російських операцій.

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


Цікаві думки.
Lonnie Best

0

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

Отже, суть функції, яка виконується в Constant Amortized Timeтому, полягає в тому, що цей "середній час" досягає межі, яка не перевищується, оскільки кількість викликів продовжує збільшуватися. Будь-який конкретний дзвінок може відрізнятись за ефективністю, але в довгостроковій перспективі цей середній час не буде рости все більшим і більшим.

У цьому головна заслуга чогось, що виконує Constant Amortized Time.

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