Постоянное амортизированное время

Что означает "Постоянное амортизированное время", когда речь идет о временной сложности алгоритма?

Ответ 1

Амортизированное время объясняется простыми словами:

Если вы выполняете операцию, скажем, миллион раз, вы не заботитесь о наихудшем случае или в лучшем случае этой операции - то, о чем вы заботитесь, - это сколько времени занимает общее время, когда вы повторяете операцию миллион раз.

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

Возьмем пример матов динамического массива, к которому вы повторно добавляете новые элементы. Обычно добавление элемента занимает постоянное время (т.е. O(1)). Но каждый раз, когда массив заполнен, вы выделяете в два раза больше места, копируете свои данные в новый регион и освобождаете старое пространство. Предполагая, что выделения и frees выполняются в постоянное время, этот процесс расширения принимает O(n) время, где n - текущий размер массива.

Итак, каждый раз, когда вы увеличиваете, вы занимаете примерно вдвое больше времени, чем последний. Но вы также ждали дважды, прежде чем делать это! Таким образом, стоимость каждого расширения может быть "распространена" среди вставок. Это означает, что в долгосрочной перспективе общее время, затрачиваемое на добавление m элементов в массив, равно O(m), и поэтому амортизированное время (то есть время на вставку) составляет O(1).

Ответ 2

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

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

Или в более строгих терминах

Существует константа c, такая, что для каждая последовательность операций (также одна из которых заканчивается дорогостоящей операцией) длина L, время не больше c * L (Спасибо Rafał Dowgird)

Ответ 3

Чтобы разработать интуитивный способ мышления об этом, рассмотрите возможность вставки элементов в динамический массив (например, std::vector в С++). Позвольте построить график, который показывает зависимость числа операций (Y), необходимых для вставки N элементов в массив:

plot

Вертикальные части черного графа соответствуют перераспределению памяти для расширения массива. Здесь мы видим, что эта зависимость может быть грубо представлена ​​в виде строки. И это линейное уравнение Y=C*N + b (C постоянное, b= 0 в нашем случае). Поэтому мы можем сказать, что нам нужно потратить в среднем C*N на операции, чтобы добавить N элементов в массив, или C*1 для добавления одного элемента (арифметическое постоянное время).

Ответ 4

Ниже объяснение Википедии оказалось полезным, после повторного прочтения 3 раза:

Источник: https://en.wikipedia.org/wiki/Amortized_analysis#Dynamic_Array

"Dynamic Array

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

Рассмотрим динамический массив, размер которого увеличивается по мере добавления к нему большего количества элементов. такой как ArrayList в Java. Если мы начали с динамического массива размером 4, потребовалось бы постоянное время, чтобы протолкнуть на него четыре элемента. Однако добавление пятого элемента в этот массив займет больше времени, так как массив должен был бы создать новый массив двойного текущего размера (8), скопируйте старые элементы в новый массив, а затем добавьте новый элемент. Следующие три операции push будут аналогично принимать постоянные время, а затем последующее добавление потребует еще один медленный удвоение размера массива.

В общем, если мы рассмотрим произвольное количество толчков n в массив размером n, мы замечаем, что операции push занимают постоянное время, кроме для последнего, который занимает O (n) время, чтобы выполнить удвоение размера операция. Поскольку было всего n операций, мы можем взять среднее об этом и найдите, что для вставки элементов в динамический массив принимает: O (n/n) = O (1), постоянное время. "

Насколько я понимаю, как простая история:

Предположим, у вас много денег. И вы хотите сложить их в комнате. И у вас длинные руки и ноги, столько, сколько вам нужно сейчас или в будущем. И вы должны заполнить все в одной комнате, так что это легко запереть.

Итак, вы идете прямо в конец /angular комнаты и начинаете складывать их. Когда вы сложите их, в комнате будет не хватать места. Однако, когда вы заполняли, их было легко сложить. Получил деньги, положил деньги. Легко. Это О (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-й размер комнаты = подходит для подсчета денег
5-й размер комнаты = подходит для подсчета денег
6-й размер комнаты = подходит для подсчета денег
Седьмой размер комнаты = подходит на счет 128 денег
8-й размер комнаты = подходит для подсчета денег
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 остается постоянным, и он благоприятен для ограниченного объема оперативной памяти, имеющегося у нас на сегодняшний день.

Ответ 5

Приведенные выше объяснения относятся к агрегированному анализу, идея принятия "среднего" по нескольким операциям. Я не уверен, как они относятся к методу банкиров или к методам анализа аффицированных физиков.

Теперь. Я не совсем уверен в правильном ответе. Но это связано с принципиальным условием обоих методов "Физики + банкир":

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

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

То есть, когда кто-то дает мне амортизированную стоимость, я знаю, что это не то же самое, что и нормальная-асимптотическая стоимость. Какие выводы я могу извлечь из амортизированной стоимости?

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

Например, для купирования фибоначчи, при расчете амортизируемой стоимости просто уменьшения-ключа, равного O (1), бессмысленно, поскольку затраты снижаются "работой, выполняемой более ранними операциями в увеличении потенциала кучи".

ИЛИ

У нас могла бы быть другая гипотеза о том, что причины об амортизированной стоимости выглядят следующим образом:

  • Я знаю, что дорогой операции будут предшествовать операции MULTIPLE LOW-COST.

  • Для анализа я собираюсь перезарядить некоторые недорогие операции, ТАКИЕ, ЧТО ИХ АСИМПТОТИЧЕСКАЯ СТОИМОСТЬ НЕ ИЗМЕНЯЕТ.

  • Благодаря этим увеличенным недорогим операциям, я могу обеспечить, чтобы дорогостоящая операционная система имела МАСШТАБИРУЮЩУЮ СТОЙКУ.

  • Таким образом, я улучшил/уменьшил ASYMPTOTIC-BOUND стоимости n операций.

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

Ответ 6

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

Итак, суть функции, выполняемой в Constant Amortized Time, заключается в том, что это "среднее время" достигает предела, который не будет превышен, поскольку количество вызовов продолжает увеличиваться. Любой конкретный вызов может отличаться по производительности, но в долгосрочной перспективе это среднее время не будет расти все больше и больше.

Это существенная заслуга чего-то, что выполняет в Constant Amortized Time.