Саморасширяющийся массив — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 6: Строка 6:
 
:В i-ую ячейку массива записывается элемент x. Время выполнения — <tex>O(1)</tex>.
 
:В i-ую ячейку массива записывается элемент x. Время выполнения — <tex>O(1)</tex>.
 
=== add(x) ===
 
=== add(x) ===
:Добавление в массив элемента x. Время выполнения — <tex>O(1)</tex>; в худшем случае, при котором необходимо перенести все элементы из текущего массива в вдвое больший массив — <tex>O(n)</tex> (n — размер массива).
+
:Добавление в массив элемента x. Время выполнения — <tex>O(1)</tex>; в худшем случае, при котором необходимо перенести все элементы из текущего массива в вдвое больший массив — <tex>O(n)</tex> (<tex>n</tex> — размер массива).
=== size ===
+
=== del() ===
 +
:Удаляет последний элемент массива. В случае, если кол-во элементов в массиве в <tex>C</tex> раз меньше его длины, то происходит сжатие в <tex>B</tex> раз. (<tex>C,B</tex>-константы, зависящие от реализации массива). Время выполнения операции в худшем случае - <tex>O(n)</tex>.
 +
=== size() ===
 
:Возвращает количество элементов массива. Время выполнения — <tex>O(1)</tex>.
 
:Возвращает количество элементов массива. Время выполнения — <tex>O(1)</tex>.
  
== Стоимость операции add(x) ==
+
== Амортизационная стоимость каждой операции ==
=== Метод предоплаты ===
+
=== Стоимость операции add(x) ===
 +
==== Метод предоплаты ====
 
[[Файл:Безымянный1.png|400px|thumb|right|Иллюстрация]]
 
[[Файл:Безымянный1.png|400px|thumb|right|Иллюстрация]]
Применим амортизационный анализ (конкретно — метод предоплаты) для того, чтобы доказать, что среднее время работы операции add(x) — <tex>O(1)</tex>.<br>
 
 
Пусть у нас единицей стоимости операции является одна монетка. Тогда при каждой операции add(x), при которой нам не требуется копирование, мы будем использовать три монетки. Из них одна пойдёт на стоимость самой этой операции, а две будут в резерве (пусть, если мы добавили i-ый элемент, мы будем класть по одной монетке к элементам с номерами <tex>i</tex> и <tex>i-\frac{n}{2}</tex>). В итоге, к тому моменту, как массив будет заполнен, рядом с каждым элементом будет лежать по одной монетке, которую мы и можем использовать на его копирование в новый массив. Таким образом, амортизационная стоимость каждой операции add(x) — 3, и среднее время её работы — <tex>O(1)</tex>.
 
Пусть у нас единицей стоимости операции является одна монетка. Тогда при каждой операции add(x), при которой нам не требуется копирование, мы будем использовать три монетки. Из них одна пойдёт на стоимость самой этой операции, а две будут в резерве (пусть, если мы добавили i-ый элемент, мы будем класть по одной монетке к элементам с номерами <tex>i</tex> и <tex>i-\frac{n}{2}</tex>). В итоге, к тому моменту, как массив будет заполнен, рядом с каждым элементом будет лежать по одной монетке, которую мы и можем использовать на его копирование в новый массив. Таким образом, амортизационная стоимость каждой операции add(x) — 3, и среднее время её работы — <tex>O(1)</tex>.
  
=== Метод потенциалов ===
+
==== Метод потенциалов ====
Метод потенциалов является обобщением метода предоплаты: здесь резерв не распределяется между отдельными элементами структуры данных, а является функцией состояния структуры данных в целом. Эта функция называется "потенциалом".
 
 
 
Общая схема метода потенциалов такова.
 
Пусть над структурой данных предстоит произвести n операций, и пусть <tex>\Phi_i</tex> — состояние структуры данных после i-й операции (в частности, <tex>\Phi_0</tex> — исходное состояние).
 
 
 
Потенциал представляет собой функцию из множества возможных состояний структуры данных во множество действительных чисел. Пусть <tex>t_i</tex> — реальная стоимость i-й операции.
 
 
 
Назовем амортизационной стоимостью i-й операции число:
 
 
 
<tex>a_i = t_i + \Phi_i - \Phi_{i-1},</tex>
 
 
 
т. е. сумму реальной стоимости операции плюс приращение потенциала в результате выполнения этой операции.
 
 
 
Теперь докажем с помощью метода потенциалов, что операция add(x) выполняется в среднем за <tex>O(1)</tex>.
 
 
За потенциал примем число:
 
