位操作-给定一个二进制数,在“1”的个数保持不变的前提下,获取最近的略大数

首先考虑后一个数是什么样子的,看下边这个例子:

1 1 0 1 0 0 0 0 0 0
9 8 7 6 5 4 3 2 1 0

如上表所示,如果我们想在保持1的个数不变的前提下,得到略大的数:

我们会发现,

1).给定一个数n,和两个位置i和j;假设将位i从0变到1,位j从1变到0,如果i再j的左侧,则变大

2).有一个0翻转至1,则必须有一个1翻转至0;

3).如果想让数变大,但是不至于太大,那么我们需要翻转最右侧的0,但是其右侧必须还要有1,换句话说,翻转最右侧但是不是拖尾的0,就上述表格中的数据而言,我们需要反转的是7所在的位置的0;

接下来是详细步骤:

步骤一:

翻转最右侧,但是非拖尾的0

首先根据获取到的P的位置(带翻转的0的位置),将其变为1;

步骤二:

将P右侧的所有位变为0

具体做法:

a=a<<p;//除去P为1,其余位均为0;

b=a-1;//将P位置之后的全部都设置成1;

mask=~b;//将P之前的所有位置均设置为1,其余变成0;

n&=mask;

上述等效为:

n&=~((1<<p)-1);

步骤三:

进行回填 将检测到的P位置之后的1的个数c1进行回填c1-1个1;

具体做法是:

a=1<<(c1-1);//将位c1-1设置成1,其余设置成0;

b=a-1;

n|=b;

简写为:

n|=(1<<(c1-1)-1);

整体的代码实现为:

 1 int getNext(int n){
 2     int c=n;
 3     int c0=0;
 4     int c1=0;
 5     while(((c&1)==0)&&(c!=0)){
 6         c0++;//获取位尾0的个数
 7         c>>=1;
 8     }
 9     while((c&1)==1){
10         c1++;//获取位尾1的个数
11         c>>=1;
12     }
13     if(c0+c1==31||c0+c1==0){
14         return -1;
15     }
16     int p=c0+c1;
17     n|=(1<<p);
18     n&=~((1<<p)-1);
19     n|=(1<<(c1-1))-1;
20     return n;    
21    }

 

posted on 2016-07-29 09:58  Cultivate  阅读(192)  评论(0编辑  收藏  举报

导航