神奇算法小集合

来源:互联网 发布:kill me heal me 知乎 编辑:程序博客网 时间:2024/06/09 22:49
 

神奇算法小集合

分类: 算法的点点滴滴 143人阅读 评论(0) 收藏 举报
算法精致神奇

    整理一些今天看到的比较神奇的算法,可以拓宽下自己的思维和视野。

一、一个Float类型的绝对值

    一个数值的绝对值可以通过与符号位的按位与操作来实现,对于IA32 32bit处理器,符号位是0x80000000,对于IA32 64bit来说,符号位是0x8000000000000000。下面代码为double类型的取绝对值操作。

[cpp] view plaincopy
  1. double x;  
  2. /* make x = abs(x) */  
  3. *(((int *) &x) + 1) &= 0x7fffffff;  

二、指针对齐

    指针的向上和向下对齐的操作可通过以下代码实现,代码中,a向b对齐,且b是2的指数次方:

[cpp] view plaincopy
  1. //向下对齐  
  2. (a & ~(b-1)); //~(b-1) == -b,所以也可以写成如下形式  
  3. (a & -b);  
  4. //向上对齐  
  5. ((a + (b-1)) & -b)  
    如果在实际应用中,想要创建一个名为a的数据结构,其含有c字节的内存,且使其向b字节对齐(b是2的指数次方),则可通过以下代码实现:

[cpp] view plaincopy
  1. a=((typeof(a))(((int)(((void *)malloc(c+(b-1)))+(b-1)))&-b))  
三、整数的平均值

    下面,看几个神奇的操作:

[cpp] view plaincopy
  1. (x+y) = ((x&y)+(x|y)) = ((x^y)+2*(x&y));  
  2. //那么,(x+y)/2可通过下面的操作实现  
  3. (x&y)+((x^y)/2) = (x&y)+((x^y)>>1)  
  4. //上一行代码的有点是这样不会导致溢出  
四、按位倒置

    一个整数按位倒置可通过以下代码实现:

[cpp] view plaincopy
  1. unsigned int  
  2. reverse(register unsigned int x)  
  3. {  
  4.     x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));  
  5.     x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));  
  6.     x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));  
  7.     x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));  
  8.     return((x >> 16) | (x << 16));  
  9. }  
  10. //此函数也可重写为如下函数  
  11. unsigned int  
  12. reverse(register unsigned int x)  
  13. {  
  14.         register unsigned int y = 0x55555555;  
  15.         x = (((x >> 1) & y) | ((x & y) << 1));  
  16.         y = 0x33333333;  
  17.         x = (((x >> 2) & y) | ((x & y) << 2));  
  18.         y = 0x0f0f0f0f;  
  19.         x = (((x >> 4) & y) | ((x & y) << 4));  
  20.         y = 0x00ff00ff;  
  21.         x = (((x >> 8) & y) | ((x & y) << 8));  
  22.         return((x >> 16) | (x << 16));  
  23. }  
五、Float类型数据的比较

    Float类型数据的比较通过如下编码实现:

[cpp] view plaincopy
  1. #define FasI(f)  (*((int *) &(f)))  
  2. #define FasUI(f) (*((unsigned int *) &(f)))  
  3.   
  4. #define lt0(f)  (FasUI(f) > 0x80000000U)        //小于0  
  5. #define le0(f)  (FasI(f) <= 0)                  //小于等于0  
  6. #define gt0(f)  (FasI(f) > 0)                   //大于0  
  7. #define ge0(f)  (FasUI(f) <= 0x80000000U)       //大于等于0  
六、通过一个指针域来实现双向链表

    通常,双向链表中有两个指针,分别之前当前节点node的前驱指针prev和指向当前节点后继的指针next,如果每个链表节点中只能存储一个指针,如何实现双向链表呢?可通过将当前节点指针赋值为其前驱指针和后继指针的按位异或值(XOR)来实现。

    不幸的是,在C语言中貌似没有对指针的异或定义。

七、除法取整

    除法的向上和就近取证如下所示:

[cpp] view plaincopy
  1. (a+b-1)/b;     //向上取整  
  2. (a+(b/2))/b;   //就近取整  
八、Gray码转换

    有很多种传统方法,比如第k个Gray码可通过K^(K>>1)来实现,下面代码是Gray码与无符号二进制数的转换:

[cpp] view plaincopy
  1. unsigned int  
  2. g2b(unsigned int gray)  
  3. {  
  4.         gray ^= (gray >> 16);  
  5.         gray ^= (gray >> 8);  
  6.         gray ^= (gray >> 4);  
  7.         gray ^= (gray >> 2);  
  8.         gray ^= (gray >> 1);  
  9.         return(gray);  
  10. }  
九、整数常量乘法

  整数乘法可以进行简单的转换,如x*y,如果x==5(4+1),则可通过如下方式来计算x*y:

[cpp] view plaincopy
  1. y2 = y + y;  
  2. y4 = y2 + y2;  
  3. result = y + y4;  
  另外,如果y是整数,则可通过移位来实现,代码如下:

[cpp] view plaincopy
  1. y4 = (y << 2);  
  2. result = y + y4;  
十、两个整数的最大值和最小值

  两个整数的最大值和最小值的计算可通过如下方式实现,其中x和y是2的补码形式:

