Изменения

Перейти к: навигация, поиск

Счётчик Кнута

1279 байт добавлено, 19:13, 4 сентября 2022
м
rollbackEdits.php mass rollback
{{Определение|id=knuth_counter|definition= '''Счетчик Кнута''' (англ. ''Сounter Knuth's Counter'') {{--- }} структура данных, представленная избыточной двоичной системой счисления, где в которой добавление единицы к любому разряду числу и вычитание единицы выполняется за <tex>O(1)</tex>.}}
''Избыточная двоичная система счисления'' - двоичная система счисления, где кроме 0 и 1 допустима 2 в записи числа.{{Определение|id=knuth_counter|definition= Алгоритм ==Имеется Неотрицательное целое число записанное <tex>N</tex> в '''избыточной двоичной системе счисления, необходимо добавить 1 к какому-либо разряду. Будем поддерживать следующий инвариант: в следующем старшем за любой двойкой разряде всегда стоит 0.* Начало.* '''Шаг 1а'''. Если 1 нужно добавить к 2, то записывается в текущем разряде установить виде последовательности разрядов <tex>(d_n d_{n-1} \dotsc d_2 d_1)</tex>, где <tex>n</tex> обозначает количество разрядов в следующем 1.* '''Шаг 1б'''. Если 1 нужно добавить к 1 и в следующем разряде 0числе, то в текущем разряде установить 2.* '''Шаг 1в'''. Если 1 нужно добавить к 1 и в следующем разряде 1, то в текущем разряде установить 0, повторить алгоритм для следующего разряда.* '''Шаг 1г'''. Если 1 нужно добавить к 1 и в следующем разряде 2, то в следующем за двойкой разряде установить 1, в разряде с двойкой установить 1, в текущем установить 0.* '''Шаг 1д'''. Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 0, то в предыдущем разряде установить 0, в текущем 2.* '''Шаг 1е'''. Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем <tex>d_i</tex> {{---}} <tex>i</tex>&ndash;й разряд числа <tex>(1\leqslant i \leqslant n)</tex>, то в предыдущем разряде установить причем <tex>d_i \in \{0, повторить алгоритм для следующего разряда.* '''Шаг 1ж'''. Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 2, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.* '''Шаг 1з'''. Если 1 нужно добавить к 0 \}</tex> и в предыдущем разряде не 2, установить в текущем разряде 1.* Конец.== Доказательство =<tex>\sum\limits_{i=Покажем, что инвариант не нарушится.Инвариант можно нарушить в 2 случаях, когда к единице прибавляют 1 и в следующем разряде стоит не 0, и когда к нулю прибавляется 1, а в предыдущем разряде стоит }^n d_i \cdot 2.В первом случае, если в следующем разряде ^{i-1, алгоритм будет устанавливать в текущий разряд 0 и запускаться от следующего разряда. Если в следующем разряде 2, то согласно инварианту следующий за 2 разряд 0, и установив в следующий за двойкой и разряд с двойкой едины инвариант не нарушится} = N.Во втором случае, если в следующем разряде 0, то предыдущий установится в 0, текущий в 2 и так как в следующем 0 инвариант не нарушится. Если в следующем разряде не 0, то предыдущий установится в 0 и алгоритм запустится от следующего.</tex>== Пример ==Рассмотрим пример для каждого варианта, добавление 1 происходит в 3 разряд.}}
а) Заметим, что в этой системе представление числа неоднозначно, например представление <tex>002_{2} \Rightarrow 0011_{2}212</tex>эквивалентно <tex>1100</tex>.
б) <tex>0010_{2} \Rightarrow 0020_{2}</tex>== Счетчик Кнута ==
в) <tex>0011_{2} \Rightarrow 0001_{2}</tex> повторение алгоритма для 4 разряда, по варианту б <tex>0001_{2} \Rightarrow 0002_{2}</tex>==== Описание операции инкремента ====
г) <tex>0012_{2} \Rightarrow 00011_{2}</tex>Оригинальный метод предложен Кнутом и состоит из двух действий:
д) # Найти младший разряд <tex>d_i</tex> равный <tex>2</tex> и, если таковой имеется, заменить последовательность <tex>02001_(\dotsc d_{2i+1} d_i \Rightarrow 00201_dotsc)</tex> на <tex>(\dotsc (d_{2i+1}+1)0 \dotsc)</tex># Заменить <tex>d_1</tex> на <tex>d_1+1</tex>.
е) <tex>0201_{2} \Rightarrow 0001_{2}</tex> повторение алгоритма Чтобы достичь необходимой оценки и не выполнять каждый раз поиск для 4 разрядапервого правила, по варианту б <tex>0001_{2} \Rightarrow 0002_{2}</tex>можно хранить односвязный [[Список|список]] позиций двоек в числе. Тогда, чтобы найти младший разряд равный двум, нужно просто взять первый элемент списка. Также, непосредственно перед изменением значений разрядов, необходимо выполнять следующие дополнительные действия:
ж) # Если <tex>0202_d_{2} \Rightarrow 0002_{2i+1}=1</tex>, то заменить первый элемент списка с <tex>i</tex> на <tex>i+1</tex>, иначе удалить его.# Если <tex>d_1=1</tex> повторение алгоритма для 4 разряда, по варианту а то добавить в начало списка <tex>0002_{2} \Rightarrow 00011_{2}1</tex>.
з) <tex>010_{2} \Rightarrow 011_{2}</tex> ==== Инвариант с нулем ====
== Амортизационная оценка алгоритма ==Проблемой может оказаться появление двух последовательных двоек, при этом первое правило может породитьВоспользуемся методом предоплаты, будем считать, что каждый раз когда мы начинаем выполнять алгоритм мы берем 2 монетки. Одна будет тратится на изменение текущего разряда и одна запасаться. Таким образом если в разряде стоит 1, то для него запасена 1 монетка, если стоит 2, то запасено 2 монетки. Проверим все вариантытройку.То есть недопустима следующая ситуация:
а<tex>(\dotsc 22\dotsc) Так как в текущем разряде было 2, то уже запасено 2 монеты, а так же согласно инварианту после 2 стоит 0, тогда возьмем одну из запасенных у двойки монет и потратим их на присвоение следующему разряду 1, вторую запасенную передадим этой единице. Одну из наших монет потратим на установку в текущий разряд 1, вторую запасем для это единицы\overset{Inc} {\longmapsto} (\dotsc 30\dotsc)</tex>.
б) Так как в текущем разряде было 1, то прибавим наши монеты к уже запасенной от единицы. Одну потратим, что бы установить 2, останется 2 монеты.В свою очередь такая ситуация получается из этой:
в<tex>(\dotsc 212\dotsc) Так как в текущем разряде было 1, то прибавив наши монеты к запасенной получим 3. Одну потратим, что бы установить 0, оставшиеся 2 потратим на повторение алгоритма для следующего разряда.\overset{Inc} {\longmapsto} (\dotsc 220\dotsc)</tex>
г) Так как в текущем разряде было 1Причем количество единиц между двойками может быть любое, в следующем 2итоге это приведет к появлению тройки.Однако если между любой парой двоек всегда будет находиться хотя бы один<tex>0</tex>, то уже запасено 3 монеты и еще 2 нашитакой ситуации не возникнет. Покажем, тогда 3 монеты потратимчто этот инвариантподдерживается после инкремента, что бы следующему за рассмотрев возможные ситуации:: Число двоек не изменяется:: <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> (частный случай предыдущего).
д) Так как в текущем разряде было 0Таким образом мы видим, в предыдущем разряде 2, в следующем что <tex>0, то уже запасено 2 монеты, одну потратим на изменение предыдущего разряда, одну на изменение текущего разряда, наши две монеты запасем для двойки в текущем разряде</tex> всегда сохраняется.
е) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 1, то уже запасено 3 монеты, одну потратим на изменение предыдущего разряда, одну сохраним с единицей, наши две монеты потратим на повторение алгоритма на следующем разряде, останется одна лишняя монета.==== Пример ====
ж) Так В таблице можно увидеть как в текущем разряде было будет изменяться представление при применении данных правил десять раз к нулю (представления чисел от <tex>0, в предыдущем разряде 2, в следующем 2, то уже запасено 4 монеты, одну потратим на изменение предыдущего разряда, две сохраним с двойкой, наши две монеты потратим на повторение алгоритма на следующем разряде, останется одна лишняя монета.</tex> до <tex>9</tex>):
з) Одну из монет потратим на изменение разряда, оставшуюся запасем с единицей.{| class="wikitable"|-! Шаг! Представление|-| 0 | 0|-| 1 | 1|-| 2 | 2|-| 3 | 11|-| 4 | 12|-| 5 | 21|-| 6 | 102|-| 7 | 111|-| 8 | 112|-| 9 | 121|}
Получается 2 монет достаточно для прибавления 1 к любому разряду, тогда наш алгоритм работает в среднем за <tex>O(1)</tex>== Обобщение на системы с произвольным основанием ==
{{Определение|id=b_ary_rr|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>}} {{Определение|id=regular_rr|definition= Назовем представление '''регулярным''' (англ. ''regular''), если между двумя разрядами равными <tex>b</tex> есть хотя бы один разряд отличный от <tex>b-1</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-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>1</tex> к <tex>d_i</tex>.# Если <tex>d_i=b</tex>, исправить <tex>d_i</tex>. Для реализации данной схемы мы используем односвязный список разрядов от младшихк старшим. В дополнение каждый разряд <tex>d_i</tex> равный <tex>b-1</tex>будет иметь указатель на самый младший разряд <tex>d_j</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_i</tex> на <tex>1</tex> будем проверятьразряд по указателю вперед (п. 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>. == См. также ==
* [[Амортизационный анализ]]
* [[Представление целых чисел: прямой код, код со сдвигом, дополнительный код|Представление целых чисел]] == Источники информации ==* 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* [http://www.cphstl.dk/Paper/Numeral-systems/mfcs-13.pdf In-Place Binary Counter]
[[Категория: Дискретная математика и алгоритмы]]
[[Категория: Амортизационный анализ]]
1632
правки

Навигация