views:

401

answers:

7

The function a = 2 ^ b can quickly be calculated for any b by doing a = 1 << b. What about the other way round, getting the value of b for any given a? It should be relatively fast, so logs are out of the question. Anything that's not O(1) is also bad.

I'd be happy with can't be done too if its simply not possible to do without logs or a search type thing.

A: 

It can't be done without testing the high bit, but most modern FPUs support log2 so all is not lost.

Ignacio Vazquez-Abrams
+13  A: 

Build a look-up table. For 32-bit integers, there are only 32 entries so it is O(1).

Most architectures also have an instruction to find the position of the most significant bit of a number a, which is the value b. (gcc provides the __builtin_clz function for this.)

For a BigInt, it can be computed in O(log a) by repeatedly dividing by 2.

int b = -1;
while (a != 0) {
  a >>= 1;
  ++ b;
}
KennyTM
the look-up table is of course needs O(log a) space
jk
@jk: Acutally the look-up table of a BigInteger occupies "infinite size". The `a` is the one in `a = 1 << b`.
KennyTM
+7  A: 

For this sort of thing I usually refer to this page with bit hacks:

For example:

Find the log base 2 of an integer with a lookup table:

static const char LogTable256[256] = 
{
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
    -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
    LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
    LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};

unsigned int v; // 32-bit word to find the log of
unsigned r;     // r will be lg(v)
register unsigned int t, tt; // temporaries

if (tt = v >> 16)
{
  r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else 
{
  r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}

There are also a couple of O(log(n)) algorithms given on that page.

Mark Byers
What an awesome link.
Dave
+1  A: 

Some architectures have a "count leading zeros" instruction. For example, on ARM:

MOV R0,#0x80      @ load R0 with (binary) 10000000
CLZ R1,R0         @ R1 = number of leading zeros in R0, i.e. 7

This is O(1).

Graham Borland
Any way to do this from C#??
Hannesh
+2  A: 

Or you can write:

while ((a >>= 1) > 0) b++;

This is O(1). One could imagine this to be expanded to:

b = (((a >> 1) > 0) ? 1 : 0) + (((a >> 2) > 0) ? 1 : 0) + ... + (((a >> 31) > 0) ? 1 : 0);

With a complier optimization, that once (a >> x) > 0) returns false, rest won't be calculated. Also comparing with 0 is faster then any other comparison. Also: alt text, where k is maximum of 32 and g is 1.

Reference: Big O notation

But in case you where using BigInteger, then my code example would look like:

    int b = 0;
    String numberS = "306180206916083902309240650087602475282639486413"
            + "866622577088471913520022894784390350900738050555138105"
            + "234536857820245071373614031482942161565170086143298589"
            + "738273508330367307539078392896587187265470464";
    BigInteger a = new BigInteger(numberS);

    while ((a = a.shiftRight(1)).compareTo(BigInteger.ZERO) > 0) b++;

    System.out.println("b is: " + b);
Margus
How can that be O(1) ?
Filip Ekberg
I do not think you understand entirely how BigO notation works.
BjornS
@BjornS, me or Margus?
Filip Ekberg
Note, that **a** size is **constant**. Only thing that can confuse you is using **BigInteger** class and you don't even have bitwise operators defined for classes.
Margus
@Margus: Why there's no bitwise operators defined for BigInteger?
KennyTM
Margus is right for languages like C, C# and Java. But there are others (Ruby, Python, Haskell, JavaScript etc) that does not have bounds on integer sizes.
Jakob
@KennyTM - I meant that operators are not defined in hardware, and are emulated by software classes, this makes them quite a bit slower.
Margus
@Margus: I see.
KennyTM
O(1)? If you say so, but it is also linear in log(a).
GregS
+1  A: 

If a is a double rather than an int then it will be represented as mantissa and exponent. The exponent is the part you are looking for, as this is the logarithm of the number.

If you can hack the binary representation then you can get the exponent out. Look up the IEEE standard to see where and how the exponent is stored.

For an integral value, if some method of getting the most significant bit position is not available then you can binary-search the bits for the upper-most 1 which is therefore O(log numbits). Doing this may well actually perform faster than converting to a double first.

CashCow
A: 

In Java you can use Integer.numberOfLeadingZeros to compute the binary logarithm. It returns the number of leading zeros in the binary representation, so

  • floor(log2(x)) = 31 - numberOfLeadingZeros(x)
  • ceil(log2(x)) = 32 - numberOfLeadingZeros(x - 1)
starblue