BitSet in C -- Programming Pearl

今天开始重温<<编程珠玑>>这本书,第一个例子就讲到用Bitmap这个数据结构,加之最近长期和C语言打交道,于是就想到了如何用C语言实现真正意义上的BitSet(bitset)。

最开始我想到用Structure里面的Bit field来实现,但是Structure里的Alignment导致了内存的浪费(因为BitSet这样的数据结构很多时候就是为了节省空间而使用的,因此这样的做法是不可取的),当然我们可以在Structure里定义8个1位的unsigned char来解决这个问题,但是这样的做法感觉很生硬不优美。
另外关于Structure里的Bit field,我觉得UNIX中文件的Permission可以由其来实现(不过实际上不是这样的)。

typedef struct tagFile{
	unsigned char usrr:1;
	unsigned char usrw:1;
	unsigned char usre:1;
	unsigned char grpr:1;
	unsigned char grpw:1;
	unsigned char grpe:1;
	unsigned char othr:1;
	unsigned char othw:1;
	unsigned char othe:1;
} File;

于是我想到了就用C语言里自带的数据类型配以各种Bit Operator来模拟BitSet中的Set, Reset以及Test方法,写了一个很Naive的小程序:

#include <stdio.h>
#include <stdlib.h>
#define _MAX_ 8388608
#define _BIT_SIZE_ 10
#define _TYPE_SIZE_ 8

typedef unsigned char *BitSet;
typedef unsigned char Type;
typedef unsigned int  INT;

const Type bitset[_TYPE_SIZE_]   = {1, 2, 4, 8, 16, 32, 64, 128};
const Type bitreset[_TYPE_SIZE_] = {254, 253, 251, 247, 239, 223, 191, 137};

INT getBytes(INT size)
{
    return (size/sizeof(Type)+1);
}

BitSet init(INT size)
{
    BitSet bs = (BitSet)malloc(getBytes(size));
    BitSet p; int i;
    for(p = bs, i = 0; i < getBytes(size); ++p, ++i)
        *p = 0;
    return bs;
}

void set(BitSet bs, INT index)
{
    INT item   = index / sizeof(Type);
    INT offset = index % sizeof(Type);
    *(bs+item) = *(bs+item) | bitset[offset];
}

void reset(BitSet bs, INT index)
{
    INT item   = index / sizeof(Type);
    INT offset = index % sizeof(Type);
    *(bs+item) = *(bs+item) & bitreset[offset];
}

INT test(BitSet bs, INT index)
{
    INT item   = index / sizeof(Type);
    INT offset = index % sizeof(Type);
    if(0 == (*(bs+item) & bitset[offset]))
        return 0;
    else
        return 1;
}

int main(void)
{
    /* Initialize the BitSet variable with a specified size. */
    BitSet bs = init(_BIT_SIZE_);

    /* Set all of the bits to 1. */
    int i = 0;
    for(i = 0; i < _BIT_SIZE_; ++i)
        set(bs, i);

    /* Reset one of the bitS to 0. */
    reset(bs, rand()%_BIT_SIZE_);

    /* Print all of the bits. */
    for(i = 0; i < _BIT_SIZE_; ++i){
        if(0 == test(bs, i))
            printf("0\n");
        else
            printf("1\n");
    }

    return 0;
}

我想应该找时间看看STL里的Source Code,毕竟自己的想法太幼稚和简单了。
看了看书上给出的提示,看看人家的代码多简洁(当然这里没有用到数组的动态分配),好在思路是一样的。Simplicity is beautiful。

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];

void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); }
void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); }
void tst(int i) { return a[i>>SHIFT] & (1<<(i & MASK));}

说几个细节,也算总结一下位操作符的使用:
想要对特定位做Reset操作,那么应该用&操作符,将指定位设为0即可完成操作(注意~操作符)。
想要对特定位做Set操作,那么应该用|操作符,将指定位设为1即可完成操作。
想要Test某一位的值,那么应该用&操作符,将指定位设为1即可实现。
另外在上述代码中,i & MASK实际上是进行的取余运算。

在本章节后面的习题中有这样一个问题,从区间[0, 10^7)中随机找出10^6个不能重复的数。关于随机的问题永远都是有“真伪”的,我们在这里可以使用一个很简单的算法来完成这样的操作,算法复杂度是O(N)。(N代表区间大小)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAXN 10000000
int x[MAXN];

int randint(int a, int b)
{	return a + (RAND_MAX * rand() + rand()) % (b + 1 - a);
}

