Счётчик Кнута — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Доказательство)
Строка 3: Строка 3:
 
''Избыточная двоичная система счисления'' - двоичная система счисления, где кроме 0 и 1 допустима 2 в записи числа.
 
''Избыточная двоичная система счисления'' - двоичная система счисления, где кроме 0 и 1 допустима 2 в записи числа.
 
== Алгоритм ==
 
== Алгоритм ==
Имеется число записанное в избыточной двоичной системе счисления, необходимо добавить 1 к какому-либо разряду. Будем поддерживать следующий инвариант: в следующем старшем за любой двойкой разряде всегда стоит 0.
+
Имеется число, записанное в избыточной двоичной системе счисления, необходимо добавить 1 к какому-либо разряду. Будем поддерживать следующий инвариант: в следующем за любой двойкой разряде всегда стоит 0.
* Начало.
+
 
* '''Шаг 1а'''. Если 1 нужно добавить к 2, то в текущем разряде установить 1, в следующем 1.
+
Разберем случаи добавления единицы:
* '''Шаг 1б'''. Если 1 нужно добавить к 1 и в следующем разряде 0, то в текущем разряде установить 2.
+
* А) Если 1 нужно добавить к 2, то в текущем разряде установить 1, в следующем 1.
* '''Шаг 1в'''. Если 1 нужно добавить к 1 и в следующем разряде 1, то в текущем разряде установить 0, повторить алгоритм для следующего разряда.
+
* Б) Если 1 нужно добавить к 1 и в следующем разряде 0, то в текущем разряде установить 2.
* '''Шаг 1г'''. Если 1 нужно добавить к 1 и в следующем разряде 2, то в следующем за двойкой разряде установить 1, в разряде с двойкой установить 1, в текущем установить 0.
+
* В) Если 1 нужно добавить к 1 и в следующем разряде 1, то в текущем разряде установить 0, повторить алгоритм для следующего разряда.
* '''Шаг 1д'''. Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 0, то в предыдущем разряде установить 0, в текущем 2.
+
* Г) Если 1 нужно добавить к 1 и в следующем разряде 2, то в следующем за двойкой разряде установить 1, в разряде с двойкой установить 1, в текущем установить 0.
* '''Шаг 1е'''. Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 1, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.
+
* Д) Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 0, то в предыдущем разряде установить 0, в текущем 2.
* '''Шаг 1ж'''. Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 2, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.
+
* Е) Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 1, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.
* '''Шаг 1з'''. Если 1 нужно добавить к 0 и в предыдущем разряде не 2, установить в текущем разряде 1.
+
* Ж) Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 2, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.
 +
* З) Если 1 нужно добавить к 0 и в предыдущем разряде не 2, установить в текущем разряде 1.
 
* Конец.
 
* Конец.
 
== Доказательство ==
 
== Доказательство ==
 
Покажем, что инвариант не нарушится.
 
Покажем, что инвариант не нарушится.
  
Инвариант можно нарушить в 2 случаях, когда к единице прибавляют 1, и в следующем разряде стоит не 0, и когда к нулю прибавляется 1, а в предыдущем разряде стоит 2.
+
Инвариант можно нарушить в 2 случаях: когда к единице прибавляют 1, и в следующем разряде стоит не 0, и когда к нулю прибавляется 1, а в предыдущем разряде стоит 2.
  
В первом случае, если в следующем разряде 1, алгоритм будет устанавливать в текущий разряд 0 и запускаться от следующего разряда. Если в следующем разряде 2, то, согласно инварианту, следующий за 2 разряд 0, и установив в следующий за двойкой и разряд с двойкой единцы инвариант не нарушится.
+
В первом случае, если в следующем разряде 1, алгоритм будет устанавливать в текущий разряд 0 и запускаться от следующего разряда. Если в следующем разряде 2, то, согласно инварианту, следующий за 2 разряд 0, и установив в следующий за двойкой и в разряд с двойкой единицы инвариант не нарушится.
  
 
Во втором случае, если в следующем разряде 0, то предыдущий установится в 0, текущий в 2 и так как в следующем 0 инвариант не нарушится. Если в следующем разряде не 0, то предыдущий установится в 0 и алгоритм запустится от следующего.
 
