最近对于位运算特别感兴趣,是因为看到了一段C代码,只有短短一行就实现了令人咂舌的效果。代码如下:
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>
";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
输出结果是当前时间的特殊显示。效果如下:
!! !!!!!! !! !!!!!! !! !!!!!!
!! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !!
!! !!!!!! !! !! !! !! !! !!!!!!
!! !! !! !! !! !! !!
!! !! !! !! !! !! !!
!! !!!!!! !! !! !! !!!!!!
作者把原本要几十行甚至上百行的代码浓缩成一行,其中用到了递归和位图。这段代码理解起来很困难,也不是我这篇文章的主题。关于它的具体解析,请参看: http://www.youkuaiyun.com/article/2013-04-11/2814852-ioccc-a-clock-in-one-line。
本文想介绍一下同样是利用位图实现的排序算法。它的强大之处在于,对于极其大量并且不重复的数据,可以用极小的内存代价实现排序。举个例子,比如有1亿个不重复并且其最大值不超过10亿的数据,如果用插入排序或快速排序等方式,单单就开辟一个存储1亿个整数的内存空间就需要4×1亿接近400M的内存空间,这是一个庞大的消耗。但是,这些数据最大不超过10亿并且没有重复的,我们可以用10亿位图(即共有10亿个位)来分别表示1-10亿,只要读到某个数就将其在位图中对应的某一位置1,最后将位值为1的位置按序输出,从而实现了排序效果。一个整数可以表示32位,那么我们最多只需要10亿/32+1个整数,大概只要30M的内存空间。所以在大量数据排序中位图排序确实是个非常有用的方法,既节省了空间,排序速度也是毋庸置疑的。
接下来具体介绍一下位图排序的原理。首先,需要一个10亿/32+1个整数的数组,我们会用到这些整数的二进制形式,只要知道是这个数组中的第几个整数以及这个整数的第几位,就可以计算出该位对应的是哪一个目标整数。比如,9999在位图中的位置是,数组中第9999/32(结果取整)个整数,该整数的第9999%32(即取余)位。接下就看看如何用程序语言实现位图排序,下面是C的实现代码:
#include <stdio.h>
#define MAX 1000000000
#define N ((MAX%32==0) ? (MAX/32) : (MAX/32+1))
#define SHIFT 5
#define MASK 0x1F
int bit[N];
//此方法将目标为置为1
//i右移5位求出整数在数组中的位置,i&MASK对32取余即求出位在整数中的位置,
//最后1左移的结果与当前数组元素进行或操作,得到将第i位置1后的整数
void set(int i)
{
bit[i>>SHIFT]=bit[i>>SHIFT]|(1<<(i&MASK));
}
//初始将所有位置0,原理同set
void clear()
{
int i;
for(i=1;i<=MAX;i++)
{
bit[i>>SHIFT]=bit[i>>SHIFT]&(~(1<<(i&MASK)));
}
}
//判断位是否为1,是的话返回对应的整数
int test(int i)
{
return bit[i>>SHIFT] & (1<<(i&MASK));
}
int main(int argc, char *argv[])
{
int i;
clear();
//输入几个数测试排序结果
while(scanf("%d",&i)!=EOF)
{
set(i);
}
for(i=1;i<=MAX;i++)
{
if(test(i))
printf("%d ",i);
}
return 0;
}