Счётчик Кнута — различия между версиями
(Доп. случай) |
м (rollbackEdits.php mass rollback) |
||
| (не показано 16 промежуточных версий 8 участников) | |||
| Строка 1: | Строка 1: | ||
| − | '''Счетчик Кнута''' (англ. ''Knuth's Counter'') {{---}} структура данных, представленная избыточной двоичной системой счисления, | + | {{Определение |
| + | |id=knuth_counter | ||
| + | |definition= '''Счетчик Кнута''' (англ. ''Knuth's Counter'') {{---}} структура данных, представленная избыточной двоичной системой счисления, в которой добавление единицы к числу и вычитание единицы выполняется за <tex>O(1)</tex>. | ||
| + | }} | ||
| − | Неотрицательное целое число <tex>N</tex> в ''избыточной двоичной системе счисления'' записывается в виде последовательности разрядов <tex>d_n d_{n-1} \dotsc d_2 d_1</tex>, где <tex>n</tex> обозначает количество разрядов в числе, <tex>d_i</tex> {{---}} <tex>i</tex>–й разряд числа <tex>(1 \leqslant i \leqslant n)</tex>, причем <tex>d_i \in \{0,1,2\}</tex> и <tex>\sum\limits_{i=1}^n d_i \cdot 2^i = N. | + | {{Определение |
| + | |id=knuth_counter | ||
| + | |definition= Неотрицательное целое число <tex>N</tex> в '''избыточной двоичной системе счисления''' записывается в виде последовательности разрядов <tex>(d_n d_{n-1} \dotsc d_2 d_1)</tex>, где <tex>n</tex> обозначает количество разрядов в числе, <tex>d_i</tex> {{---}} <tex>i</tex>–й разряд числа <tex>(1 \leqslant i \leqslant n)</tex>, причем <tex>d_i \in \{0,1,2\}</tex> и <tex>\sum\limits_{i=1}^n d_i \cdot 2^{i-1} = N. | ||
</tex> | </tex> | ||
| + | }} | ||
| − | + | Заметим, что в этой системе представление числа неоднозначно, например представление <tex>212</tex> эквивалентно <tex>1100</tex>. | |
| − | + | == Счетчик Кнута == | |
| − | # Найти младший разряд <tex>d_i</tex> равный <tex>2</tex> и, если таковой имеется, заменить последовательность <tex>d_{i+1}d_i</tex> на <tex>(d_{i+1}+1)0</tex> | + | ==== Описание операции инкремента ==== |
| + | |||
| + | Оригинальный метод предложен Кнутом и состоит из двух действий: | ||
| + | |||
| + | # Найти младший разряд <tex>d_i</tex> равный <tex>2</tex> и, если таковой имеется, заменить последовательность <tex>(\dotsc d_{i+1}d_i \dotsc)</tex> на <tex>(\dotsc (d_{i+1}+1)0 \dotsc)</tex> | ||
# Заменить <tex>d_1</tex> на <tex>d_1+1</tex>. | # Заменить <tex>d_1</tex> на <tex>d_1+1</tex>. | ||
| − | Чтобы достичь необходимой оценки и не выполнять каждый раз поиск для первого правила, можно хранить односвязный список позиций двоек в числе. Тогда чтобы найти младший разряд равный двум нужно просто взять первый элемент списка. Также, непосредственно перед изменением значений разрядов | + | Чтобы достичь необходимой оценки и не выполнять каждый раз поиск для первого правила, можно хранить односвязный [[Список|список]] позиций двоек в числе. Тогда, чтобы найти младший разряд равный двум, нужно просто взять первый элемент списка. Также, непосредственно перед изменением значений разрядов, необходимо выполнять следующие дополнительные действия: |
# Если <tex>d_{i+1}=1</tex>, то заменить первый элемент списка с <tex>i</tex> на <tex>i+1</tex>, иначе удалить его. | # Если <tex>d_{i+1}=1</tex>, то заменить первый элемент списка с <tex>i</tex> на <tex>i+1</tex>, иначе удалить его. | ||
# Если <tex>d_1=1</tex>, то добавить в начало списка <tex>1</tex>. | # Если <tex>d_1=1</tex>, то добавить в начало списка <tex>1</tex>. | ||
| − | Проблемой может оказаться появление двух последовательных двоек, при этом | + | ==== Инвариант с нулем ==== |
| − | тройку. То есть недопустима следующая ситуация <tex>\dotsc 22\dotsc \ | + | |
| − | В свою очередь такая ситуация получается из этой <tex>\dotsc 212\dotsc \ | + | Проблемой может оказаться появление двух последовательных двоек, при этом первое правило может породить |
| + | тройку. То есть недопустима следующая ситуация: | ||
| + | |||
| + | <tex>(\dotsc 22\dotsc) \overset{Inc} {\longmapsto} (\dotsc 30\dotsc)</tex>. | ||
| + | |||
| + | В свою очередь такая ситуация получается из этой: | ||
| + | |||
| + | <tex>(\dotsc 212\dotsc) \overset{Inc} {\longmapsto} (\dotsc 220\dotsc)</tex> | ||
| + | |||
Причем количество единиц между двойками может быть любое, в итоге это приведет к появлению тройки. | Причем количество единиц между двойками может быть любое, в итоге это приведет к появлению тройки. | ||
| − | Однако если между любой парой двоек всегда будет | + | Однако если между любой парой двоек всегда будет находиться хотя бы один |
| − | <tex>0</tex>, то такой ситуации не возникнет. Покажем, | + | <tex>0</tex>, то такой ситуации не возникнет. Покажем, что этот инвариант |
поддерживается после инкремента, рассмотрев возможные ситуации: | поддерживается после инкремента, рассмотрев возможные ситуации: | ||
| − | + | : Число двоек не изменяется | |
| − | + | :: <tex>(\dotsc 2\dotsc 0\dotsc 12\dotsc 0) \overset{Inc} {\longmapsto} (\dotsc 2\dotsc 0\dotsc 20\dotsc 1)</tex>. | |
| − | + | :: <tex>(\dotsc 02\dotsc 1) \overset{Inc} {\longmapsto} (\dotsc 10\dotsc 2)</tex>. | |
| − | + | :: <tex>(\dotsc 2\dotsc 02\dotsc 1) \overset{Inc} {\longmapsto} (\dotsc 2\dotsc 10\dotsc 2)</tex> (частный случай предыдущего). | |
| − | + | :: <tex>(\dotsc 12) \overset{Inc} {\longmapsto} (\dotsc 21)</tex>. | |
| − | + | : Пропадает одна двойка | |
| − | + | :: <tex>(\dotsc 02\dotsc 0) \overset{Inc} {\longmapsto} (\dotsc 10\dotsc 1)</tex>. | |
| − | + | :: <tex>(\dotsc 02) \overset{Inc} {\longmapsto} (\dotsc 11)</tex>. | |
| − | + | : Появление новой двойки | |
| − | + | :: <tex>(\dotsc 1) \overset{Inc} {\longmapsto} (\dotsc 2)</tex> (имеется в виду появление единственной двойки). | |
| − | + | :: <tex>(\dotsc 12\dotsc 1) \overset{Inc} {\longmapsto} (\dotsc 20\dotsc 2)</tex>. | |
| − | + | :: <tex>(\dotsc 2\dotsc 0\dotsc 12\dotsc 1) \overset{Inc} {\longmapsto} (\dotsc 2\dotsc 0\dotsc 20\dotsc 2)</tex> (частный случай предыдущего). | |
| + | |||
| + | Таким образом мы видим, что <tex>0</tex> всегда сохраняется. | ||
| − | + | ==== Пример ==== | |
| − | В таблице можно увидеть как будет | + | В таблице можно увидеть как будет изменяться представление при применении данных правил десять раз к нулю (представления чисел от <tex>0</tex> до <tex>9</tex>): |
{| class="wikitable" | {| class="wikitable" | ||
| Строка 78: | Строка 98: | ||
== Обобщение на системы с произвольным основанием == | == Обобщение на системы с произвольным основанием == | ||
| − | В общем случае | + | {{Определение |
| − | но основание системы может быть произвольным, то есть <tex>d_i \in \{0,1,\dotsc ,b\}</tex> и <tex>\sum\limits_{i=1}^n d_i \cdot b^i = N</tex> | + | |id=b_ary_rr |
| − | , где <tex>b</tex> {{---}} основание. | + | |definition=В общем случае подобное представление называется '''<tex>b</tex>-ричным избыточным представлением''' ('''ИП''', англ. ''b-ary redundant representation''), которое похоже на представление в счетчике Кнута, но основание системы может быть произвольным, то есть <tex>d_i \in \{0,1,\dotsc ,b\}</tex> и <tex>\sum\limits_{i=1}^n d_i \cdot b^i = N</tex>, где <tex>b</tex> {{---}} основание. Оно позволяет прибавить единицу к любому разряду, то есть увеличить число на <tex>b^i</tex> за <tex>O(1)</tex> |
| − | за <tex>O(1)</tex> | + | }} |
| − | Назовем | + | |
| − | <tex>b-1</tex>. | + | {{Определение |
| − | Операция ''исправления'' (''fix'') разряда <tex>d_i=b</tex> в | + | |id=regular_rr |
| − | + | |definition= Назовем представление '''регулярным''' (англ. ''regular''), если между двумя разрядами равными <tex>b</tex> есть хотя бы один разряд отличный от <tex>b-1</tex>. | |
| − | <tex>d_i</tex> в <tex>0</tex>, | + | }} |
| − | что и <tex>d</tex>. | + | |
| − | Чтобы добавить <tex>1</tex> к разряду <tex>d_i</tex> регулярного | + | {{Определение |
| − | нужно | + | |id=fixup |
| + | |definition= Операция '''исправления''' (англ. ''fix'') разряда <tex>d_i=b</tex> в регулярном ИП увеличивает <tex>d_{i+1}</tex> на <tex>1</tex> и устанавливает <tex>d_i</tex> в <tex>0</tex>, образуя новое регулярное ИП, представляющее то же число, что и <tex>d</tex>. | ||
| + | }} | ||
| + | |||
| + | Чтобы добавить <tex>1</tex> к разряду <tex>d_i</tex> регулярного ИП <tex>d</tex>, | ||
| + | нужно выполнить следующие действия: | ||
# Если <tex>d_i=b</tex>, исправить <tex>d_i</tex>. | # Если <tex>d_i=b</tex>, исправить <tex>d_i</tex>. | ||
# Если <tex>d_i=b-1</tex> и самый младший значащий разряд <tex>d_j</tex>, такой, что <tex>j>i</tex> и <tex>d_j \ne b-1</tex>, равен <tex>b</tex> (т.е. <tex>d_j=b</tex>), применить операцию исправления к разряду <tex>d_j</tex>. | # Если <tex>d_i=b-1</tex> и самый младший значащий разряд <tex>d_j</tex>, такой, что <tex>j>i</tex> и <tex>d_j \ne b-1</tex>, равен <tex>b</tex> (т.е. <tex>d_j=b</tex>), применить операцию исправления к разряду <tex>d_j</tex>. | ||
| − | # Добавить 1 к <tex>d_i</tex>. | + | # Добавить <tex>1</tex> к <tex>d_i</tex>. |
# Если <tex>d_i=b</tex>, исправить <tex>d_i</tex>. | # Если <tex>d_i=b</tex>, исправить <tex>d_i</tex>. | ||
| − | Для реализации данной схемы | + | Для реализации данной схемы мы используем односвязный список разрядов от младших |
к старшим. В дополнение каждый разряд <tex>d_i</tex> равный <tex>b-1</tex> | к старшим. В дополнение каждый разряд <tex>d_i</tex> равный <tex>b-1</tex> | ||
будет иметь указатель на самый младший разряд <tex>d_j</tex>, такой, | будет иметь указатель на самый младший разряд <tex>d_j</tex>, такой, | ||
что <tex>j>i</tex> и <tex>d_j \ne b-1</tex>, если он равен <tex>b</tex>, | что <tex>j>i</tex> и <tex>d_j \ne b-1</tex>, если он равен <tex>b</tex>, | ||
иначе этот указатель будет на произвольный разряд <tex>d_j</tex> (<tex>j>i</tex>). | иначе этот указатель будет на произвольный разряд <tex>d_j</tex> (<tex>j>i</tex>). | ||
| − | Теперь | + | Теперь во время увеличения разряда <tex>d_i</tex> на <tex>1</tex> будем проверять |
разряд по указателю вперед (п. 2). | разряд по указателю вперед (п. 2). | ||
| − | Такое представление позволяет | + | Такое представление позволяет увеличивать произвольный разряд на единицу за константное |
время. Обновление указателя вперед происходит следующим образом: когда <tex>d_{i+}</tex> становится равен <tex>b-1</tex> при исправлении разряда <tex>d_{i-1}</tex>, устанавливаем указатель вперед разряда <tex>d_{i}</tex> на <tex>d_{i+1}</tex>, если <tex>d_{i+1}=b</tex>, либо копируем указатель вперед из <tex>d_{i+1}</tex> в <tex>d_{i}</tex>, если <tex>d_{i+1}=b-1</tex>. | время. Обновление указателя вперед происходит следующим образом: когда <tex>d_{i+}</tex> становится равен <tex>b-1</tex> при исправлении разряда <tex>d_{i-1}</tex>, устанавливаем указатель вперед разряда <tex>d_{i}</tex> на <tex>d_{i+1}</tex>, если <tex>d_{i+1}=b</tex>, либо копируем указатель вперед из <tex>d_{i+1}</tex> в <tex>d_{i}</tex>, если <tex>d_{i+1}=b-1</tex>. | ||
При собственно добавлении единицы к разряду <tex>d_i</tex>, также необходимо обновлять его указатель вперед аналогичным образом, | При собственно добавлении единицы к разряду <tex>d_i</tex>, также необходимо обновлять его указатель вперед аналогичным образом, | ||
если этот разряд становится равен <tex>b-1</tex>. | если этот разряд становится равен <tex>b-1</tex>. | ||
| + | |||
| + | == См. также == | ||
| + | * [[Амортизационный анализ]] | ||
| + | * [[Представление целых чисел: прямой код, код со сдвигом, дополнительный код|Представление целых чисел]] | ||
== Источники информации == | == Источники информации == | ||
| Строка 113: | Строка 142: | ||
* G. S. Brodal. Worst case priority queues. ''Proc. 7th annual ACM-SIAM Symposium on Discrete Algorithms (SODA 96)'', страницы 52-58. ACM Press, 1996. | * G. S. Brodal. Worst case priority queues. ''Proc. 7th annual ACM-SIAM Symposium on Discrete Algorithms (SODA 96)'', страницы 52-58. ACM Press, 1996. | ||
* H. Kaplan и R. E. Tarjan. Purely functional representations of catenable sorted lists. ''Proceedings of the 28th Annual ACM Symposium of Computing'', страницы 202-211. ACM Press, 1996 | * H. Kaplan и R. E. Tarjan. Purely functional representations of catenable sorted lists. ''Proceedings of the 28th Annual ACM Symposium of Computing'', страницы 202-211. ACM Press, 1996 | ||
| − | * http://www.cphstl.dk/Paper/Numeral-systems/mfcs-13.pdf | + | * [http://www.cphstl.dk/Paper/Numeral-systems/mfcs-13.pdf In-Place Binary Counter] |
| − | |||
| − | |||
| − | |||
[[Категория: Дискретная математика и алгоритмы]] | [[Категория: Дискретная математика и алгоритмы]] | ||
[[Категория: Амортизационный анализ]] | [[Категория: Амортизационный анализ]] | ||
Текущая версия на 19:13, 4 сентября 2022
| Определение: |
| Счетчик Кнута (англ. Knuth's Counter) — структура данных, представленная избыточной двоичной системой счисления, в которой добавление единицы к числу и вычитание единицы выполняется за . |
| Определение: |
| Неотрицательное целое число в избыточной двоичной системе счисления записывается в виде последовательности разрядов , где обозначает количество разрядов в числе, — –й разряд числа , причем и |
Заметим, что в этой системе представление числа неоднозначно, например представление эквивалентно .
Содержание
Счетчик Кнута
Описание операции инкремента
Оригинальный метод предложен Кнутом и состоит из двух действий:
- Найти младший разряд равный и, если таковой имеется, заменить последовательность на
- Заменить на .
Чтобы достичь необходимой оценки и не выполнять каждый раз поиск для первого правила, можно хранить односвязный список позиций двоек в числе. Тогда, чтобы найти младший разряд равный двум, нужно просто взять первый элемент списка. Также, непосредственно перед изменением значений разрядов, необходимо выполнять следующие дополнительные действия:
- Если , то заменить первый элемент списка с на , иначе удалить его.
- Если , то добавить в начало списка .
Инвариант с нулем
Проблемой может оказаться появление двух последовательных двоек, при этом первое правило может породить тройку. То есть недопустима следующая ситуация:
.
В свою очередь такая ситуация получается из этой:
Причем количество единиц между двойками может быть любое, в итоге это приведет к появлению тройки. Однако если между любой парой двоек всегда будет находиться хотя бы один , то такой ситуации не возникнет. Покажем, что этот инвариант поддерживается после инкремента, рассмотрев возможные ситуации:
- Число двоек не изменяется
- .
- .
- (частный случай предыдущего).
- .
- Пропадает одна двойка
- .
- .
- Появление новой двойки
- (имеется в виду появление единственной двойки).
- .
- (частный случай предыдущего).
Таким образом мы видим, что всегда сохраняется.
Пример
В таблице можно увидеть как будет изменяться представление при применении данных правил десять раз к нулю (представления чисел от до ):
| Шаг | Представление |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 2 | 2 |
| 3 | 11 |
| 4 | 12 |
| 5 | 21 |
| 6 | 102 |
| 7 | 111 |
| 8 | 112 |
| 9 | 121 |
Обобщение на системы с произвольным основанием
| Определение: |
| В общем случае подобное представление называется -ричным избыточным представлением (ИП, англ. b-ary redundant representation), которое похоже на представление в счетчике Кнута, но основание системы может быть произвольным, то есть и , где — основание. Оно позволяет прибавить единицу к любому разряду, то есть увеличить число на за |
| Определение: |
| Назовем представление регулярным (англ. regular), если между двумя разрядами равными есть хотя бы один разряд отличный от . |
| Определение: |
| Операция исправления (англ. fix) разряда в регулярном ИП увеличивает на и устанавливает в , образуя новое регулярное ИП, представляющее то же число, что и . |
Чтобы добавить к разряду регулярного ИП ,
нужно выполнить следующие действия:
- Если , исправить .
- Если и самый младший значащий разряд , такой, что и , равен (т.е. ), применить операцию исправления к разряду .
- Добавить к .
- Если , исправить .
Для реализации данной схемы мы используем односвязный список разрядов от младших к старшим. В дополнение каждый разряд равный будет иметь указатель на самый младший разряд , такой, что и , если он равен , иначе этот указатель будет на произвольный разряд (). Теперь во время увеличения разряда на будем проверять разряд по указателю вперед (п. 2).
Такое представление позволяет увеличивать произвольный разряд на единицу за константное время. Обновление указателя вперед происходит следующим образом: когда становится равен при исправлении разряда , устанавливаем указатель вперед разряда на , если , либо копируем указатель вперед из в , если . При собственно добавлении единицы к разряду , также необходимо обновлять его указатель вперед аналогичным образом, если этот разряд становится равен .
См. также
Источники информации
- H. Kaplan и R. E. Tarjan. New heap data structures. 1998
- M. J. Clancy и D. E. Knuth. A programming and problem-solving seminar. Technical Report STAN-CS-77-606, Department of Computer Sciencr, Stanford University, Palo Alto, 1977.
- G. S. Brodal. Worst case priority queues. Proc. 7th annual ACM-SIAM Symposium on Discrete Algorithms (SODA 96), страницы 52-58. ACM Press, 1996.
- H. Kaplan и R. E. Tarjan. Purely functional representations of catenable sorted lists. Proceedings of the 28th Annual ACM Symposium of Computing, страницы 202-211. ACM Press, 1996
- In-Place Binary Counter