7

I was asked below question in an interview:

Every number can be described via the addition and subtraction of powers of 2. For example, 29 = 2^0 + 2^2 + 2^3 + 2^4. Given an int n, return minimum number of additions and subtractions of 2^i to get n.

Example 1:

Input: 15
Output: 2
Explanation: 2^4 - 2^0 = 16 - 1 = 15

Example 2:

Input: 8
Output: 1

Example 3:

Input: 0
Output: 0

Below is what I got but is there any way to improve this or is there any better way to solve above problem?

  public static int minPowerTwo(int n) {
    if (n == 0) {
      return 0;
    }
    if (Integer.bitCount(n) == 1) {
      return 1;
    }
    String binary = Integer.toBinaryString(n);
    StringBuilder sb = new StringBuilder();
    sb.append(binary.charAt(0));
    for (int i = 0; i < binary.length() - 1; i++) {
      sb.append('0');
    }
    int min = Integer.parseInt(sb.toString(), 2);

    sb.append('0');
    int max = Integer.parseInt(sb.toString(), 2);

    return 1 + Math.min(minPowerTwo(n - min), minPowerTwo(max - n));
  }
user218867
  • 16,252
  • 12
  • 112
  • 156
john
  • 9,493
  • 34
  • 111
  • 210
  • Wow, I think this is the first time I've seen `bitCount` used ! Given both `bitCount` and the maximum bit position, one alternative is you can count the number of 1s and 0s, and so determine from that whether to add or start-with-next-larger-power-of-two-and-subtract . eg, 33 has max bit=6, bitcount 2 (ie 4 zeros), so since bitcount < zeros use adding. 30, on the other hand, has max bit=5, bitcount=4 (ie 1 zero), so since bitcount > zeros start from 32 and use subtractions. (eta, of course, I haven't seen bitcount as I haven't had to calculare parity) – racraman Sep 05 '19 at 01:20
  • Closely related: *How can we multiply with minimum number of additions and subtractions?*. When there were no hardware multipliers, one answer was to [recode](https://en.m.wikipedia.org/wiki/Non-adjacent_form) "the multiplier". – greybeard Sep 05 '19 at 06:23
  • I am no JAVA coder but `Integer.parseInt(sb.toString(), 2);` ??? you are making strings and then parse them back to int ? that seem to be much slower than brute force attack on integers alone ... why not using `x&1` and `x>>=1` to check if bit is set from lsb to msb ... remembering if bit is consequent or not and then construct either `+2^bit` or `+2^(maxbit+1) - 2^(minbit)` ... – Spektre Sep 05 '19 at 07:35

5 Answers5

6

Well... we can deduce that each power of two should be used only once, because otherwise you can get the same result a shorter way, since 2x + 2x = 2x+1, -2x - 2x = -2x+1, and 2x - 2x = 0.

Considering the powers used in order, each one has to change the corresponding bit from an incorrect value to the correct value, because there will be no further opportunities to fix that bit, since each power is used only once.

When you need to add or subtract, the difference is what happens to the higher bits:

000000    000000    111100    111100
 + 100     - 100     + 100    -  100
------    ------    ------    ------
000100    111100    000000    111000

One way, all the higher bits are flipped. The other way they are not.

Since each decision can independently determine the state of all the higher bits, the consequences of choosing between + or - are only relevant in determining the next power of 2.

When you have to choose + or -, one choice will correct 1 bit, but the other choice will correct 2 bits or more, meaning that the next bit that requires correction will be higher.

So, this problem has a very straightforward solution with no dynamic programming or searching or anything like that:

  1. Find the smallest power of 2 that needs correction.
  2. Either add it or subtract it. Pick the option that corrects 2 bits.
  3. Repeat until all the bits are correct

in java, that would look like this. Instead of finding the operations required to make the value, I'll find the operations required to change the value to zero, which is the same thing with opposite signs:

int minPowersToFix(int val) {
    int result = 0;
    while(val!=0) {
        ++result;
        int firstbit = val&-val; //smallest bit that needs fixed
        int pluscase = val+firstbit;
        if ((pluscase & (firstbit<<1)) == 0) {
            val+=firstbit;
        } else {
            val-=firstbit;
        }
    }
    return result;
}
Matt Timmermans
  • 36,921
  • 2
  • 27
  • 59
1

And, here is a test case to check whether a solution is correct, written in Java.

(It was written for my solution, which is proven not correct in some case, so I removed that answer, but the test case is still relevant.)

Matt Timmermans's answer passes all the test cases, including negative numbers.

And, Integer.bitCount(val ^ (3 * val)) passes most of them, except when input is Integer.MAX_VALUE.


Code

MinCountOf2PowerTest.java

import org.testng.Assert;
import org.testng.annotations.Test;

public class MinCountOf2PowerTest {
    @Test
    public void testPositive() {
        // no flip,
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01010001", 2)), 3);

        // flip,
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011", 2)), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0111", 2)), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01111", 2)), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.MAX_VALUE), 2);

        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01101", 2)), 3);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011011", 2)), 3);

        // flip, there are multiple flippable location,
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0100000111", 2)), 3);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("010010000000111", 2)), 4);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0100100000001111111", 2)), 4);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("010011000000001111111", 2)), 5);
    }

    @Test
    public void testZero() {
        Assert.assertEquals(MinCountOf2Power.minCount(0), 0);
    }

    @Test
    public void testNegative() {
        Assert.assertEquals(MinCountOf2Power.minCount(-1), 1);
        Assert.assertEquals(MinCountOf2Power.minCount(-9), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(-100), 3);
    }

    // a positive number has the same result as its negative number,
    @Test
    public void testPositiveVsNegative() {
        for (int i = 1; i <= 1000; i++) {
            Assert.assertEquals(MinCountOf2Power.minCount(i), MinCountOf2Power.minCount(-i));
        }
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.MAX_VALUE), MinCountOf2Power.minCount(-Integer.MAX_VALUE));
    }

    // corner case - ending 0,
    @Test
    public void testCornerEnding0() {
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01110", 2)), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011110", 2)), 2);

        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011100", 2)), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0111000", 2)), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01110000", 2)), 2);
    }

    // input from OP's question, refer: https://stackoverflow.com/questions/57797157
    @Test
    public void testOpInput() {
        Assert.assertEquals(MinCountOf2Power.minCount(15), 2);
        Assert.assertEquals(MinCountOf2Power.minCount(8), 1);
        Assert.assertEquals(MinCountOf2Power.minCount(0), 0);
    }
}