Во втором случае, если в следующем разряде 0, то предыдущий установится в 0, текущий в 2 и так как в следующем 0 инвариант не нарушится. Если в следующем разряде не 0, то предыдущий установится в 0 и алгоритм запустится от следующего.
Строка 26: Строка 27:
 
Рассмотрим пример для каждого варианта, добавление 1 происходит в 3 разряд.
 
Рассмотрим пример для каждого варианта, добавление 1 происходит в 3 разряд.
  
а) <tex>002_{2} \Rightarrow 0011_{2}</tex>
+
а) <tex>200_{2} \Rightarrow 1100_{2}</tex>
  
б) <tex>0010_{2} \Rightarrow 0020_{2}</tex>
+
б) <tex>0100_{2} \Rightarrow 0200_{2}</tex>
  
в) <tex>0011_{2} \Rightarrow 0001_{2}</tex> повторение алгоритма для 4 разряда, по варианту б <tex>0001_{2} \Rightarrow 0002_{2}</tex>
+
в) <tex>1100_{2} \Rightarrow 1000_{2}</tex> повторение алгоритма для 4 разряда, по варианту б <tex>1000_{2} \Rightarrow 2000_{2}</tex>
  
г) <tex>0012_{2} \Rightarrow 00011_{2}</tex>
+
г) <tex>2100_{2} \Rightarrow 11000_{2}</tex>
  
д) <tex>02001_{2} \Rightarrow 00201_{2}</tex>
+
д) <tex>10020_{2} \Rightarrow 10200_{2}</tex>
  
е) <tex>0201_{2} \Rightarrow 0001_{2}</tex> повторение алгоритма для 4 разряда, по варианту б <tex>0001_{2} \Rightarrow 0002_{2}</tex>
+
е) <tex>1020_{2} \Rightarrow 1000_{2}</tex> повторение алгоритма для 4 разряда, по варианту б <tex>1000_{2} \Rightarrow 2000_{2}</tex>
  
ж) <tex>0202_{2} \Rightarrow 0002_{2}</tex> повторение алгоритма для 4 разряда, по варианту а <tex>0002_{2} \Rightarrow 00011_{2}</tex>
+
ж) <tex>2020_{2} \Rightarrow 2000_{2}</tex> повторение алгоритма для 4 разряда, по варианту а <tex>2000_{2} \Rightarrow 11000_{2}</tex>
  
з) <tex>010_{2} \Rightarrow 011_{2}</tex>  
+
з) <tex>010_{2} \Rightarrow 110_{2}</tex>  
  
 
== Амортизационная оценка алгоритма ==
 
== Амортизационная оценка алгоритма ==
Воспользуемся методом предоплаты, будем считать, что каждый раз когда мы начинаем выполнять алгоритм мы берем 2 монетки. Одна будет тратится на изменение текущего разряда и одна запасаться. Таким образом если в разряде стоит 1, то для него запасена 1 монетка, если стоит 2, то запасено 2 монетки. Проверим все варианты.
+
Воспользуемся методом предоплаты. Будем считать, что каждый раз когда мы начинаем выполнять алгоритм мы берем 2 монетки. Одна будет тратится на изменение текущего разряда и одна запасаться. Таким образом, если в разряде стоит 1, то для него запасена 1 монетка, если стоит 2, то запасено 2 монетки. Проверим все варианты.
  
 
а) Так как в текущем разряде было 2, то уже запасено 2 монеты, а так же согласно инварианту после 2 стоит 0, тогда возьмем одну из запасенных у двойки монет и потратим их на присвоение следующему разряду 1, вторую запасенную передадим этой единице. Одну из наших монет потратим на установку в текущий разряд 1, вторую запасем для это единицы.
 
а) Так как в текущем разряде было 2, то уже запасено 2 монеты, а так же согласно инварианту после 2 стоит 0, тогда возьмем одну из запасенных у двойки монет и потратим их на присвоение следующему разряду 1, вторую запасенную передадим этой единице. Одну из наших монет потратим на установку в текущий разряд 1, вторую запасем для это единицы.
Строка 51: Строка 52:
 