int main(int argc, char *argv[])
{	int i, k, n, t, p;
	srand((unsigned) time(NULL));
	k = atoi(argv[1]);
	n = atoi(argv[2]);
	for (i = 0; i < n; i++)
		x[i] = i;
	for (i = 0; i < k; i++) {
		p = randint(i, n-1);
		t = x[p]; x[p] = x[i]; x[i] = t;
		printf("%d\n", x[i]);
	}
	return 0;
}

写到这里我想起了洗牌问题(Shuffling),通常来说有两种洗牌方法:

1. for i:=1 to n do swap(a[i], a[randomIndex(1,n)]);
2. for i:=1 to n do swap(a[i], a[randomIndex(i,n)]);

我们如何判断这两种方法哪一个是公平的。所谓洗牌,其实就是求出一组数字的一个Random Permutation,而一组大小为N的数字,其全排列的个数应该是N!,因此公平的洗牌方法应该是得出这N!(或者是N!的倍数)种情况下的一种,显然第二种方法是公平的,又因为N^N不能整除N!,所以第一种洗牌方法不是公平的。这样的Shuffling算法也被称为Fisher-Yates Shuffling算法。

C Programming Language

C语言是自己系统学习的第一门高级程序语言,本科时用其写过一些数据结构和算法的基础程序,来到USC以后又用它写了Master期间Algorithm课程的作业。虽然用C语言马上就要进入第六个年头了,但是感觉对语言的理解还不是那么系统和深入,对一些有Trick的地方也还没能够熟练掌握。去年末,在C语言之父Dennis Ritchie去世之际购买了<<C Programming Language>>这本经典著作,决定认真研读一番,感受感受大师的风范,同时也为自己学习GNU C Library, Berkeley DB以及Nginx等相关Open Source Project打下基础。这篇文章就是对该书的阅读笔记。

Writing

比起广为被人诟病的Speaking能力,中国留学生的Writing能力也是很弱的。虽说我们在TOEFL考试中的Writing部分都能取得不错的高分,但是我们在写作逻辑、用词上还是有极大缺陷的。计划开一个关于Writing的专题,把自己平时学习和实践中对于写作的收获进行收集和整理,希望通过这样的举动能够真真切切地提升自己的写作水平。

逃不过的终究是命运的捉弄

在每一个生日前夕,一个人找一个安静的地方,回想一下过去一年发生的事情,写下一篇短短的文章,这样的纪念方式很是让自己舒服。去年的那篇在这里儿呢。

最近有两件事情给我的触动和影响很大。一是两位中国留学生被枪杀的事件,二是有关11-12欧洲冠军联赛的两场半决赛的。

来美国以前就听说过USC周边的治安不太好,当时也没当作一回事,总是觉得这样的事情是不会发生在自己身上的,结果两名EE专业的学长学姐就在离我现在居住的房子不远的地方被残忍且没有人性的凶手杀害了。我敢确定的是我被惊呆了,我意识到了生命的无常和脆弱,同时也受到了足够的惊吓;另外我也确定很多同样在美国留学的同学不会因为这件事情而有所触动和思考,因为离他们“太远了”。

两场欧冠半决赛,近五年世界足球的最强三人,梅西、C罗、卡卡都罚丢了点球,自己所在的球队也因此被对手淘汰出局。看见他们痛苦的表情和伤心的泪水,我问自己:为什么罚失点球的恰恰是他们三人,为何他们拥有这么多球迷如此多金钱、荣誉也还会痛苦和哭泣。足球对于他们到底意味着什么?

不知道大家有没有在生活中发现,有的人运气就是会好一点,而有的人往往付出了很多却没有得到应有的回报。高考的时候有没有发现身边一些不太爱学习的同学反而考上了最为顶尖的学府,而好多那些平日里刻苦勤奋的同学却名落孙山;找工作或者申请出国时,是不是也有类似的情况出现,为什么有的人条件没我好,却得到了更好的结果。为什么有的人可以爆发,而我却总是失误呢?有多少人还记得08年北京奥运会上那个将自己最后一发子弹打到了对手靶子上的美国哥们,这样的事情在平日的训练里又多久才会出现一次呢?

从小在各种电影电视剧里就听说过:天命难为、天意弄人这样的词语。那是不是我们这样渺小和浅薄的凡人都不能逃过命运的捉弄呢?

