/**html
* 功能:給定一個正整數,找出與其二進制表示中1的個數相同,且大小最接近的那兩個數。java
* (一個略大一個略小。)算法
*/app
三種方法:spa
方法一:蠻力法.net
方法二:位操做法htm
[java] view plain copyblog
方法三:算術法ip
[java] view plain copyget
或者:
1,問題描述
給定一個整數N,該整數的二進制權值定義以下:將該整數N轉化成二進制表示法,其中 1 的個數即爲它的二進制權值。
好比:十進制數1717 的二進制表示爲:0000 0110 1011 0101 故它的二進制權值爲7(二進制表示中有7個1)
如今要求一個比N大,且最靠近N的數,且這個數的二進制權值與N相同。(這裏不考慮Integer.MAX_VALUE 和負數情形。)
對於有符號的32位整數而言:它們的補碼以下:
Integer.MAX_VALUE= 0111 1111 1111 1111 1111 1111 1111 1111 (2^32-1)
Integer.MIN_VALUE= 1000 0000 0000 0000 0000 0000 0000 0000 (-2^32)
0 = 0000 0000 0000 0000 0000 0000 0000 0000
-1= 1111 1111 1111 1111 1111 1111 1111 1111
(負數的補碼是在原碼的基礎上,符號位不變,其他位取反,末位加1)參考:原碼, 反碼, 補碼 詳解
2,問題分析
思路①
先求出N的二進制權值,而後從N+1開始遞增,依次判斷這個數的二進制權值是否與N相同,直到找到一個相同的二進制權值的數。
而求解二進制權值的算法能夠用移位操做來實現。可參考:JAVA中經常使用的二進制位操做
//求解正數的二進制表示法中的 1 的位數 private static int countBit(int num){ int count = 0; for(; num > 0; count++) { num &= (num - 1); } return count; }
思路①這種方式,當N很大時,效率會很慢。
那有沒有更好的思路呢?
其實咱們的目的是找到一個數,只要這個數的二進制權值與N相同,且該數大於N且最接近N便可。
那麼,能夠先將N用二進制表示出來。而後,從低位開始尋找N的二進制中出現 1 以後,第一次出現0的位,假設是第 i 位,那麼將第 i 位置爲1,獲得一個新的數M,此時 M 的二進制中 1 的個數比N多一個。再把M的二進制中的 第 i-1 位的 1 設置爲0 ,就獲得了大於N且最接近N的二進制權值同樣的數。
示例以下:
N= 0010 1001 0101 1111
將第5位置爲0,獲得了M(最右邊的位爲第0位)
M= 0010 1001 0111 1111
因爲是從低位開始尋找第一次出現0的位。故第5位的右邊全是1,再將M的 第 i-1 位(第四位)設置爲0,獲得了H
H= 0010 1001 0110 1111
H所對應的十進制數,就是題目中要尋找的數。
再好比:
N= 0010 1001 0101 1100
M= 0010 1001 0111 1100
H= 0010 1001 0110 1100
再好比:
N= 0000 1000
M= 0001 1000
H= 0001 0000
3,代碼實現:
思路①實現:
import java.util.Scanner; public class Main{ public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNextLong()) { int num = sc.nextInt(); long start = System.currentTimeMillis(); int weight = countBit(num); int k = num + 1; while(countBit(k) != weight) { k++; } long end = System.currentTimeMillis(); System.out.println("res:" + k + " time: " + (end - start)); } sc.close(); } private static int countBit(int num){ int count = 0; for(; num > 0; count++) { num &= (num - 1); } return count; } }
②思路②實現:
import java.util.Scanner; public class Larger_Near_Than_N { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNextInt()) { int number = sc.nextInt(); long start = System.currentTimeMillis(); int result = findNearThanN(number); long end = System.currentTimeMillis(); System.out.println("res:" + result + " time: " + (end - start)); } sc.close(); } private static int findNearThanN(int number){ int result = -1; int first_indexOf_1 = getFirst_1(number); if(first_indexOf_1 != -1)//找到了一個 二進制位 1 { //若是找到了一個二進制位1, indexOf_0不可能爲0 int indexOf_0 = getFirst_0(number, first_indexOf_1); int tmp = setBit_1(number, indexOf_0); result = setBit_0(tmp, indexOf_0 - 1); } return result; } //第 i位爲1 返回true,爲0 返回false private static boolean getBit(int number, int i){ return ((number & (1 << i)) != 0); } //從右到左(低位開始)查找number的二進制位 1 的位置 private static int getFirst_1(int number){ int index = -1; for(int i = 0; i <= 31; i++) if(getBit(number, i)) { index = i; break; } return index;//返回二進制位 1 在 number 中的位置 } //從 start+1 位置開始,查找 number的二進制中,第一個出現的0的位置 private static int getFirst_0(int number, int start){ int index = -1; for(int i = start + 1; i <= 31; i++) { if(!getBit(number, i)) { index = i; break; } } return index; } //將 number 的二進制表示法中的第 i 位設置爲 1 private static int setBit_1(int number, int i){ return (number | (1 << i)); } //將 number 的二進制表示法中的第 i 位設置爲 0 private static int setBit_0(int number, int i){ int mask = ~(1 << i); return (number & mask); } }