a function to count the step reaching 0
Question:
Given a binary number, I need to write a function to count the total steps reaching zero. The rules are:
- If the number is even, divide it by 2
- If the number is odd, subtract 1 from it
for example, it takes six iterations for "1110" (14) to become 0:
- 14 / 2 = 7
- 7 – 1 = 6
- 6 / 2 = 3
- 3 – 1 = 2
- 2 / 2 = 1
- 1 – 1 = 0
I have come up with a naive solution that does calculations, but this algorithm cannot handle numbers that are very large.
def test(x):
a = int(x,2)
steps = 0
while a != 0:
if a % 2 == 0:
a = a // 2
else:
a = a - 1
steps += 1
return steps
test("1000")
Out[65]: 4
test("101")
Out[66]: 4
test("001")
Out[67]: 1
test("0010010001")
Out[68]: 10
test("001001")
Out[69]: 5
what I need to know: How can I avoid doing the calculation and have an algorithm that is fast / can handle big numbers?
Answers:
Your algorithm isn’t correct for odd numbers. You are only dividing when the number is even, which is not how you described the “steps.”
you want
def test(x, 2):
x_int = int(x)
steps = 0
while x_int <= 0:
x_int //= 2
x -= 1
steps += 1
You should clarify your algorithm, because the way you described it, you’re not guaranteed to converge to 0 for all inputs. The way you described it is an infinite loop for odd numbers. Just try 1:
#test(1)
1 // 2 = 0
0 - 1 = -1
...
Now you will never get to 0, which is why you should check for x_int <= 0.
I suggest you reconsider why you want to do this anyway. I’m fairly certain that you don’t even need an iterative algorithm to know how many “steps” are required anyway, there should just be a mathematical formula for this.
You could also use a recursive approach:
def stepsToZero(N):
return N if N < 2 else 2 + stepsToZero(N//2-1)
This will get you results up to N = 2**993 (which is quite a big number) with a very concise (and imho more elegant) function.
What would run much faster would be to solve this mathematically
For example:
import math
def steps2Zero(N):
if N < 2: return N
d = int(math.log(N+2,2))-1
s = int(N >= 3*2**d-2)
return 2*d+s
Note that, for N=2^900, the mathematical solution is a hundred times faster than the recursion. On the other hand, the recursive function responds in well under a second and is a lot more readable. So, depending on how this would be used and on what size numbers, performance considerations are likely pointless
Assuming your code is correct and the rule is:
- test(0) = 0
- test(n) = 1 + test(n / 2) if n is even;
1 + test(n − 1) otherwise
the important thing to notice is that:
- an even number ends with a binary 0
- dividing by 2 removes the 0 from the end (and nothing else)
- an odd number ends with a binary 1
- subtracting 1 turns the last 1 to a 0 (and nothing else)
So every 1 bit except for the first one adds 2 steps, and every significant 0 bit adds 1 step. That means for inputs that start with 1
, you can write:
def test(x):
return x.count('1') + len(x) - 1
Now you just need to account for leading zeros, or just the specific case of "0"
if leading zeros aren’t possible.
I had this question on a coding test today, I had 40 mins to complete the test. Unfortunately, I only came up with a good solution after the timer had reached the limit.
You do not need to calculate the divisions and the subtractions(!). You can iterate over the characters of S, if the character is a 1, two steps are required, if the character is a 0, only one step is required.
- If there is a 1 at the end, you will subtract 1
- If there is a 0 at the end, you can divide by two and the number will shift to the right.
- The first character is an exception (
S[0]
)
Here is the solution:
def iterate_string(S: str):
acc = 0
for c in S:
if c == "0":
acc += 1
else:
acc += 2
acc -= 1 # the very first 1 is only + 1, thus - 1
return acc
Here is an example:
1001 (17) - 1 = 1000 (16)
1000 (16) / 2 = 100 (8)
100 (8) / 2 = 10 (4)
10 (4) / 2 = 1
1 - 1 = 0
# First digit, requires two steps:
|
1001
# Second digit, requires one step:
|
1001
# Third digit, requires one step:
|
1001
# S[0] is 1, but requires only one step:
|
1001
=> total of 5 steps:
0: 1001 # (-1)
1: 1000 # (/2)
2: 100 # (/2)
3: 10 # (/2)
4: 1 # (-1)
5: 0
Good luck to the next person who is having the same challenge! 🙂
Here is the naive solution that can’t handle big numbers:
def do_calculations(S: str):
decimal_value = int(S, 2)
iterations = 0
while decimal_value > 0:
if decimal_value % 2 == 1:
decimal_value = decimal_value - 1
else:
decimal_value = decimal_value / 2
iterations += 1
return iterations
If the input number is in binary (or convert the input number to binary) then implement this function simply
def solution(s): # 's' should be a binary input (011100)
while s[0] == "0":
s = s[1:]
ones = s.count('1')
zeros = s.count('0')
return ones*2+zeros-1
Given a binary number, I need to write a function to count the total steps reaching zero. The rules are:
- If the number is even, divide it by 2
- If the number is odd, subtract 1 from it
for example, it takes six iterations for "1110" (14) to become 0:
- 14 / 2 = 7
- 7 – 1 = 6
- 6 / 2 = 3
- 3 – 1 = 2
- 2 / 2 = 1
- 1 – 1 = 0
I have come up with a naive solution that does calculations, but this algorithm cannot handle numbers that are very large.
def test(x):
a = int(x,2)
steps = 0
while a != 0:
if a % 2 == 0:
a = a // 2
else:
a = a - 1
steps += 1
return steps
test("1000")
Out[65]: 4
test("101")
Out[66]: 4
test("001")
Out[67]: 1
test("0010010001")
Out[68]: 10
test("001001")
Out[69]: 5
what I need to know: How can I avoid doing the calculation and have an algorithm that is fast / can handle big numbers?
Your algorithm isn’t correct for odd numbers. You are only dividing when the number is even, which is not how you described the “steps.”
you want
def test(x, 2):
x_int = int(x)
steps = 0
while x_int <= 0:
x_int //= 2
x -= 1
steps += 1
You should clarify your algorithm, because the way you described it, you’re not guaranteed to converge to 0 for all inputs. The way you described it is an infinite loop for odd numbers. Just try 1:
#test(1)
1 // 2 = 0
0 - 1 = -1
...
Now you will never get to 0, which is why you should check for x_int <= 0.
I suggest you reconsider why you want to do this anyway. I’m fairly certain that you don’t even need an iterative algorithm to know how many “steps” are required anyway, there should just be a mathematical formula for this.
You could also use a recursive approach:
def stepsToZero(N):
return N if N < 2 else 2 + stepsToZero(N//2-1)
This will get you results up to N = 2**993 (which is quite a big number) with a very concise (and imho more elegant) function.
What would run much faster would be to solve this mathematically
For example:
import math
def steps2Zero(N):
if N < 2: return N
d = int(math.log(N+2,2))-1
s = int(N >= 3*2**d-2)
return 2*d+s
Note that, for N=2^900, the mathematical solution is a hundred times faster than the recursion. On the other hand, the recursive function responds in well under a second and is a lot more readable. So, depending on how this would be used and on what size numbers, performance considerations are likely pointless
Assuming your code is correct and the rule is:
- test(0) = 0
- test(n) = 1 + test(n / 2) if n is even;
1 + test(n − 1) otherwise
the important thing to notice is that:
- an even number ends with a binary 0
- dividing by 2 removes the 0 from the end (and nothing else)
- an odd number ends with a binary 1
- subtracting 1 turns the last 1 to a 0 (and nothing else)
So every 1 bit except for the first one adds 2 steps, and every significant 0 bit adds 1 step. That means for inputs that start with 1
, you can write:
def test(x):
return x.count('1') + len(x) - 1
Now you just need to account for leading zeros, or just the specific case of "0"
if leading zeros aren’t possible.
I had this question on a coding test today, I had 40 mins to complete the test. Unfortunately, I only came up with a good solution after the timer had reached the limit.
You do not need to calculate the divisions and the subtractions(!). You can iterate over the characters of S, if the character is a 1, two steps are required, if the character is a 0, only one step is required.
- If there is a 1 at the end, you will subtract 1
- If there is a 0 at the end, you can divide by two and the number will shift to the right.
- The first character is an exception (
S[0]
)
Here is the solution:
def iterate_string(S: str):
acc = 0
for c in S:
if c == "0":
acc += 1
else:
acc += 2
acc -= 1 # the very first 1 is only + 1, thus - 1
return acc
Here is an example:
1001 (17) - 1 = 1000 (16)
1000 (16) / 2 = 100 (8)
100 (8) / 2 = 10 (4)
10 (4) / 2 = 1
1 - 1 = 0
# First digit, requires two steps:
|
1001
# Second digit, requires one step:
|
1001
# Third digit, requires one step:
|
1001
# S[0] is 1, but requires only one step:
|
1001
=> total of 5 steps:
0: 1001 # (-1)
1: 1000 # (/2)
2: 100 # (/2)
3: 10 # (/2)
4: 1 # (-1)
5: 0
Good luck to the next person who is having the same challenge! 🙂
Here is the naive solution that can’t handle big numbers:
def do_calculations(S: str):
decimal_value = int(S, 2)
iterations = 0
while decimal_value > 0:
if decimal_value % 2 == 1:
decimal_value = decimal_value - 1
else:
decimal_value = decimal_value / 2
iterations += 1
return iterations
If the input number is in binary (or convert the input number to binary) then implement this function simply
def solution(s): # 's' should be a binary input (011100)
while s[0] == "0":
s = s[1:]
ones = s.count('1')
zeros = s.count('0')
return ones*2+zeros-1