我才刚刚年满23岁,不是要告诉自己已经到了知天命的年龄,更不是要为自己的不足和缺失寻找借口。只是想要提醒自己:努力做自己,做最真实做最好的自己,去掉所有外界的干扰听到内心最为真切的声音和呼喊。这样才能让自己在被命运捉弄时少一些失落和遗憾。这一点很难,真的很难,不在乎别人的眼光,不在乎世俗的评价,很难很难,但是如果可以做到这一点了,真的会让自己非常非常舒服,我相信那是难以体会到的轻松和愉悦。

照例对自己提出一些要求:

积极、向上、阳光。总的来说我觉得这一点自己做得还蛮好的,前几天看《非诚勿扰》,里面出来一个“苦大仇深”的男嘉宾,当然他可能因为在还很小的时候就背负上要为家里偿还债务的负担,所以导致他的心态和心智发生了一些变化。乐嘉说得很好:要努力,也要够聪明,一件事情你用健康阳光的心态去对待,这样可能别人就会很尊重你,如果你把事情弄得沉重残酷了,大家就会远离和躲避你。

保持平和的心态,少发怒少烦躁。

善于接受别人的意见和批评。这一点十分重要,而且吧我觉得不论是来自谁的意见和批评,都应该虚心和用心去聆听和思考。

在充满世俗的世界里保持自己的简单,但绝不是单纯。经常听到身边的朋友抱怨说现在很难交到知心朋友,很多时候那些所谓的朋友可能都是因为“有所需、有所求”才来接触自己的。听到这样的话,一方面是无赖和失望,毕竟现在我们都长大了,可能很难找到以前那样单纯和美好的友谊。另一方面也表示理解,我觉得保持自己的简单和善良就好了,当然自己也应该是清醒的。

Stay Hungry, Stay Foolish.这点不用多加阐述了,努力、专注,学业和事业绝对是这个年龄阶段最为重要的事情。

祝自己23岁生日快乐!(现在的文字太烂了,特别是这中文)。

非诚勿扰

昨天看了父亲的最新日志,其实主要是一些旅游图片,产生了些小小的联想。父亲今年春节去三亚进行了一把“非诚勿扰2”之旅。去石梅湾艾美酒店,去那美丽的“鸟巢”屋。

照片很美,给人以很舒服的感觉,非常亲近大自然。我还和父亲开玩笑说道:我以后结婚举行婚礼就要去那里。非诚勿扰系列电影是国产影片中我最最喜欢的,除了非常喜欢葛大爷和舒淇以外,感觉整个片子特别有味道,许多对白都是很令人回味的。最喜欢的一句话就是“婚姻怎么选都是错的,长久的婚姻就是将错就错;一辈子很短,让我们将错就错”。我完全不知道婚姻是什么,只是觉得每一段感情最最重要的就是相互包容和理解,也就是让大家将错就错。身边的好朋友们有的已经将要或者是想要接触婚姻了,每每听到这样的消息,除了祝福他们以外,我总是问自己,为什么我一点都不知道这是什么东西,也从来没有想过什么是婚姻。我只是觉得现在这个阶段,婚姻这种事绝对是不要提的,爱情是很好的是可以的,简简单单地享受纯粹的爱情就是最好最好的事情了。

这段时间不经意间听到了很多身边的“八卦”,这着实让我发现自己对于身边的人际关系和社交圈子了解太少。听说有男生可以同时和一打女生保持暧昧的关系,又听说来到美国后还真存在“炮友”这样的事情。对于这些“应该”存在的客观事实,我除了感到唏嘘以外似乎也没什么好评论的。

这学期稍显“兵荒马乱”,究其缘由还是自己想要的东西太多了不能专注于一些事情。我只想说如果仅仅是想要在某件事情上留个名字的话,那就还是不要去做了,当然对于爱情和那被称为婚姻的东西更是这样,如果是寂寞了或者是想要因此来满足自己的一些虚荣那还是不要去触碰这样美好的事物了,非诚勿扰就是这样!

特别喜欢Taylor Swift的Love Story以及其MV,喜欢听Enya和Bandari,我还自己的内心深处还是保留着许多简单的梦想和期待的。

PS:今天迎来了在美国的第一个春假,刚开始想要找点乐子让自己放松放松,可是发现各种约不到人(Lakers vs Celtics的比赛估计也去不了),所以我决定每天睡8小时以上,好好休息休息,剩余的时间还是以学业(事业)为主,看闲书和运动为辅吧。就像Advisor说的,spring break is for kids.

Mike's hard with Zen and the Art of Motorcycle Maintenance.

 

CSCI599 NewSQL Database Management Systems

  • Facebook
  • Google+
  • LinkedIn
  • Twitter
  • Tumblr
  • Flickr
  • YouTube
  • Delicious