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?

Asked By: potentialwjy

||

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.

Answered By: Tom Lubenow

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

Answered By: Alain T.

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.

Answered By: Ry-

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:

  1. 1001 (17) - 1 = 1000 (16)
  2. 1000 (16) / 2 = 100 (8)
  3. 100 (8) / 2 = 10 (4)
  4. 10 (4) / 2 = 1
  5. 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
Answered By: davidkuda

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
Answered By: Sai Nishanth
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.