в) Так как в текущем разряде было 1, то прибавив наши монеты к запасенной получим 3. Одну потратим, что бы установить 0, оставшиеся 2 потратим на повторение алгоритма для следующего разряда.
 
в) Так как в текущем разряде было 1, то прибавив наши монеты к запасенной получим 3. Одну потратим, что бы установить 0, оставшиеся 2 потратим на повторение алгоритма для следующего разряда.
  
г) Так как в текущем разряде было 1, в следующем 2, то уже запасено 3 монеты и еще 2 наши, тогда 3 монеты потратим, что бы следующему за 2 разряду установить 1, разряду с 2 установить 1, текущему установить 0, останется 2 монеты, которые распределятся между двумя единицами.
+
г) Так как в текущем разряде было 1, в следующем 2, то уже запасено 3 монеты и еще 2 наши. 3 монеты потратим, что бы следующему за 2 разряду установить 1, разряду с 2 установить 1, текущему установить 0. Останется 2 монеты, которые распределятся между двумя единицами.
  
д) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 0, то уже запасено 2 монеты, одну потратим на изменение предыдущего разряда, одну на изменение текущего разряда, наши две монеты запасем для двойки в текущем разряде.
+
д) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 0, то уже запасено 2 монеты. Одну потратим на изменение предыдущего разряда, одну на изменение текущего разряда, наши две монеты запасем для двойки в текущем разряде.
  
е) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 1, то уже запасено 3 монеты, одну потратим на изменение предыдущего разряда, одну сохраним с единицей, наши две монеты потратим на повторение алгоритма на следующем разряде, останется одна лишняя монета.
+
е) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 1, то уже запасено 3 монеты. Одну потратим на изменение предыдущего разряда, одну сохраним с единицей, наши две монеты потратим на повторение алгоритма на следующем разряде. Останется одна лишняя монета.
  
ж) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 2, то уже запасено 4 монеты, одну потратим на изменение предыдущего разряда, две сохраним с двойкой, наши две монеты потратим на повторение алгоритма на следующем разряде, останется одна лишняя монета.
+
ж) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 2, то уже запасено 4 монеты. Одну потратим на изменение предыдущего разряда, две сохраним с двойкой, наши две монеты потратим на повторение алгоритма на следующем разряде. Останется одна лишняя монета.
  
 
з) Одну из монет потратим на изменение разряда, оставшуюся запасем с единицей.
 
з) Одну из монет потратим на изменение разряда, оставшуюся запасем с единицей.

Версия 22:38, 10 июня 2013

Счетчик Кнута (англ. Сounter Knuth) - структура данных, представленная избыточной двоичной системой счисления, где добавление единицы к любому разряду выполняется за [math]O(1)[/math].

Избыточная двоичная система счисления - двоичная система счисления, где кроме 0 и 1 допустима 2 в записи числа.

Алгоритм

Имеется число, записанное в избыточной двоичной системе счисления, необходимо добавить 1 к какому-либо разряду. Будем поддерживать следующий инвариант: в следующем за любой двойкой разряде всегда стоит 0.

Разберем случаи добавления единицы:

  • А) Если 1 нужно добавить к 2, то в текущем разряде установить 1, в следующем 1.
  • Б) Если 1 нужно добавить к 1 и в следующем разряде 0, то в текущем разряде установить 2.
  • В) Если 1 нужно добавить к 1 и в следующем разряде 1, то в текущем разряде установить 0, повторить алгоритм для следующего разряда.
  • Г) Если 1 нужно добавить к 1 и в следующем разряде 2, то в следующем за двойкой разряде установить 1, в разряде с двойкой установить 1, в текущем установить 0.
  • Д) Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 0, то в предыдущем разряде установить 0, в текущем 2.
  • Е) Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 1, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.
  • Ж) Если 1 нужно добавить к 0 и в предыдущем разряде 2, в следующем 2, то в предыдущем разряде установить 0, повторить алгоритм для следующего разряда.
  • З) Если 1 нужно добавить к 0 и в предыдущем разряде не 2, установить в текущем разряде 1.
  • Конец.

Доказательство

Покажем, что инвариант не нарушится.

