1. 程式人生 > 其它 >求與一個數最接近的2的N次冪

求與一個數最接近的2的N次冪

第一種方法

public class TestClosest2NthPower {

  public static void main(String[] args) {
    System.out.println(test(-1));//1
    System.out.println(test(1));//1
    System.out.println(test(-1));//1
    System.out.println(test(10));//16
    System.out.println(test(16));//16
    System.out.println(test(Integer.MAX_VALUE));//2^30
  }

  private static int test(int target) {
    //int型別最大值為(2^31)-1,所以目標能取到的最大值為2^30
    int MAXIMUM = 1 << 30;
    if (target >= MAXIMUM) {
      return MAXIMUM;
    }
    int result = 1;
    while (result < target) {
      result *= 2;
    }
    return result;
  }

}

相當於在2的0次冪和2的30次冪中的31個數中找一個最接近的數。

[2^0,2^1 ... 2^30]

第二種方法

public class TestClosest2NthPower2 {

  public static void main(String[] args) {
    System.out.println(test(-1));//1
    System.out.println(test(1));//1
    System.out.println(test(-1));//1
    System.out.println(test(10));//16
    System.out.println(test(16));//16
    System.out.println(test(Integer.MAX_VALUE));//2^30
  }

  private static int test(int target) {
    //int型別最大值為(2^31)-1,所以目標能取到的最大值為2^30
    int MAXIMUM = 1 << 30;
    if (target >= MAXIMUM) {
      return MAXIMUM;
    }
    int temp = target - 1;
    temp |= temp >> 1;
    temp |= temp >> 2;
    temp |= temp >> 4;
    temp |= temp >> 8;
    temp |= temp >> 16;
    return (temp < 0) ? 1 : temp + 1;
  }

}

示例分析

以129為例(可以更明顯看出效果),先減1為128,二進位制表示為

00000000 00000000 00000000 10000000

右移1位

00000000 00000000 00000000 01000000

兩者按位或

00000000 00000000 00000000 11000000

保證了前兩位都為1,以此類推可以保證第一個1及之後的所有位都為1

00000000 00000000 00000000 11111111

再加一

00000000 00000000 00000001 00000000

十進位制表示為256。

核心原理

核心原理就是將一個數減1的二進位制表示的第一個1及之後的所有位都置為1,然後加1,這樣得到的數就是2的N次冪,相當於最高位的1向左進1位,之後的所有位都置為0。

為什麼要先減1

為了相容一個數已經是2的N次冪的情況。以2的4次冪16為例,減1為15,最後操作結果還是16,如果這個數不是2的N次冪,如15,其實減不減1都可以得到正確結果16。

使用場景

ForkJoinPool的構造器中初始化workQueues的容量時就使用到了這種方法。

第三種方法

public class TestClosest2NthPower3 {

  public static void main(String[] args) {
    System.out.println(test(-1));//1
    System.out.println(test(1));//1
    System.out.println(test(-1));//1
    System.out.println(test(10));//16
    System.out.println(test(16));//16
    System.out.println(test(Integer.MAX_VALUE));//2^30
  }

  private static int test(int target) {
    //int型別最大值(2^31)-1,所以目標能取到的最大值為2^30
    int MAXIMUM = 1 << 30;
    if (target >= MAXIMUM) {
      return MAXIMUM;
    }
    int n = -1 >>> Integer.numberOfLeadingZeros(target - 1);
    return (n < 0) ? 1 : n + 1;
  }

}

示例分析

以129為例(可以更明顯看出效果),先減1為128,二進位制表示為

00000000 00000000 00000000 10000000

使用Integer的numberOfLeadingZeros()方法求出前導0的個數為24,-1的二進位制表示為

11111111 11111111 11111111 11111111

無符號右移24位

00000000 00000000 00000000 11111111

再加一

00000000 00000000 00000001 00000000

十進位制表示為256。

核心原理

和上一個方法的原理類似,也是將一個數減1的二進位制表示的第一個1及之後的所有位都置為1,然後加1。

使用場景

這種方法參考的就是HashMap中計算容量大小的演算法。

參考

求最接近數n的2的次方數