简体   繁体   中英

How does this program actually work?

I wrote a recursive method that will take in a number, and return the next power of two. If the number entered is a power of two, it will map to itself. This presumes n > 0

static int nextPower1(int n) {
    if(n == 0) {
        System.out.println("nextPower(" + n + ") = 2");
        return 2;
    } else if(n == 1) {
        System.out.println("nextPower(" + n + ") = 1");
        return 1;
    } else {
        int lastBit = n & 1;
        int secondLastBit = (n >> 1) & 1;
        int restOfTheBits = (n >> 2);

        int result = nextPower1(restOfTheBits << 1 | secondLastBit ^ lastBit);
        return result << 1;
    }

What I don't get is how does XORing the last two bits and settting the second last bit return the correct power of two? How does this bitwise trick work?


BIG EDIT:

Ok, I see where it was going wrong, and by updating the base cases, and changing XOR to OR, I think it is fixed. I put it in a loop to test it. The loop checked powers of 2 from to Integer.MAX_VALUE to Integer.MAX_VALUE. It returned the expected answer from Integer.MIN_VALUE up to at least (1 << 30), though it breaks after that. I suspect its because integers are signed. The updated code:

static int nextPower2(int n) {
    if(n == 3) {
        return 4;
    } else if(n < 4) {
        return 1 << (n >> 1);
    } else {
        return nextPower(((n >> 2) << 1) | ((n >> 1) & 1) | (n & 1)) << 1;
    }
}

this code is broken, it always returns the wrong answer for eg a 1 followed by one or more zeroes followed by two 1's: 1011, 10011, etc. (because as it recurses it combines the paired 1's into a 0, which from that point on makes it indistinguishable from an incoming 1000, 10000, etc.)

(Edit: it's wrong for all values that have an even number of 1 bits to the right of the msb. Every pair xor's to 0, losing information necessary for a correct result. - AR.)

The recursive loop is rolling up the least significant bit into the number as a kind of history of whether the rolled-up portion contained any 1's. Isolating both least signficant bits just makes the code harder to follow; it's doing otherBits = n >> 1; return nextPower1(otherBits ^ lastBit) otherBits = n >> 1; return nextPower1(otherBits ^ lastBit)

When looking at the last two bits left (the most signficant bit and the rolled up history bit), if the history is zero then the most significant bit is the only set bit, and itself is the next power of 2. If the history bit is one, however, then the next power of two is the next bit position, one past the msb. (if only the history bit worked correctly; see below)

The code looks not at the last two bits, but at the rolled up result of the msb and the history bit. If 1, then only the msb was set, history is zero, and returns 1 (self) as the power of two. If 0, then the history bit was set, and returns the next higher power (2). As the recursion unwinds, the power (1 or 2) is shifted back to the original msb position, yielding the msb or the next bigger power of two.

However, because the history does not propagate through zeros, this breaks.

To fix, the recursion would need to fix the history (use OR, not XOR), and not blend the history bit into the msb at the end, something like:

nextPower( int n ) {
    if (n == 1) return 1;
    if (n == 2) return 2;
    if (n == 3) return 4;
    return nextPower((n >> 1) | (n & 1)) << 1;
}

(test for (n==0) to support n >= 0, which the code explicitly does not)

Fixing the code as follows makes it work (See http://ideone.com/P5s4rd ):

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        int n;
        int result;
        for (n=0;n<=16;n++) {
            result = nextPower(n);
            System.out.println("Next power of " + n + " is " + result);
        }
    }

    static int nextPower(int n) {
        int result;
        result = nextPower1(n);
        if (n<=1 || result ==n)
            return result;
        else
            return result * 2;
    }


    static int nextPower1(int n) {
        if(n == 0) {
            return 1;
        } else if(n == 1) {
            return 1;
        } else {
            int lastBit = n & 1;
            int secondLastBit = (n >> 1) & 1;
            int restOfTheBits = (n >> 2);

            int result = nextPower1(restOfTheBits << 1 | secondLastBit ^ lastBit);
            return result << 1;
        }
    }
}

You get the following output:

Next power of 0 is 1 (0 <= 2 power 0)
Next power of 1 is 1 (1 <= 2 power 0)
Next power of 2 is 2 (2 <= 2 power 1)
Next power of 3 is 4 (3 <= 2 power 2)
Next power of 4 is 4 ...etc
Next power of 5 is 8
Next power of 6 is 8
Next power of 7 is 8
Next power of 8 is 8
Next power of 9 is 16
Next power of 10 is 16
Next power of 11 is 16
Next power of 12 is 16
Next power of 13 is 16
Next power of 14 is 16
Next power of 15 is 16
Next power of 16 is 16

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM