algorithmmathdynamic-programming

minimum number of steps to reduce number to 1


Given any number n, and three operations on n:

  1. add 1
  2. subtract 1
  3. divide by 2 if the number is even

I want to find the minimum number of the above operations to reduce n to 1. I have tried dynamic progamming approach, also BFS with pruning, but n can be very large (10^300) and I do not know how to make my algorithm faster. The greedy approach (divide by 2 if even and subtract 1 if odd) also does not give the optimal result. Is another solution exists?


Solution

  • There is a pattern which allows you to know the optimal next step in constant time. In fact, there can be cases where there are two equally optimal choices -- in that case one of them can be derived in constant time.

    If you look at the binary representation of n, and its least significant bits, you can make some conclusions about which operation is leading to the solution. In short:

    Proof

    If the least significant bit is zero, the next operation should be the division by 2. We could instead try 2 additions and then a division, but then that same result can be achieved in two steps: divide and add. Similarly with 2 subtractions. And of course, we can ignore the useless subsequent add & subtract steps (or vice versa). So if the final bit is 0, division is the way to go.

    Then the remaining 3-bit patterns are like **1. There are four of them. Let's write a011 to denote a number that ends with bits 011 and has a set of prefixed bits that would represent the value a:

    So for odd numbers the second-last bit determines the next step (except for 3).

    Python Code

    This leads to the following algorithm (Python), which needs one iteration for each step, making for O(logš¯‘›) iterations. If we consider that basic arithmetic operations on large integers have a logš¯‘› complexity, this brings us to a time complexity of O(logĀ²š¯‘›):

    def stepCount(n):
        count = 0
        while n > 1:
            if n % 2 == 0:             # bitmask: *0
                n = n // 2
            elif n == 3 or n % 4 == 1: # bitmask: 01
                n = n - 1
            else:                      # bitmask: 11
                n = n + 1
            count += 1
        return count
    

    JavaScript Snippet

    Here is a version where you can input a value for n and let the snippet produce the number of steps:

    function stepCount(n) {
        var count = 0n;
        while (n > 1n) {
            if (n % 2n == 0) {                    // bitmask: *0
                n /= 2n;
            } else if (n == 3n || n % 4n == 1n) { // bitmask: 01
                n--;
            } else {                              // bitmask: 11
                n++;
            }
            count++;
        }
        return count;
    }
    
    // I/O
    var input = document.getElementById('input');
    var output = document.getElementById('output');
    var calc = document.getElementById('calc');
    
    calc.onclick = function () {
      var n = BigInt(input.value);
      var res = stepCount(n);
      output.textContent = res;
    }
    <input id="input" value="123549811245">
    <button id="calc">Calculate steps</button><br>
    Result: <span id="output"></span>

    This script uses BigInt typed values to allow for arbitrary large integers as is the case in Python.