За потенциал примем число:
 
<tex>\Phi(c, s) = 2s - c </tex>,
 
<tex>\Phi(c, s) = 2s - c </tex>,
Строка 43: Строка 31:
  
 
В итоге, средняя стоимость операции add(x) — 3, а среднее время работы — <tex>O(1)</tex>.
 
В итоге, средняя стоимость операции add(x) — 3, а среднее время работы — <tex>O(1)</tex>.
 +
 +
=== Стоимость операции del(x) ===
 +
==== Метод предоплаты ====
 +
Пусть наш массив расширяется в 2 раза, и уменьшается в 2 раза, когда длина массива в 4 раза больше кол-ва элементов в массиве. В этом случае амортизационная стоимость каждой операции будет <tex>O(1)</tex>.
 +
 +
При каждой операции будем использовать три монетки. Одну из них потратим на само удаление элемента, одну монету на элемент, стоящий на позиции <tex>i-\frac{n}{4}</tex>, и одну монетку на место удаленного элемента. Тогда даже в самом худшем случае (только что расширились, а потом <tex>\frac{n}{4}</tex> удалили) у нас будет хватать денег на копирование в новый массив.
 +
==== Метод потенциалов ====
 +
<tex>:(</tex>

Версия 13:10, 11 июня 2012

Операции

Определим операции, которые мы будем применять к саморасширяющемуся массиву:

get(i)

Возвращает значение i-ой ячейки массива. Время выполнения — [math]O(1)[/math].

set(i,x)

В i-ую ячейку массива записывается элемент x. Время выполнения — [math]O(1)[/math].

add(x)

Добавление в массив элемента x. Время выполнения — [math]O(1)[/math]; в худшем случае, при котором необходимо перенести все элементы из текущего массива в вдвое больший массив — [math]O(n)[/math] ([math]n[/math] — размер массива).

del()

Удаляет последний элемент массива. В случае, если кол-во элементов в массиве в [math]C[/math] раз меньше его длины, то происходит сжатие в [math]B[/math] раз. ([math]C,B[/math]-константы, зависящие от реализации массива). Время выполнения операции в худшем случае - [math]O(n)[/math].

size()

Возвращает количество элементов массива. Время выполнения — [math]O(1)[/math].

Амортизационная стоимость каждой операции

Стоимость операции add(x)

Метод предоплаты

Иллюстрация

Пусть у нас единицей стоимости операции является одна монетка. Тогда при каждой операции add(x), при которой нам не требуется копирование, мы будем использовать три монетки. Из них одна пойдёт на стоимость самой этой операции, а две будут в резерве (пусть, если мы добавили i-ый элемент, мы будем класть по одной монетке к элементам с номерами [math]i[/math] и [math]i-\frac{n}{2}[/math]). В итоге, к тому моменту, как массив будет заполнен, рядом с каждым элементом будет лежать по одной монетке, которую мы и можем использовать на его копирование в новый массив. Таким образом, амортизационная стоимость каждой операции add(x) — 3, и среднее время её работы — [math]O(1)[/math].

Метод потенциалов

За потенциал примем число: [math]\Phi(c, s) = 2s - c [/math],

где с - размер массива, s - число элементов массива.

Рассмотрим два случая и посчитаем амортизационной стоимость операции add(x):

1) Массив не расширяется: [math] a_i = t_i + \Phi(c, s + 1) - \Phi(c, s) = 1+ (s + 1) - s = 2[/math]

2) Массив расширяется: [math] a_i = t_i + \Phi(2c, s + 1) - \Phi(c, s) = (s + 1) + 2 - s = 3 [/math]

В итоге, средняя стоимость операции add(x) — 3, а среднее время работы — [math]O(1)[/math].

Стоимость операции del(x)

Метод предоплаты

Пусть наш массив расширяется в 2 раза, и уменьшается в 2 раза, когда длина массива в 4 раза больше кол-ва элементов в массиве. В этом случае амортизационная стоимость каждой операции будет [math]O(1)[/math].

При каждой операции будем использовать три монетки. Одну из них потратим на само удаление элемента, одну монету на элемент, стоящий на позиции [math]i-\frac{n}{4}[/math], и одну монетку на место удаленного элемента. Тогда даже в самом худшем случае (только что расширились, а потом [math]\frac{n}{4}[/math] удалили) у нас будет хватать денег на копирование в новый массив.

Метод потенциалов

[math]:([/math]