Tips:

  • It's written in Java, and use TestNG.
    • But you can use JUnit instead simply by replacing the import statement, I guess.
    • Or translate to other languages by coping the input / output value pairs with specific syntax.
  • I also found that a positive integer always has the same result as its negative number.
    And there is a test case included to proved that.
user218867
  • 16,252
  • 12
  • 112
  • 156
0

I wrote this algorithm to solve the problem.

Given N a positive integer:

  1. Find the highest power of 2 A and the lowest power of 2 B, such that A ≤ N ≤ B and A≠B. In other words find in what interval of consecutive powers of 2 N belongs;
  2. Find if N is closer to A or B, for example by comparing N with the mid value between A and B (It is their average, and since B=2×A the average is 3×A/2 or 1.5×A)
  3. If N is closer to the lower bound (A) than N = A + δ: Append "subtract B" to the explanation message;
  4. If N is closer to the higher bound (B) than N = B - δ: Append "add A" to the explanation message;
  5. Replace N with δ and repeat

The number of iterations minus 1 is the solution you are looking for.

To solve step 1 I wrote this support method that returns the closest power of 2 that is smaller than input, that is A (and we can get B because it is just the double of A)

public int getClosestLowerboundPowerof2 (int n)
{
    int i = 1;
    while (i<=n/2){
        i*=2;
    }
    return i;
}

The rest is done here:

int operations;
String explanation = "";
if (input>0){
    operations = -1;
    int n = input, a;
    while (n >= 1) {
        operations++;
        a = getClosestLowerboundPowerof2(n);
        if (n > a*1.5) {
            explanation += " - "+ a * 2;
            n = a * 2 - n;
        } else {
            explanation += " + " + a;
            n -= a;
        }
    }
    System.out.println(input + " = " + explanation.substring(3,explanation.length()) + ", that " + ((operations==1)?"is":"are") + " "+ operations + " operation" + ((operations==1)?"":"s"));
}
else{
    System.out.println("Input must be positive");
}

As an example with input = 403 it would print:

403 = 512 - 128 + 16 + 2 + 1, that are 4 operations

Hope I helped!


NOTE: I first misinterpreted the question so I put effort in writing a detailed answer to the wrong problem... I'm keeping here the original answer because it may be interesting for somebody.

The problem is actually a mathematical argument: how to convert a number from base 10 to base 2, and they just asked you to implement an algorithm for that.

