昨天晚上,一个在当当网实习的同学回来,说遇到了一个算法没看明白,仔细一看,还挺有意思,现在给大家分享一下。
算法说明:
1 表示在该位取,0 表示不取。
例如,对ABC三个元素进行全组合,用二进制表示如下
001表示取C, 001=1
010表示取B, 010=2
011表示取BC, 011=3
100表示取A, 100=4
101表示取AC, 101=5
110表示取AB, 110=6
111表示取ABC, 111=7
很显然ABC三个元素全组合共有2^3-1=7种。
推广到对n个元素进行全排列,取法就是从1到2^n-1的所有二进制形式,要取得2^n,只需将0xFFFFFFFF左移32-n位,再右移回来就可以了。0xFFFFFFFF>>>(32 - n) 结果既为(2的n次方减1)。
对于每一种情况,怎么快速检验一位被丢掉,哪一位被选中呀?假设这个标记数是 i,想检测它的第 j 位是 0 还是 1 。int 为 4 字节,占32位。首先先 将 i 左移 31-j 位,就将第 j 移到了最高位,再将结果右移31位,此时原来的第j位移到了最低位,它左侧的所有位都被填充为原第j位的值。所以此时整个标记数的值只有两种情况0或-1,分别对应第j位被过滤或者是被选中的情况。
这里复习一下Java中的相关知识:
java中有三种移位运算符
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
>>>: 无符号右移,忽略符号位,空位都以0补齐
0xffffffff表示的是一个十六进制数
换为十进制数 : 0xffffffff=16x10^7+16x10^6+...+16x10^0=4294967295
换为二进制数 : 十六进制转换为二进制就是直接把每位转换成二进制就可以了,就是直接f变成二进制:1111,
则 0xffffffff = 1111 1111 1111 1111 1111 1111 1111 1111 , 即32位数都是1的二进制数
其中16进制数 0xFFFFFFFF 不用强制转换可以是int型,其值为 -1
因为int 为带符号类型,带符号类型最高为是符号位,又因为0xFFFFFFFF,也就是四个字节32 bits全是1, 符号位是1,所以这个数是负数。
内存中的数值为补码表示,所以0xFFFFFFFF是一个负数的补码。负数从补码求原码,最高符号位不变,保持 1, 其余各位求反,末尾加1。
也就是 0xFFFFFFFF,二进制为:1111 1111 1111 1111 1111 1111 1111 1111
-> 10000000 00000000 00000000 00000000
-> 10000000 00000000 00000000 00000001
原码首位表示符号位,其余位表示绝对值大小,所以,这个数是 -1
Java实现:
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str[] = { "A", "B", "C", "D", "E" };
int nCnt = str.length;
//int nBit = (0xFFFFFFFF >>> (32 - nCnt));
int nBit = 1<<nCnt;
for (int i = 1; i <= nBit; i++) {
for (int j = 0; j < nCnt; j++) {
if ((i << (31 - j)) >> 31 == -1) {
System.out.print(str[j]);
}
}
System.out.println("");
}
}
}
其中 int nBit = (0xFFFFFFFF >>> (32 - nCnt)); 与语句 int nBit = 1<<nCnt; 等价