《算法心得》一点整理

近期在图书馆看到本神书《算法心得:高效算法的奥秘》。主要解说计算机算法的,强调编译器优化和计算机体系结构设计的。尽管看的不大懂。但还是给自己增长了见识和知识。少许整理些自己感兴趣的算法,以备兴许温故知新。

1. 操作最右边的位元
a. 将字组中值为1且最靠右的位元置0,假设不存在值为1的位元。则所有结果为0(比如 0101 1110 => 0101 1100):
x & (x-1)
这个操作能够推断无符号证书是不是2的幂或者0.

b. 将字组中值为0且最靠右的位元置1。假设不存在值为0的位元,则所有结果的每一位为1(比如 1010 0111 => 1010 1111):
x | (x+1)
这个操作能够推断无符号证书是不是2的幂或者0.

c. 将字组尾部的1所有变成0。假设尾部没有1。则不变(比如 1010 0111 => 1010 0000)
x & (x+1)
这个操作能够推断无符号证书是不是2^n-1或者0.

d. 将字组尾部的0所有变成1,假设尾部没有0。则不变(比如 1010 1000 => 1010 1111)
x | (x-1)

e. 将字组中值为0且最靠右的位元置1。其余位置0,假设不存在值为0的位元,则所有结果的每一位为0(比如 1010 0111 => 0000 1000):
~x & (x+1)

f. 将字组中值为1且最靠右的位元置0,其余位置1,假设不存在值为1的位元,则所有结果的每一位为1(比如 1010 1000 => 1111 0111):
~x | (x-1)

g. 将字尾部所有0的位元变成1,其余位置0,假设不存在值为0的位元。则所有结果的每一位为0(比如 0101 1000 => 0000 0111):
~x & (x-1) 或 ~(x | -x)

h. 将字尾部所有1的位元变成0,其余位置1。假设不存在值为1的位元。则所有结果的每一位为1(比如 1010 0111 => 1111 1000):
~x | (x+1)

i. 将字组中值为1且最靠右的位元保留。其余位置0,假设不存在值为1的位元,则所有结果的每一位为0(比如 0101  1000 => 0000 1000):
x & (-x)

j. 将字组中值为1且最靠右的位元,以及其右方所有值为0的位元都置为1。其余位置0。假设不存在值为1的位元,则所有结果的每一位为1,而当x尾部没有值为0的位元是,运算结果是1(比如 0101  1000 => 0000 1111):
x ^ (x-1)

k. 将字组中值为0且最靠右的位元。以及其右方所有值为1的位元都置为1。其余位置0,假设不存在值为0的位元,则所有结果的每一位为1,而当x尾部没有值为0的位元是,运算结果是1(比如 0101  0111 => 0000 1111):
x ^ (x+1)

l. 将字组右側连续出现且值为1的位元置为0,(比如 0101 1100 => 0100 0000):
( ( x | (x-1)) +1 ) & x

m. 将字组右側连续出现且值为0的位元置为1。(比如 0100 0111 => 0111 1111):
( ( x & (x+1)) -1 ) | x

依据上面的知识规律。以下这道题就能够这样做:
比如:求给定数 x 往上添加近期的2^n 的值。

如给定5时,求出8,给定4时。求出4.

unsigned int fun1(unsigned int x)
{
x = x<<1;
while ( x & (x-1) )
     x = x & (x-1);
return x; 
}

unsigned int fun2(unsigned int x)
{
while ( ( ( x | (x-1) ) +1 ) & x )
     x = ( ( x | (x-1) ) +1 ) & x ;
return x<<1;
}

2. 德摩根定律
~(x&y) = ~x | ~y
~(x|y) = ~x & ~y
~(x+y) = ~x - y
~(x-y) = ~x + y
~-x = x-1

3. 位操作新式使用方法
给定一个数x。然后找出下一个比它大的数字y,该数字y中值为1的位元数与x中的同样。这题能够用在找出元素个数为某一定值的所有子集的算法中。
思路:给定一个表示子集位串的字组x。首先要找到连续出如今x右側且值为1的一组位元,然后将该值“加1”。再把原来后面跟着的哪些0 补上。

举例来说。假设带计算的位串是xxx0 1111 0000,那么结果就应该是xxx1 0000 0111,当中xxx这三个位元值不限。该算法首先定义 s = x&-x。算出s == 0000 0001 0000,这样就找到x中最小的那个1

然后把它与x相加。把两书仅仅和xxx1 0000 0000存放在r 中。此时结果中的1个位元已经计算好了。想求出其它位元。还须要把位串中剩下的n-1个“1”移到右側。要向移动到右側,首次要计算r和x的异或。在本例中就是0001 1111 0000.

上面那个值中“1”的个数太多了,并且没有靠右对齐。

为了解决此问题。要将它与s相除,这样就能够把哪些“1”靠右对齐了。除之前还要先向右移动2位,以便丢弃那2个多余的位元。此结果与r取或。就得到终于答案了。

用C语言实现就是
unsigned fun3(insigned x)
{
unsigned smallest, ripple, ones;
                               //x=xxx0 1111 0000
smallest = x & -x;             //  xxx0 0001 0000
ripple = x +smallest;          //  xxx1 0000 0000
ones = x ^ ripple;             //  xxx1 1111 0000
ones = ( ones>>2 )/smallest;   //  xxx0 0000 0111
return ripple | ones;          //  xxx1 0000 0111
}

4. 结合逻辑操作的加减运算
-x = ~x+1
-~x = x+1
~-x = x-1
x^y = ( x|y ) - ( x&y )
x + y = ( x^y ) + 2( x&y )
         = ( x|y ) + ( x&y )
x - y  = ( x^y ) - 2( ~x&y )
         = ( x&~y ) - ( ~x&y )
当中 x + y = ( x^y ) + 2( x&y ) 是先对两数做不进位加法x^y,然后再补上进位。
x - y  = ( x^y ) - 2( ~x&y ) 是先对两数进行不进位剪发x^y。然后再把结尾的位从结果中减去。

5. 与常数相乘
要将x乘以13(二进制1101)。可运行下述操作
t1 = x<<2;
t2 = x<<3;
r = x + t1 + t2;

posted on 2017-04-20 13:55  ljbguanli  阅读(159)  评论(0编辑  收藏  举报