Here some theory about this concept and here a method for reference.

Programmatically I'm interpreting the problem as "Given an integer print a string of its representation in base 2". For instance given 100 print 2^6 + 2^5 + 2^2. As the linked wiki on radixes explains, that there is no need for subtractions, so there will only be additions.

The shortest way to do is to start from n, halve it at each iteration (i), and write 2^i only if this number (m) is odd. This is tested with modulo operator (% in java). So the method will be just this:

public String from10to2(int n){
  String str = "";
  for (int m = n, i=0; m>=1; m/=2, i++){
      str = ((m%2==1)?"+ 2^"+i+" ":"")+str; //"adds '+ 2^i' on top of the string when m is odd, keep str the same otherwise
  }
  return str.substring(2,str.length()); //debug to remove " + " at the start of the string
}

The content of the for may look inintuitive because I put effort to make the code as short as possible.

With little effort my method can be generalized to convert a number in base 10 to any base:

public String baseConverter(int targetBase, int decimalNumber){
  String str = "";
  for (int m = decimalNumber, i=0; m>=1; m/=targetBase, i++){
      str = ((m%targetBase==1)?"+ "+targetBase+"^"+i+" ":"")+str; //"adds '+ x^i' on top of the string when m is odd, keep str the same

otherwise } return str.substring(2,str.length()); //debug to remove " + " at the start of the string }

PS: I didn't use StringBuilder because it's not conceived to append a string on the start. The use of the String concatenation as I did is argument of debate (someone approve it, other don't).

Sterconium
  • 541
  • 4
  • 19
0

This seems pretty trivial to solve for the cases presented in the examples, like:

0111...1

You can replace any of this pattern with just two powers; i.e.: 7 = 8 - 1 or 15 = 16 - 1 and so on.

You can also deduce that if there are less then 3 consecutive ones, you don't gain much, for example:

0110 (4 + 2)
0110 (8 - 2)

But at the same time, you don't lose anything by doing that operation; in contrast for some cases this is even beneficial:

0110110 - // 54, this has 4 powers

we can take the "last" 0110 and replace it with 1000 - 0010 (8-2) or:

0111000 - 000010 (56 - 2)

but now we can replace 0111 with just two powers : 1000 - 0001.

As such a simple "replace" algorithm can be made:

static int count(int x) {
    String s = new StringBuffer(Integer.toBinaryString(x)).reverse().toString() + "0";

    Pattern p = Pattern.compile("1+10");
    Matcher m = p.matcher(s);

    int count = 0;
    while (m.find()) {
        ++count;
        s = m.replaceFirst("1");
        m = p.matcher(s);
    }

    return Integer.bitCount(Integer.parseInt(s, 2)) + count;
} 
Eugene
  • 102,901
  • 10
  • 149
  • 252
0

I guess

For example, 29 = 2^0 + 2^2 + 2^3 + 2^4

is not a correct example in the context of this question. As far as I understand, I should be able to do like

29 = 2^5 - 2^2 + 2^0

Alright, basically this is a math problem. So if math isn't your best suit like me then i would advise you to consider logarithm in the first place whenever you see exponentials in a question. Sometimes it is very useful like in this case since it reduces this problem to a sort of coin change problem with dynamical denominators and also subtraction is allowed.

  1. First I need to find the biggest n that's close to the target. Lets find the exact n value in 2^n = 29 which is basically log (2^n) = log 29, which is n log 2 = log 29 so n = log 29 / log 2. Which happens to be 4.857980995127573 and now i know that i will start with by rounding it to 5.
  2. 2^5 is an overshoot. Now i need to reach 32-29 = 3 and also since 32 > 29 the result, 2^2 will be subtracted.
  3. Now we have 2^5 - 2^2 which is 28 and less than 29. Now we need to add the next result and our target is 1.

Ok here is a simple recursive code in JS. I haven't fully tested but seemingly applies the logic just fine.

function pot(t, pr = 0){                        // target and previous result
  var d  = Math.abs(t - pr),                    // difference
      n  = Math.round(Math.log(d)/Math.log(2)), // the n figure
      cr = t > pr ? pr + 2**n                   // current result
                  : pr - 2**n;
  return t > cr ? `2^${n} + ` + pot(t, cr)      // compose the string result
                : t < cr ? `2^${n} - ` + pot(t, cr)
                         : `2^${n}`;
}

console.log(pot(29));
console.log(pot(1453));
console.log(pot(8565368));
Redu
  • 19,106
  • 4
  • 44
  • 59