給定一個正整數,找出與其二進制表示中1的個數相同,且大小最接近的那兩個數

/**html

 * 功能:給定一個正整數,找出與其二進制表示中1的個數相同,且大小最接近的那兩個數。java

 * (一個略大一個略小。)算法

 */app

 

三種方法:spa

方法一:蠻力法.net

 

方法二:位操做法htm

[java] view plain copyblog

 

  1. <span style="white-space:pre">    </span>/** 
  2.      * 方法:位操做法 
  3.      * 思路:獲取後一個較大的數 
  4.      *      1)計算c0和c1。c1是拖尾1的個數,c0是緊鄰拖尾1的做坊一連串0的個數。 
  5.      *      2)將最右邊、非拖尾0變爲1,其位置爲p=c1+c0。 
  6.      *      3)將位p右邊的全部位清零。 
  7.      *      4)在緊鄰位置p的右方,插入c1-1個1。 
  8.      * @param n 
  9.      * @return 
  10.      */  
  11.     public static int getNext(int n){  
  12.         int c=n;  
  13.         int c0=0;  
  14.         int c1=0;  
  15.           
  16.         while((c&1)==0&&(c!=0)){  
  17.             c0++;  
  18.             c>>=1;  
  19.         }  
  20.           
  21.         while((c&1)==1){  
  22.             c1++;  
  23.             c>>=1;  
  24.         }  
  25.           
  26.         if(c0+c1==31||c0+c1==0)//c0+c1+1=32,1表示p所在位。  
  27.             return -1;  
  28.           
  29.         int p=c0+c1;//最右邊處,非拖尾0的位置。  
  30.         n|=(1<<p);//翻轉0爲1  
  31.         n&=~((1<<p)-1);//將p右邊的全部位清零  
  32.         n|=(1<<(c1-1))-1;//在右邊填入(c1-1)個1  
  33.           
  34.         return n;  
  35.     }  
  36.       
  37.     /** 
  38.      * 思路:獲取前一個較小的數 
  39.      *      1)計算c0和c1。c1是拖尾1的個數,c0是緊鄰拖尾1的做坊一連串0的個數。 
  40.      *      2)將最右邊、非拖尾1變爲0,其位置爲p=c1+c0。 
  41.      *      3)將位p右邊的全部位清零。 
  42.      *      4)在緊鄰位置p的右方,插入c1+1個1。 
  43.      *      注意:步驟2和3能夠合併。 
  44.      * @param n 
  45.      * @return 
  46.      */  
  47.     public static int getPrev(int n){  
  48.         int c=n;  
  49.         int c0=0;  
  50.         int c1=0;  
  51.           
  52.         while((c&1)==1){  
  53.             c1++;  
  54.             c>>=1;  
  55.         }  
  56.           
  57.         if(c==0)  
  58.             return -1;//錯誤檢查!!!全爲1時,沒法找到  
  59.           
  60.         while((c&1)==0&&(c!=0)){  
  61.             c0++;  
  62.             c>>=1;  
  63.         }  
  64.           
  65.         int p=c0+c1;  
  66.         n&=~((1<<(p+1))-1);//將最右邊、非拖尾1變爲0,其位置爲p=c1+c0;將位p右邊的全部位清零。  
  67.         int mask=(1<<(c1+1))-1;//在緊鄰(!!!)位置p的右方,插入c1+1個1。  
  68.         n|=mask<<(c0-1);  
  69.           
  70.         return n;  
  71.     }  


方法三:算術法ip

[java] view plain copyget

 

  1. <span style="white-space:pre">    </span>/** 
  2.      * 方法:算術法 
  3.      * 思路:獲取後一個較大的數,從新表述問題 
  4.      *      1)計算c0和c1。c1是拖尾1的個數,c0是緊鄰拖尾1的做坊一連串0的個數。 
  5.      *      2)將p位置1。 
  6.      *      3)將位p右邊的全部位清零。 
  7.      *      4)在緊鄰位置p的右方,插入c1-1個1。 
  8.      * 步驟2,3有一種快速作法,將拖尾0置爲1(獲得p個拖尾1),而後再加1。加1後,全部拖尾1都會翻轉,最終位p變爲1,後邊跟p個0. 
  9.      * @param n 
  10.      * @return 
  11.      */  
  12.     public static int getNextArith(int n){  
  13.         int c=n;  
  14.         int c0=0;  
  15.         int c1=1;  
  16.           
  17.         while((c0&1)==0&&(c!=0)){  
  18.             c0++;  
  19.             c>>=1;  
  20.         }  
  21.           
  22.         while((c1&1)==1){  
  23.             c1++;  
  24.             c>>=1;  
  25.         }  
  26.           
  27.         if(c0+c1==31||c0+c1==0)  
  28.             return -1;  
  29.           
  30.         //將拖尾0置1,獲得p個拖尾1  
  31.         n|=(1<<c0)-1;  
  32.         //先將p個1清零,而後位p改成1  
  33.         n+=1;  
  34.         //在右邊填入(c1-1)個1  
  35.         n|=(1<<(c1-1))-1;  
  36.           
  37.         return n;  
  38.     }  
  39.     /** 
  40.      * 方法:算術法 
  41.      * 思路:獲取前一個較小的數,從新表述問題 
  42.      *      1)計算c0和c1。c1是拖尾1的個數,c0是緊鄰拖尾1的做坊一連串0的個數。 
  43.      *      2)將p位清零。 
  44.      *      3)將位p右邊的全部位置1。 
  45.      *      4)將位0到位c0-1清零。 
  46.      * @param n 
  47.      * @return 
  48.      */  
  49.     public static int getPrevArith(int n){  
  50.         int c=n;  
  51.         int c0=0;  
  52.         int c1=0;  
  53.           
  54.         while((c&1)==1){  
  55.             c1++;  
  56.             c>>=1;  
  57.         }  
  58.           
  59.         while((c&1)==0&&(c!=0)){  
  60.             c0++;  
  61.             c>>=1;  
  62.         }  
  63.           
  64.         if(c==0)  
  65.             return -1;//錯誤檢查!!!全爲1時,沒法找到  
  66.           
  67.         n-=(1<<c1)-1;//清除拖尾1,此時p位爲1,後面所有爲零  
  68.         n-=1;//將p爲置0,後面全部位置置1  
  69.         n&=~(1<<(c0-1)-1);//將最後邊置c0-1個0  
  70.           
  71.         return n;  
  72.     } 

或者:

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);
    }
}
相關文章
相關標籤/搜索