神奇算法小集合

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

一、一个Float类型的绝对值

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

double x;
/* make x = abs(x) */
*(((int *) &x) + 1) &= 0x7fffffff;

二、指针对齐

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

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

a=((typeof(a))(((int)(((void *)malloc(c+(b-1)))+(b-1)))&-b))
三、整数的平均值

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

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

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

unsigned int
reverse(register unsigned int x)
{
	x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
	x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
	x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
	x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
	return((x >> 16) | (x << 16));
}
//此函数也可重写为如下函数
unsigned int
reverse(register unsigned int x)
{
        register unsigned int y = 0x55555555;
        x = (((x >> 1) & y) | ((x & y) << 1));
        y = 0x33333333;
        x = (((x >> 2) & y) | ((x & y) << 2));
        y = 0x0f0f0f0f;
        x = (((x >> 4) & y) | ((x & y) << 4));
        y = 0x00ff00ff;
        x = (((x >> 8) & y) | ((x & y) << 8));
        return((x >> 16) | (x << 16));
}
五、Float类型数据的比较

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

#define FasI(f)  (*((int *) &(f)))
#define FasUI(f) (*((unsigned int *) &(f)))

#define	lt0(f)	(FasUI(f) > 0x80000000U)        //小于0
#define	le0(f)	(FasI(f) <= 0)                  //小于等于0
#define	gt0(f)	(FasI(f) > 0)                   //大于0
#define	ge0(f)	(FasUI(f) <= 0x80000000U)       //大于等于0
六、通过一个指针域来实现双向链表

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

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

七、除法取整

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

(a+b-1)/b;     //向上取整
(a+(b/2))/b;   //就近取整
八、Gray码转换

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

unsigned int
g2b(unsigned int gray)
{
        gray ^= (gray >> 16);
        gray ^= (gray >> 8);
        gray ^= (gray >> 4);
        gray ^= (gray >> 2);
        gray ^= (gray >> 1);
        return(gray);
}
九、整数常量乘法

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

y2 = y + y;
y4 = y2 + y2;
result = y + y4;
  另外,如果y是整数,则可通过移位来实现,代码如下:

y4 = (y << 2);
result = y + y4;
十、两个整数的最大值和最小值

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

//x和y的最小值
x+(((y-x)>>(WORDBITS-1))&(y-x));
//x和y的最大值
x-(((x-y)>>(WORDBITS-1))&(x-y));
十一、整数的指数次方

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

y2 = y * y;
y4 = y2 * y2;
result = y * y4;
十二、整数选择

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

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

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

unsigned int
lzc(register unsigned int x)
{
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
        return(WORDBITS - ones(x));
}
十五、求一个输的最低有效bit

//求得x的最低有效bit
(x^(x&(x-1)));
十六、求一个整数的log2的值

//向下取整的版本
unsigned int
floor_log2(register unsigned int x)
{
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
#ifdef	LOG0UNDEFINED
        return(ones32(x) - 1);
#else
	return(ones32(x >> 1));
#endif
}
//向上取整的版本,即需要判断x是否是2的幂次方
unsigned int
log2(register unsigned int x)
{
	register int y = (x & (x - 1));

	y |= -y;
	y >>= (WORDBITS - 1);
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
#ifdef	LOG0UNDEFINED
        return(ones(x) - 1 - y);
#else
	return(ones(x >> 1) - y);
#endif
}
十七、求大于一个整数的最小2的幂次方的数

//Next Largest Power of 2
unsigned int
nlpo2(register unsigned int x)
{
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
        return(x+1);
}
十八、求一个整数的最高有效bit位

unsigned int
msb32(register unsigned int x)
{
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
        return(x & ~(x >> 1));
}
十九、多项式计算

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

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

unsigned int
ones32(register unsigned int x)
{
        /* 32-bit recursive reduction using SWAR...
	   but first step is mapping 2-bit values
	   into sum of 2 1-bit values in sneaky way
	*/
        x -= ((x >> 1) & 0x55555555);
        x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
        x = (((x >> 4) + x) & 0x0f0f0f0f);
        x += (x >> 8);
        x += (x >> 16);
        return(x & 0x0000003f);
}

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

//通过异或实现
x ^= y; /* x' = (x^y) */
y ^= x;	/* y' = (y^(x^y)) = x */
x ^= y; /* x' = (x^y)^x = y */
//通过加减法实现
x = x+y;
x = x-y;
y = x-y;
二十二、计算整数二进制表达中尾部零的个数

//Trailing Zero Count
unsigned int
tzc(register int x)
{
        return(ones((x & -x) - 1));
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值