算法的力量

一直以来我们都说算法是很有力量的,以前程序员杂志也有一起专门讲算法的力量,李开复的文章就叫这个名字。我一直觉得算法很重要,但是一直没有深入研究过。今天看《编程珠玑》,第一个问题就体现出了算法的巨大力量。

书上大致是这么一个故事:一个程序员要对拥有10 000 000条7位数的文件进行排序,而可使用的内存只有1M。这应该怎么做?

一般来说,如果没有那个1M的限制,在现在的拥有大量内存的机器上,我会想将数据都读出来,找一个很好的算法,比如快速排序来进行。我虽然写过快排的代码,但是要应用在这个地方来说可能还是会占用不少的调试时间以边将其写正确。10 000 000条记录,每条7位数,如果按照7个char(单字节)来存储,会占用 7*10000000/1024 Bytes=68359 KB=66 MB;如果使用整数(int,4字节)来表示,会占用大约38M内存。至于算法时间,由于快速排序的最好时间是O(nlgn)的样子吧,由于n很大,这个数字估计也不小了。毕竟会来回好多趟呢。而且还有一个1M的限制呢。

但是《编程珠玑》的作者最后给出的解决方法是使用位向量来解决的,对所有的数据只处理一遍,数据读完之时,排序也好了。所以时间就直接是O(n)了。不过呢,作者给出的程序(http://www.cs.bell-labs.com/cm/cs/pearls/bitsort.c )没有给出实例。我用一个程序写了一个将10 000 000个数字写到文件中的程序,由于觉得打乱次序太烦,直接排好序又不好,我就倒序输出了。本来我还用了一个随机数,以便如果生成的100以内的数字小于25就多减少一个,以便不要全部都写进去(大概算作是1/4的概率吧),结果是基本都没有效用。最后生成的文件有70多M(并不都是之前条件所讲的7位数。当1/4起作用的时候实际上只能得到大约一半的数据,用时3秒,如果没有起作用,用时6秒。虽然我个人感觉好像过了好长时间)。我使用作者的程序测试了一下,仅仅是排序还是很快的,但是输出到屏幕就慢了。所以我稍微改了一下,输出到文件。程序如下(做了少许修改:加上时间测试,从文件读取并输出到文件):

/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */
/* bitsort.c -- bitmap sort from Column 1
* Sort distinct integers in the range [0..N-1]
*/
#include <stdio.h>
#include <time.h>
#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)); }
int test(int i){ return a[i>>SHIFT] & (1<<(i & MASK)); }
int main()
{
    int i;
    FILE *f = fopen ("test.txt", "r");
    long t = time(NULL);
    //for (i = 0; i < N; i++)
    //    clr(i);
    // Replace above 2 lines with below 3 for word-parallel init
    int top = 1 + N/BITSPERWORD;
    for (i = 0; i < top; i++)
    a[i] = 0;
   
    while (fscanf(f, "%d", &i) != EOF)
        set(i);
    fclose(f);
    printf("%ld/n", (time(NULL)-t));
    f = fopen ("t.txt", "w");
    t = time(NULL);
    for (i = 0; i < N; i++)
        if (test(i))
            fprintf(f, "%d ", i);
    fclose(f);
    printf("%ld/n", (time(NULL)-t));
    return 0;
}

根据测试,对于那个70多M的文件,一般来说,读取排序那一部分耗时3~4秒,输出到文件耗时5~6秒。这符合了作者在书中讲到的需要10秒钟的结果。至于内存和CPU占用,在我的双核上CPU占用总是保存在50%左右,内存占用为1900K,稍微超出了1M的需求,但是想到一个100多万个元素的整数数组就得占用1M多的内存,也能想下去了。

作者对于初始化数组使用了两种方法,我都试验过,对性能几乎没有什么影响(现在的代码中注释掉的是原来的代码)。作者也说过,这种方法仅限于没有重复的情况,不过确实是很好的方法。非常的Nice。

你可能感兴趣的:(算法的力量)