Let's begin by "normalizing" the function in the same way as in your previous question, noting that once again all changes in a
and stopping conditions are proportional to 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
}
Unfortunately, this is where the similarity ends...
Snippet 1)
Note that m
can be written as an integer power of 2, since it doubles every loop:
i = 0
while (n > pow(2, i)) {
i++
}
// m = pow(2, i)
From the stopping condition:

Snippet 2)
Here m
decreases in the exact opposite way to 1), so it can again be written as a power of 2:
// using i from the end of 1)
while (n>=1) {
k = pow(2, i)
while (n >= k) {
n = n - k
}
i--
}
The inner loop is simpler than the inner loop from your previous question, because m
does not change inside it. It is easy to deduce the number of times c
it executes, and the value of n
at the end:

This is the exact definition of the Modulus operator %
in the "C-family" of languages:
while (n>=1) {
k = pow(2, i)
n = n % k // time complexity O(n / k) here instead of O(1)
i--
}
Note that, because consecutive values of k
only differ by a factor of 2, at no point will the value of n
be greater than or equal to 2k
; this means that the inner loop executes at most once per outer loop. Therefore the outer loop executes at most i
times.
Both the first and second loops are O(log n)
, which means the total time complexity is O(log n) = O(log [a/b])
.
Update: numerical tests in Javascript as before.
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;
}
Plotting T(n)
against log(n)
shows a nice straight line:

Edit: a more thorough explanation of snippet 2).
At the end of snippet 1), the value of i = ceil(log2(n))
represents the number of significant bits in the binary representation of the integer ceil(n)
.
Computing the modulus of an integer with a positive power-of-2 2^i
is equivalent to discarding all but the first i
bits. For example:
n = ...00011111111 (binary)
m = ...00000100000 (= 2^5)
n % m = ...00000011111
----- (5 least significant bits)
The operation of snippet 2) is therefore equivalent to removing the most significant bit of n
, one at a time, until only zero is left. For example:
outer loop no | n
----------------------------
1 | ...110101101
| ^
2 | ...010101101
| ^
3 | ...000101101
| ^
4 | ...000001101
| ^
: | :
: | :
i (=9) | ...000000001
| ^
----------------------------
final | 000000000
When the current most significant bit (pointed to by ^
) is:
- 0: the inner loop does not execute because the value of
n
is already smaller than k = 2^i
(equal to the bit position value of ^
).
- 1: the inner loop executes once because
n
is greater than k
, but less than 2k
(which corresponds the bit above the current position ^
).
Hence the "worst" case occurs when all significant bits of n
are 1, in which case the inner loop to always executes once.
Regardless, the outer loop executes ceil(log2(n))
times for any value of n
.