Инвариант можно нарушить в 2 случаях: когда к единице прибавляют 1, и в следующем разряде стоит не 0, и когда к нулю прибавляется 1, а в предыдущем разряде стоит 2.

В первом случае, если в следующем разряде 1, алгоритм будет устанавливать в текущий разряд 0 и запускаться от следующего разряда. Если в следующем разряде 2, то, согласно инварианту, следующий за 2 разряд 0, и установив в следующий за двойкой и в разряд с двойкой единицы инвариант не нарушится.

Во втором случае, если в следующем разряде 0, то предыдущий установится в 0, текущий в 2 и так как в следующем 0 инвариант не нарушится. Если в следующем разряде не 0, то предыдущий установится в 0 и алгоритм запустится от следующего.

Пример

Рассмотрим пример для каждого варианта, добавление 1 происходит в 3 разряд.

а) [math]200_{2} \Rightarrow 1100_{2}[/math]

б) [math]0100_{2} \Rightarrow 0200_{2}[/math]

в) [math]1100_{2} \Rightarrow 1000_{2}[/math] повторение алгоритма для 4 разряда, по варианту б [math]1000_{2} \Rightarrow 2000_{2}[/math]

г) [math]2100_{2} \Rightarrow 11000_{2}[/math]

д) [math]10020_{2} \Rightarrow 10200_{2}[/math]

е) [math]1020_{2} \Rightarrow 1000_{2}[/math] повторение алгоритма для 4 разряда, по варианту б [math]1000_{2} \Rightarrow 2000_{2}[/math]

ж) [math]2020_{2} \Rightarrow 2000_{2}[/math] повторение алгоритма для 4 разряда, по варианту а [math]2000_{2} \Rightarrow 11000_{2}[/math]

з) [math]010_{2} \Rightarrow 110_{2}[/math]

Амортизационная оценка алгоритма

Воспользуемся методом предоплаты. Будем считать, что каждый раз когда мы начинаем выполнять алгоритм мы берем 2 монетки. Одна будет тратится на изменение текущего разряда и одна запасаться. Таким образом, если в разряде стоит 1, то для него запасена 1 монетка, если стоит 2, то запасено 2 монетки. Проверим все варианты.

а) Так как в текущем разряде было 2, то уже запасено 2 монеты, а так же согласно инварианту после 2 стоит 0, тогда возьмем одну из запасенных у двойки монет и потратим их на присвоение следующему разряду 1, вторую запасенную передадим этой единице. Одну из наших монет потратим на установку в текущий разряд 1, вторую запасем для это единицы.

б) Так как в текущем разряде было 1, то прибавим наши монеты к уже запасенной от единицы. Одну потратим, что бы установить 2, останется 2 монеты.

в) Так как в текущем разряде было 1, то прибавив наши монеты к запасенной получим 3. Одну потратим, что бы установить 0, оставшиеся 2 потратим на повторение алгоритма для следующего разряда.

г) Так как в текущем разряде было 1, в следующем 2, то уже запасено 3 монеты и еще 2 наши. 3 монеты потратим, что бы следующему за 2 разряду установить 1, разряду с 2 установить 1, текущему установить 0. Останется 2 монеты, которые распределятся между двумя единицами.

д) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 0, то уже запасено 2 монеты. Одну потратим на изменение предыдущего разряда, одну на изменение текущего разряда, наши две монеты запасем для двойки в текущем разряде.

е) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 1, то уже запасено 3 монеты. Одну потратим на изменение предыдущего разряда, одну сохраним с единицей, наши две монеты потратим на повторение алгоритма на следующем разряде. Останется одна лишняя монета.

ж) Так как в текущем разряде было 0, в предыдущем разряде 2, в следующем 2, то уже запасено 4 монеты. Одну потратим на изменение предыдущего разряда, две сохраним с двойкой, наши две монеты потратим на повторение алгоритма на следующем разряде. Останется одна лишняя монета.

з) Одну из монет потратим на изменение разряда, оставшуюся запасем с единицей.

Получается 2 монет достаточно для прибавления 1 к любому разряду, тогда наш алгоритм работает в среднем за [math]O(1)[/math]

Смотри также