编程珠玑第一章中的代码

使用位图法对七位正整数进行排序的算法。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

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

/*
 * i >> SHIFT      :i / (1 << SHIFT) 即i / (1 << 5) = i / 32, 这样获取i在数组a中的下标值,如i=100,则100/32=3,
 *                   所以100在数组a中的位置是3。
 * 1 << (i & MASK) :确定应该在当前数组位置中的第几个二进制位设置为1。
 * MASK的推导的过程:
 * 数字i在当前下标的数组内对应的二进制位是i - (i / 32) * 32,如i=100,就是100 - (100/32)*32 = 100 - 96 = 4
 * 因为前面已经确定100在a[3]中,前面还有a[0],a[1],a[2]三个元素,这三个元素所占的二进制位数正好是96个,所以
 * 100应该在a[3]中第4个二进制位。这是思路,下面说怎么确定MASK的值。
 * 在计算机里加减乘除都是用补码进行的,正整数的补码等于整数本身,负整数的补码等于其反码加1,所以,以i=100为例
 * i - (i / 32) * 32   = 100       - (100 / 32) * 32 = 100 - 96 = 100 + (-96) = 4,等价于
 * i - ((i >> 5) << 5) = 100       - ((100 >> 5) << 5)
 * 为了方便书写,这里用单字节表示。
 *                     = 0110 0100 - (0110 0100 & 1110 0000)
 *                     = 0110 0100 - (0110 0000)
 *                     = 100 + (-96)
 * -96的反码 = 0110 0000按位取反,即: 1001 1111
 * 对应的补码= 反码加1,即: 1010 0000, 故,
 *                     = 0110 0100 + 1010 0000 = 1 0000 0100
 * 结果的最高位多了一个1,这是溢出位,所以结果就是0000 0100 = 4
 * 即 100 + (~(100 & 0xe0) + 1) = i + (~(i & 0xe0) + 1) == i & 0x1f
 * 所以MASK = 0x1F
 */

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

int main(void)
{
    int i, count=100;
    srand((unsigned)time(NULL));
    for (i = 0; i < N; i++)
        clr(i);
    while (count > 0)
    {
        i = rand() % 100;
        set(i);
        count--;
    }
    for (i = 0; i < N; i++)
    {
        printf("%d : ", i);
        if (test(i))
            printf("%d", i);
        else
            printf("%d", 0);
        printf("
");
    }

    for(i = 0; i < (1 + N/BITSPERWORD); i++)
    {
        printf("%d : %d
", i, a[i]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lrxing/p/7047628.html