Давайте начнем с «нормализации» функции так же, как в вашем предыдущем вопросе , отметив, что все изменения в a
и условия остановки снова пропорциональны b
:
n = a/b
// 1)
m = 1
while(n>m){
m = m*2
}
// 2)
while(n>=1){
while(n>=m){
n = n-m
}
m=m/2
}
К сожалению, на этом сходство заканчивается ...
Фрагмент 1)
Обратите внимание, что m
можно записать как целое число 2, поскольку оно удваивает каждый цикл:
i = 0
while (n > pow(2, i)) {
i++
}
// m = pow(2, i)
Из условия остановки:
Фрагмент 2)
Здесь m
уменьшается прямо противоположным образом 1) , поэтому его снова можно записать как степеньиз 2:
// using i from the end of 1)
while (n>=1) {
k = pow(2, i)
while (n >= k) {
n = n - k
}
i--
}
Внутренний цикл проще, чем внутренний цикл из вашего предыдущего вопроса, потому что m
не изменяется внутри него.Легко определить, сколько раз c
он выполняет, а значение n
в конце:
Этоточное определение оператора модуля %
в "семействе C" языков:
while (n>=1) {
k = pow(2, i)
n = n % k // time complexity O(n / k) here instead of O(1)
i--
}
Обратите внимание, что, поскольку последовательные значения k
отличаются только в два раза2, ни при каких условиях значение n
не будет больше или равно 2k
;это означает, что внутренний цикл выполняет самое большее один раз для каждого внешнего цикла.Поэтому внешний цикл выполняется не более i
х .
И первый, и второй циклы O(log n)
, что означает общую сложность времениO(log n) = O(log [a/b])
.
Обновление: числовые тесты в Javascript, как и раньше.
function T(n)
{
let t = 0;
let m = 1;
while (n > m) {
m *= 2; t++;
}
while (n >= 1) {
while (n >= m) {
n -= m; t++;
}
m/=2;
}
return t;
}
График T(n)
против log(n)
показывает хорошую прямую линию:
Редактировать: более подробное объяснение фрагмента 2) .
В конце фрагмента 1) , значение i = ceil(log2(n))
представляет количество значащих бит в двоичном представлении целого числа ceil(n)
.
Вычисление модуля целого числа с положительной степенью-of-2 2^i
эквивалентно отбрасыванию всех, кроме первых i
битов.Например:
n = ...00011111111 (binary)
m = ...00000100000 (= 2^5)
n % m = ...00000011111
----- (5 least significant bits)
Операция фрагмента 2) , таким образом, эквивалентна удалению старшего значащего бита n
, по одному, пока не останется только ноль.Например:
outer loop no | n
----------------------------
1 | ...110101101
| ^
2 | ...010101101
| ^
3 | ...000101101
| ^
4 | ...000001101
| ^
: | :
: | :
i (=9) | ...000000001
| ^
----------------------------
final | 000000000
Когда текущий старший значащий бит (на который указывает ^
) равен:
- 0 : внутренний цикл выполняет не выполняется, потому что значение
n
уже меньше k = 2^i
(равно значению битовой позиции ^
). - 1 : внутреннеецикл выполняется один раз , потому что
n
больше k
, но меньше 2k
(что соответствует биту выше текущей позиции ^
).
Следовательно«худший» случай возникает, когда все значащие биты n
равны 1, и в этом случае внутренний цикл всегда выполняется один раз.
Независимо от этого внешний цикл выполняется ceil(log2(n))
раз для любого значения n
.