[cpp] view plaincopy
  1. //x和y的最小值  
  2. x+(((y-x)>>(WORDBITS-1))&(y-x));  
  3. //x和y的最大值  
  4. x-(((x-y)>>(WORDBITS-1))&(x-y));  
十一、整数的指数次方

  可参加整数的常量乘法中的技巧来计算整数的指数次方,如果x==5(4+1),则通过下面方式来计算y的x次方:

[cpp] view plaincopy
  1. y2 = y * y;  
  2. y4 = y2 * y2;  
  3. result = y * y4;  
十二、整数选择

  通过以下代码实现整数的选择:

[cpp] view plaincopy
  1. //实现以下if和else语句,如  
  2. if (a<b)   
  3.     x=c;   
  4. else   
  5.     x=d;   
  6. //通过下面代码实现此if else语句  
  7. ((((a-b) >> (WORDBITS-1)) & (c^d)) ^ d)  
十三、判断一个数是否是2的幂次方

[cpp] view plaincopy
  1. //如果x是2的幂次方,则此表达式为0,否则x不是2的幂次方  
  2. (x&(x-1));  
十四、计算整数的二进制表示前面有几个零

[cpp] view plaincopy
  1. unsigned int  
  2. lzc(register unsigned int x)  
  3. {  
  4.         x |= (x >> 1);  
  5.         x |= (x >> 2);  
  6.         x |= (x >> 4);  
  7.         x |= (x >> 8);  
  8.         x |= (x >> 16);  
  9.         return(WORDBITS - ones(x));  
  10. }  
十五、求一个输的最低有效bit

[cpp] view plaincopy
  1. //求得x的最低有效bit  
  2. (x^(x&(x-1)));  
十六、求一个整数的log2的值

[cpp] view plaincopy
  1. //向下取整的版本  
  2. unsigned int  
  3. floor_log2(register unsigned int x)  
  4. {  
  5.         x |= (x >> 1);  
  6.         x |= (x >> 2);  
  7.         x |= (x >> 4);  
  8.         x |= (x >> 8);  
  9.         x |= (x >> 16);  
  10. #ifdef  LOG0UNDEFINED  
  11.         return(ones32(x) - 1);  
  12. #else  
  13.     return(ones32(x >> 1));  
  14. #endif  
  15. }  
  16. //向上取整的版本,即需要判断x是否是2的幂次方  
  17. unsigned int  
  18. log2(register unsigned int x)  
  19. {  
  20.     register int y = (x & (x - 1));  
  21.   
  22.     y |= -y;  
  23.     y >>= (WORDBITS - 1);  
  24.         x |= (x >> 1);  
  25.         x |= (x >> 2);  
  26.         x |= (x >> 4);  
  27.         x |= (x >> 8);  
  28.         x |= (x >> 16);  
  29. #ifdef  LOG0UNDEFINED  
  30.         return(ones(x) - 1 - y);  
  31. #else  
  32.     return(ones(x >> 1) - y);  
  33. #endif  
  34. }  
十七、求大于一个整数的最小2的幂次方的数

[cpp] view plaincopy
  1. //Next Largest Power of 2  
  2. unsigned int  
  3. nlpo2(register unsigned int x)  
  4. {  
  5.         x |= (x >> 1);  
  6.         x |= (x >> 2);  
  7.         x |= (x >> 4);  
  8.         x |= (x >> 8);  
  9.         x |= (x >> 16);  
  10.         return(x+1);  
  11. }  
十八、求一个整数的最高有效bit位

[cpp] view plaincopy
  1. unsigned int  
  2. msb32(register unsigned int x)  
  3. {  
  4.         x |= (x >> 1);  
  5.         x |= (x >> 2);  
  6.         x |= (x >> 4);  
  7.         x |= (x >> 8);  
  8.         x |= (x >> 16);  
  9.         return(x & ~(x >> 1));  
  10. }  
十九、多项式计算

    一个多项式的表达式如x0+x1*x+x2*x*x+x3*x*x*x+...,其更加有效的计算可通过表达式x0+x*(x1+x*(x2+x*(x3+x*(...))))来替代。

二十、统计32位整数中1的个数

[cpp] view plaincopy
  1. unsigned int  
  2. ones32(register unsigned int x)  
  3. {  
  4.         /* 32-bit recursive reduction using SWAR... 
  5.        but first step is mapping 2-bit values 
  6.        into sum of 2 1-bit values in sneaky way 
  7.     */  
  8.         x -= ((x >> 1) & 0x55555555);  
  9.         x = (((x >> 2) & 0x33333333) + (x & 0x33333333));  
  10.         x = (((x >> 4) + x) & 0x0f0f0f0f);  
  11.         x += (x >> 8);  
  12.         x += (x >> 16);  
  13.         return(x & 0x0000003f);  
  14. }  

二十一、不使用临时变量交换两个数的值

[cpp] view plaincopy
  1. //通过异或实现  
  2. x ^= y; /* x' = (x^y) */  
  3. y ^= x; /* y' = (y^(x^y)) = x */  
  4. x ^= y; /* x' = (x^y)^x = y */  
  5. //通过加减法实现  
  6. x = x+y;  
  7. x = x-y;  
  8. y = x-y;  
二十二、计算整数二进制表达中尾部零的个数

[cpp] view plaincopy
  1. //Trailing Zero Count  
  2. unsigned int  
  3. tzc(register int x)  
  4. {  
  5.         return(ones((x & -x) - 1));  
  6. }  
0 0
原创粉丝点击