这个练习中,我们将基于《C语言练习01:单向链表的实现》中实现的链表程序实现一个搜索某一范围内所有质数的程序。
假定我们搜索的范围在DWORD(unsigned __int32)范围内。
2是唯一的偶数质数,2以后所以后的质数都是奇数。所以我们程序中搜索质数的时候没有从2开始,而是从3开始,并且每次递增的步长是2。
没有用埃拉托色尼筛选法。如果用埃拉托色尼筛选法的话速度更快,但是在这个练习中我用自己写的一个小小的算法作为一次锻炼。
以下是代码:
先给出现关的类型定义,免得读者简单那个奇怪的 Item 类型不知道是什么东西。
/* * CustomizedTypes.h * * Customized types definition * * By Yaping Xin 110929 * */ #ifndef _CustomizedTypes_H_ #define _CustomizedTypes_H_ /* typedef unsigned __int32 Item; */ typedef unsigned __int32 UInt32; struct Item_Struct { UInt32 data; UInt32 offset; }; typedef struct Item_Struct Item; Item CreateEmptyItem(); Item CreateItem(unsigned __int32 data); int CompareItems(Item value0, Item value1); typedef struct { UInt32 x; UInt32 y; } ItemPair; #endif /* _CustomizedTypes_H_ */
/* * CustomizedTypes.c * * Customized types implementation * * By Yaping Xin 110929 * */ #include <string.h> #include "CustomizedTypes.h" Item CreateEmptyItem() { Item result; result.data = 0; result.offset = 0; return result; } Item CreateItem(unsigned __int32 data) { Item result; result.data = data; result.offset = data; return result; } int CompareItems(Item value0, Item value1) { return memcmp(&value0, &value1, sizeof(Item)); }
好,下面就是正式的计算了:
// SearchPrime.c : Defines the entry point for the console application. // #include <stdio.h> #include "CustomizedTypes.h" #include "List.h" #include <time.h> UInt32 SqrtUInt32(UInt32 n) { UInt32 a; for (a=0; n>=(2*a)+1; n-=(2*a++)+1); return a; } void SearchPrime(List *list, UInt32 vCurrent, UInt32 maxRange) { UInt32 vNext; UInt32 vLimit; UInt32 vLimitRange; Node * node; if(vCurrent < 13) { vCurrent = 13; } if(vCurrent >= 0xFFFFFFFD) { return; } vNext = vCurrent + 2; if(vNext > maxRange) { return; } if(vNext >= 0xFFFE0001) { vLimit = 0x0000FFFF; vLimitRange = 0xFFFFFFFF; } else { vLimit = SqrtUInt32(vNext); vLimitRange = (vLimit + 1) * (vLimit + 1); } while(vNext <= maxRange) { for(node = list->begin; node != list->end; node = node->next) { if(node->item.data > vLimit) { break; } while(node->item.offset < vNext) { node->item.offset += node->item.data; } if(node->item.offset == vNext) { goto NEXT_INT; } /* if(vNext % node->item.data == 0) { goto NEXT_INT; } */ } ListAppendNode(list, CreateItem(vNext)); NEXT_INT: vNext += 2; if(vNext > vLimitRange) { vLimit ++; vLimitRange = (vLimit + 1) * (vLimit + 1); } } } int main(int argc, char* argv[]) { List list; long time_start; long time_end; UInt32 rangeBegin; UInt32 range; InitializeList(&list); ListAppendNode(&list, CreateItem(3)); ListAppendNode(&list, CreateItem(5)); ListAppendNode(&list, CreateItem(7)); ListAppendNode(&list, CreateItem(11)); ListAppendNode(&list, CreateItem(13)); range = 0x0000FFFF; rangeBegin = 13; time_start = clock(); SearchPrime(&list, rangeBegin, range); time_end = clock(); printf("[R:%8X][C:%8X][V:%8X][T:%6ld]\n", range, list.count + 1, list.end->item.data, time_end - time_start); DisposeList(&list); return 0; }
计算结果:
搜索 3 -- 0x0000FFFF 范围内的质数,在一台很普通的计算机上耗时3ms。(我相信绝大多数读者的计算机配置都不低于这台机器)
把 SearchPrime.c 中的变量range的值改为 0x000FFFFF,耗时65ms。
把 range 的值改为 0x00FFFFFF,耗时1862ms。
这个程序还能够继续优化吗?让我们把这项工作留到以后吧。
算法的解释暂略。这个计算速度比C语言入门教科书上的那些程序要高出好几个数量级,但相对于那些算法设计精良的程序来说,这个运算速度毫无可夸耀之处。仅仅是一次小练习而已。