日历
网志分类
· 所有网志 (26)
· C100 (16)
· Algorithm (0)
· Architecture (1)
· Linux_Programming (8)
· ICS (0)
· 未分类 (1)
最新的评论
站内搜索
友情链接
· 我的歪酷 非非共享界

订阅 RSS

0007707

歪酷博客

把握自己


落日黄昏 @ 2007-11-07 14:08

去掉所有的行尾空格:  %s/\s\+$//

去掉所有的空白行:      %s/\(\s*\n\)\+/\r/

去掉所有的"//"注释:    %s!\s*//.*!!

去掉所有的"/*...*/"注释:    %s!\s*/ \*\_.\{-}\*/\s*! !g

删除DOS方式的回车^M:%s/r//g
:%s= *$== 删除行尾空白:
:%s/^(.*)n1/1$/ 删除重复行:
:%s/^.{-}pdf/new.pdf/ 只是删除第一个pdf:
:%s/<!--_.{-}-->// 又是删除多行注释(咦?为什么要说「」呢?)
:g/s*
^$/d 删除所有空行 :这个好用有没有人用过还有其他的方法吗?
:g!/^dd/d 删除不含字符串'dd'的行
:v/^dd/d 同上 (译释:v == g!,就是不匹配!)
:g/str1/,/str2/d 删除所有第一个含str1到第一个含str2之间的行
:v/./.,/./-1join 压缩空行
:g/^$/,/./-j 压缩空行
ndw 或 ndW 删除光标处开始及其后的 n-1 个字符。
d0 删至行首。
d$ 删至行尾。
ndd 删除当前行及其后 n-1 行。
x 或 X 删除一个字符。
Ctrl+u 删除输入方式下所输入的文本。
^R 恢复u的操作
J 把下一行合并到当前行尾
V 选择一行
^V 按下^V后即可进行矩形的选择了
aw 选择单词
iw 内部单词(无空格)
as 选择句子
is 选择句子(无空格)
ap 选择段落
ip 选择段落(无空格)
D 删除到行尾
x,y 删除与复制包含高亮区
dl 删除当前字符(与x命令功能相同)
d0 删除到某一行的开始位置
d^ 删除到某一行的第一个字符位置(不包括空格或TAB字符)
dw 删除到某个单词的结尾位置
d3w 删除到第三个单词的结尾位置
db 删除到某个单词的开始位置
dW 删除到某个以空格作为分隔符的单词的结尾位置
dB 删除到某个以空格作为分隔符的单词的开始位置
d7B 删除到前面7个以空格作为分隔符的单词的开始位置
d) 删除到某个语句的结尾位置
d4) 删除到第四个语句的结尾位置
d( 删除到某个语句的开始位置
d) 删除到某个段落的结尾位置
d{ 删除到某个段落的开始位置
d7{ 删除到当前段落起始位置之前的第7个段落位置
dd 删除当前行
d/text 删除从文本中出现“text”中所指定字样的位置,
一直向前直到下一个该字样所出现的位置(但不包括该字样)之间的内容
dfc 删除从文本中出现字符“c”的位置,一直向前直到下一个该字符所出现的位置(包括该字符)之间的内容
dtc 删除当前行直到下一个字符“c”所出现位置之间的内容
D 删除到某一行的结尾
d$ 删除到某一行的结尾
5dd 删除从当前行所开始的5行内容
dL 删除直到屏幕上最后一行的内容
dH 删除直到屏幕上第一行的内容
dG 删除直到工作缓存区结尾的内容
d1G 删除直到工作缓存区开始的内容

:s/str1/str2/       用字符串 str2 替换行中首次出现的字符串 str1

:s/str1/str2/g      用字符串 str2 替换行中所有出现的字符串 str1

:.,$ s/str1/str2/g  用字符串 str2 替换正文当前行到末尾所有出现的字符串 str1

:1,$ s/str1/str2/g  用字符串 str2 替换正文中所有出现的字符串 str1

:g/str1/s//str2/g   功能同上

从上述替换命令可以看到:g 放在命令末尾,表示对搜索字符串的每次出现进行替换;不加 g,表示只对搜索

字符串的首次出现进行替换;g 放在命令开头,表示对正文中所有包含搜索字符串的行进行替换操作。

给出一个字符串,可以通过搜索该字符串到达指定行。如果希望进行正向搜索,将待搜索的字符串置于两个

/”之间;如果希望反向搜索,则将字符串放在两个“?”之间。例如:

:/str/                      正向搜索,将光标移到下一个包含字符串 str 的行

:?str?                      反向搜索,将光标移到上一个包含字符串 str 的行

:/str/w file                正向搜索,并将第一个包含字符串 str 的行写入 file 文件

:/str1/,/str2/w file        正向搜索,并将包含字符串 str1 的行至包含字符串 str2 的行写入 file 文件




 
落日黄昏 @ 2007-09-20 11:07

扩充Finbonacci数(Ex_fib)
定义一组叫做扩充Fibonacci数如下-已知X与Y两个数,于是扩充Fibonacci数F(i)为:
                   F(i) = 1      (i = 0)
                   F(i) = 1      (i = 1)
                   F(i) = X×F(i-2) + Y×F(i-1)   (i > 1)
因此,当X=Y=1时,这一组扩充Fibonacci数就变成了一般的Fibonacci数了。请写一个函数,接收一个n的值,不用数组和递归的方法算出下面的结果:F(0)×F(n)+
F(1)×F(n-1)+...+F(i)×F(n-i)+F(n-1)×F(1)+F(n)×F(0)



 
落日黄昏 @ 2007-09-19 15:23

快速Fibonacci数算法(Fib_Quick)
在非递归的Fibonacci程序中,在算f(n)的时候最多不超过n-2个加法,请编写一个更快的程序,或许可以用乘法。如果每一个乘法用m单位时间,每一个加法用p单位时间,于是非递归的写法因为最多有n-2个加法,因此最多用(n-2)×p的时间。请问,改良程序要用多少时间?假设只考虑加法和乘法。

解答:
考虑:
f(n) = f(n-1) + f(n-2) = 1×
f(n-1) + 1×f(n-2)
f(n-1) = f(n-1) = 1×f(n-1) + 0×f(n-2)
可以用矩阵乘法来表示两个式子:
|f(n)   | = | 1 1 | ×|f(n-1)|
|f(n-1)|    | 1 0 |   |f(n-2)|



 
落日黄昏 @ 2007-09-19 12:29

Fibonacci数列非递归解(Fib_Iteration)
Fibonacci数列f1,f2,...,fn的定义如下:
                fn =    1            n = 1
                fn =    1            n = 2
                fn = fn-1+fn-2   n > 2   
请不要用递归方法,也不用数组,写一个函数,它接受n的值,传回fn。

解答:
这个问题比较简单,如果已经有fi-1和fi-2,那么
fi = fi-1 + fi-2,下次计算fi+1时需要fi和fi-1,保留fi-1和fi的值,丢弃fi-2的值即可。

代码:
long int fib_iter(int n)
{
    long int f1 = 1, f2 = 1;
    long int temp;
    int i;
    if(n <= 2) return 1;
    else
    {
        for(i = 3; i <= n; i++)
        {
           temp = f1 + f2;
           f2 = f1;
           f1 = temp;
        }
       return temp;
    }
}


思考问题:
(1)如果用传统的递归办法,程序如下所示
long int fibonacci(int n)
{
    if(n <= 2)   return 1;
    else return fibonacci(n-1)+fibonacci(n-2);
}
请用n=13做输入,用手算方式看看有哪些计算是重复做过的。
n = 13:1次;n = 12:1次;n = 10:2次
n = 93次n = 85次n = 78次n = 613次n = 521次n = 4:34次n = 3:55次n = 2:88次n = 1:143次;呈一个fibonacci数列的规律。

(2)请问算fn时一共用了多少个加法?请导出一个公式。

(3)k阶的Fibonacci数列问题:前k个数值=1,fi
(k) = fi-1(k) + fi-2(k) + ... + fi-k+1(k)(i > k),当k=2时就是一般的Fibonacci数列。请不要用递归,或许可用数组,写一个函数fibK(n,k),接收n与k,算出fj(k)。

(4)为什么函数要用unsigned long而不用int?



 
落日黄昏 @ 2007-09-19 09:28

sscanf()-从一个字符串中读进与指定格式相符的数据

函数原型:
int sscanf(const char *buffer, const char *format [,argument ] ... );
int scanf( const char *format [,argument]... );
它强大的功能在于对format的支持上。

sscanf与scanf类似,都是用于输入,只是后者以屏幕(stdin)做为输入源,前者以固定字符串为输入源。其中的format可以是一个或者多个:
{
%[*][width][{h|l|I64|L}]type|' '|'\t'|'\n'|非%符号}
(1)*也可以用于格式中,加了*表示跳过此数据不读入到参数中。
(2){a|b|c}表示a、b、c中选一,[d]表示d可有可无。
(3)width表示读取数据的宽度。
(4){
h|I|l64|L}:参数的size,h通常表示单字节size,I表示2字节,L表示4字节(double除外),l64表示8字节size。
(5)type:%s,%d之类。
(6)
%*[width][{h|l|I64|L}]type表示满足该条件的被过滤掉,不会向目标参数中写值。
支持集合操作:   %[a-z]   表示匹配从a到z的任意字符,贪婪性
                           %[aB']   表示匹配a、B、'中的一员,贪婪性
                           %[^a]    表示匹配任意非a的字符,贪婪性
类似于正则表达式,但没有正则表达式强大。

sscanf使用实例:
1、常见用法

char buf[512] = {0};
sscanf("123456 ", "%s", buf);
printf("%s\n", buf);
结果为:123456
2、取指定长度的字符串
sscanf("123456 ", "%4s", buf);
printf("%s\n", buf);
结果为:1234

3、取到指定字符为止的字符串
sscanf("123456 ", "%[^ ]", buf);
printf("%s\n", buf);
结果为:123456

4、取仅包含指定字符集的字符串
sscanf("123456abcdedfBCDEF","%[1-9a-z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf

5、取到指定字符集为止的字符串
sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf

6、给定一个字符串,取其中两个特定字符之间的字符串sscanf("abiios/12DDWDFF@122","%*[^/]/%[^@]", buf);
printf("%s\n", buf);
结果为:
12DDWDFF
7、取"Hello, world"中的world(注意,,后面有个空格)
sscanf("hello, world", "%*s%s", buf);
printf("%s\n", buf);
结果为:world



 
落日黄昏 @ 2007-09-18 15:04

数值自乘非递归解(I_Power)
求m的n次方(m、n均为正整数)。请开发一个非递归的程序求解,但是和前面的递归算法效率相同。

解答:

1、以m的45次方为例
,45(10进制)=101101(2进制),所以
m的45次方 = m的(1×2的5次方+1×2的3次方+1×2的2次方+1×2的0次方)次方
                    = (m的2次方的5次方)×(m的2次方的3次方)×(m的2次方的2次方)×(m的2次方的0次方)
在45(10进制)=101101(2进制)中,值为1的那一位(比如第i位,从右往左位0,1,2,3....),在m的指数中就有m的2的i次方这一项,所以可以依次把m的2的0次方、m的2的1次方、m的2的2次方...m的2的i次方、m的2的i+1次方...求出来,这正好对应着n的二进制表示中从右向左的顺序。
注意到m的2的i+1次方=m的(2的i次方×2)次方=(m的2的i次方)的平方,且m的2的0次方=m,所以m的2的i次方只是求m的自乘而已。一开始为m,下次为m的平方,再下一次为m的4次方,....。
程序中用temp来保存最终结果,初值为1。在while循环中,测试n的值。如果n是奇数,表示n的二进制表示最右边的数为1,于是m目前的值乘到temp中。不管如何,都把m求平方,把n减半,所以m的值就依次是m,m平方,m的4次方,...,对应着从右到左,每一位的位置的值。

代码:
unsigned long int I_Power(unsigned long int m, unsigned long int n)
{
    unsigned long int temp = 1;
    while(n > 0)
    {
       if(n & 0x1UL)
          temp *= m;
       m *= m;
       n >> 1;
    }
    return temp;
}

分析:
这个while循环最多执行logn+1次,原因是每执行一次,n的值就会减半,所以最多logn+1次后n就会是0(为什么?)每执行一次时,最多做两次乘法,依次是m的自乘,另一次是把m乘入temp,所以最多做2(logn+1)次乘法,所以速度会比传统的乘法快。传统方法求m的45次方要乘44次,但此处只需14次。

思考问题:
(1)这个程序的乘法数目还可以降低,其实保存完整的m,m平方,m的4次方,...是没有多大必要的,请以此为出发点来改良程序。
(2)请用手算的方式列出2(13),
2(15),2(16)的过程,是不是这个程序也会得到算2(15)要多几个乘法呢?请问,一般而言,n是多少时会比较慢?
(3)如果用的硬件系统会检测出整数溢出,那么这个程序可能会有问题,因为求出m的45次方后,程序还会多算m的64次方,这个就可能会产生溢出了,请修改程序克服这个问题。



 
落日黄昏 @ 2007-09-18 14:12

Linux下的软件格式:
软件后缀为.rpm最初是Red Hat Linux提供的一种包封装格式,现在许多Linux发行版本都使用;后缀为.deb是Debain Linux提供的一种包封装格式;后缀为.tar.gz、tar.Z、tar.bz2或.tgz是使用Unix系统打包工具tar打包的;后缀为.bin的一般是一些商业软件。
 
RPM格式软件包的安装
RPM全称是Red Hat Package Manager(Red Hat包管理器)。RPM本质上就是一个包,包含可以立即在特定机器体系结构上安装和运行的Linux软件。RPM示意图见下:

大多数Linux RPM软件包的命名有一定的规律,它遵循名称-版本-修正版-类型。例如,MYsoftware-1.2-1.i386.rpm
1、安装RPM包软件:
# rpm -ivh MYsoftware-1.2 -1.i386.rpm
RPM命令主要参数:
                -i 安装软件。
                -t 测试安装,不是真的安装。
                -p 显示安装进度。
                -f 忽略任何错误。
                -U 升级安装。
                -v 检测套件是否正确安装。
2、卸载软件
# rpm -e 软件名  
需要说明的是,代码中使用的是软件名,而不是软件包名
3、强行卸载RPM包
有时除去一个RPM是不行的,尤其是系统上有别的程序依赖于它的时候。如果执行命令会显示如下错误信息:

        # rpm -e xsnow
        error: removing these packages would break dependencies:
        /usr/X11R6/bin/xsnow is needed by x-amusements-1.0-1
在这种情况下,可以用--force选项重新安装xsnow:# rpm -ivh --force xsnow-1.41-1.i386.rpm
4、安装.src.rpm类型的文件
目前RPM有两种模式,一种是已经过编码的(i386.rpm),一种是未经编码的(src.rpm)。
    rpm --rebuild Filename.src.rpm
    这时系统会建立一个文件Filenamr.rpm,在/usr/src/redflag/RPMS/子目录下,一般是i386,具体情况和Linux发行版本有关。然后执行下面代码即可:
    rpm -ivh /usr/src/regflag/RPMS/i386/Filename.rpm

使用deb打包的软件安装
    deb是Debian Linux提供的一个包管理器,它与RPM十分类似。但由于RPM出现得早,并且应用广泛,所以在各种版本的Linux中都常见到,而Debian的包管理器dpkg只出现在Debina Linux中。它的优点是不用被严格的依赖性检查所困扰,缺点是只在Debian Linux发行版中才能见到这个包管理工具。
1、安装
# dpkg -i MYsoftware-1.2.-1.deb
2、卸载
# dpkg -e MYsoftware

使用源代码进行软件安装
    和RPM安装方式相比,使用源代码进行软件安装会复杂一些,但是用源代码安装软件是Linux下进行软件安装的重要手段,也是运行Linux的最主要的优 势之一。使用源代码安装软件,能按照用户的需要选择定制的安装方式进行安装,而不是仅仅依靠那些在安装包中的预配置的参数选择安装。另外,仍然有一些软件 程序只能从源代码处进行安装。一般的安装步骤如下:
1、解压数据包
    源代码软件通常以.tar.gz做为扩展名,也有tar.Z、tar.bz2或.tgz为扩展名的。不同扩展名解压缩命令也不相同

2、编译软件
    成功解压缩源代码文件后,进入解包的目录。在 安装前阅读Readme文件和Install文件。尽管许多源代码文件包都使用基本相同的命令,但是有时在阅读这些文件时能发现一些重要的区别。例如,有 些软件包含一个可以安装的安装脚本程序(.sh)。在安装前阅读这些说明文件,有助于安装成功和节约时间。一般在安装软件以前要成为root用户。
通常的安装方法是从安装包的目录执行以下命令:
        gunzip soft1.tar.gz
        cd soft1
        # ./configure                 #配置#
        make                            #调用make#
        make install                  #安装源代码#
删除安装时产生的临时文件:
        # make clean
卸载软件:
        # make uninstall
有些软件包的源代码编译安装后可以用make uninstall命令卸载。如果不提供此功能,则软件的卸载必须手动删除。由于软件可能将文件分散地安装在系统的多个目录中,往往很难把它删除干净,应该在编译前进行配置。

.bin文件安装
    扩展名为.bin文件是二进制的,它也是源程序经编译后得到的机器语言。有一些软件可以发布为以.bin为后缀的安装包,例如,流媒体播放器RealONE。如果安装过RealONE的Windows版的话,那么安装RealONE for Linux版本(文件名:r1p1_linux22_libc6_i386_a1.bin)就非常简单了:
    #chmod +x r1p1_linux22_libc6_i386_a1.bin
    ./r1p1_linux22_libc6_i386_a1.bin
    接下来选择安装方式,有普通安装和高级安装两种。如果不想改动安装目录,就可选择普通安装,整个安装过程几乎和在Windwos下一样。
    .bin文件的卸载,以RealONE for Linux为例,如果采用普通安装方式的话,在用户主目录下会有Real和Realplayer9两个文件夹,把它们删除即可。





 
落日黄昏 @ 2007-09-18 12:35

数值自乘递归解(R_Power)
如果n与m是正整数,那么m的n次方就是把m连乘n次,这是一个很没有效率的方法。试编写一个更有效率的方法,并且分析程序中一共用了多少个乘法。应该以少于n-1个乘法为设计原则。

解答:
分情况讨论:
n =  0时,m的n次方= 1;
n = 2k为偶数时,m的n次方 = m的k次方×m的k次方;
n = 2k+1为奇数时,m的n次方 = m的k次方×m的k次方×m;
这样就可以写出解决这个问题的递归函数了。

每次分成两部分(m的k次方或者m的2k次方)之后,都要用一个乘法把m的k次方×m的k次方或者m×m的2k次方算出来;在最坏情况下,n为奇数,m的n次方
= m的k次方×m的k次方×m,因此在算m的2k次方时只少了1,不过,下一次递归就会算m的k次方,再算m的k次方×m的k次方。因此递归的层次最多为2×logn层。

代码:
unsigned long int recursive_power(unsigned long int m, unsigned long int n)
{
    unsigned long int temp;
    if(n==0) return 1;
    else if(n & 0x1UL == 0)
    {
       temp = recursive_power(m, n/2);
       return temp*temp;
    }
    else return m*recursive_power(m, n-1);
}

思考问题:
(1)请用手算模拟求2(13)2(15)2(17)。
(2)请算一算程序计算
2(7)2(8)2(9)...2(14)所花的时间,请问在时间上有没有什么异常的情况?能解释吗?
2(07)      0.000030s
2(08)      0.000032s
2(09)      0.000043s
2(10)      0.000031s
2(11)      0.000020s
2(12)      0.000038s
2(13)      0.000045s
2(14)      0.000032s
2(15)      0.000033s
(3)请手算以求2(11)2(13)2(15)的步骤来解答(2)中的异常情形。
(4)请把这个程序改成不用递归的方法;千万不要用堆栈来仿真。
(5)如果把每一次递归调用的n值输出出来,也许可以帮助解决(4);但这些值跟n的二进制表示法有什么关联呢?
(6)请扩充这个程序,使得m和n可以是负数或者0,如何处理那些不正常的情况?



 
落日黄昏 @ 2007-09-17 11:37

因子分解(Factor)
请写一个程序,读入一个正整数,把它的所有质因子找出来。例如输入72,72=2×2×2×3×3,于是质因子就有2和3;如果输入181944,181944=2×2×2×3×3×7×19×19,所有质因子为2、3、7和19。为方便起见,2×2×2×3×3可以用2(3)3(2)来表示,即如果分解开来有a×a×a×a...(b个a相乘),那么输出就是a(b)。

解答:
可以用2一直去除该正整数,直到除不尽为止(用循环,循环的次数就是因子2的指数),然后再用
3,5,7,...这些数依次去除,这样即可得到各因子及其对应的指数。
for(i = 0; work % k == 0 && work > 1; work /= k, i++) ;其中work就是待求的正整数

代码:
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
#define SAVE_FACTOR(fact, exp)   { if(exp>0) factors[count] = fact, exps[count++] = i; }
int main(void)
{
    long int factors[MAXSIZE];
    long int exps[MAXSIZE];
    long int n, work;
    int count = 0;
    int i, k;
    char line[100], *dummy;
    printf("\nFactorization by Division Program");
    printf("\n=========================");
    printf("\n\nInput a positive integer ---> ");
    fgets(line, 100, stdin);
    n = strtol(line, &dummy, 10);
    for(i = 0, work = n; (work & 0x01UL) == 0 && work > 1; work >> 1, i++)
       ;
    /*正整数被2除可以用右移1位完成*/
    SAVE_FACTOR(2, i);
    for(k = 3; k <= work; k += 2)
    {
       for(i = 0; work % k == 0 && work > 1; work /= k, i++)
          ;
       SAVE_FACTOR(k, i);
    }
    printf("\n%ld = ", n);
    for(i = 0; i < count; i++)
       printf("%ld(%ld)", factors[i], exps[i]);
}

思考问题:
(1)程序中都有work>1的测试,究竟这有没有必要?请说明理由。
解答:没有必要,因为到最后work一定会变为1,而1%任何数都不为0,循环一定会中止。
(2)在第2个for循环中,分别用k=3,5,7,9,11,13,15....去除,但事实上在用3去除的时候就已经去掉了3的所有倍数,所以9和15是不可能整除work的。请修改程序,使得只会用质数去除,9,15...等不会出现。
解答:可以先用一个质数产生器产生2~输入正整数之间的所有质数,然后再用这些质数去除该正整数。
(3)请问这个程序的输出为什么都是质因子?
解答:假设产生了一个合数因子r = p×q(p、q是质数,且p<r,q<r),按照程序的算法,在循环中,k先达到p和q的值,应该把正整数work中所有的质因子p和q已经除去,矛盾。所以,不可能产生非质因子。
(4)在程序中,factors[ ]和exps[ ]这两个用来存放因子和对应的指数的数组是必要的,为什么?请修改程序,不用这两个数组,但效果完全相同。
解答:每次循环产生的结果直接输出即可



 
落日黄昏 @ 2007-09-16 15:22

**** 线性筛法(L_Sieve) ****
在做筛法(Sieve)求质数的问题时,在删除非质数的数据时,有很多是重复删除的。例如有一个数是3×7×17×23,那么在删除3的倍数时会删除它,删除7、17、23的倍数时也会删除它。看起来好像没多大关系,因为只是把一个值存入对应的位置而已,但是,当要求的质数范围很大时,这一类型有很多质因子的合数就会越来越多,因此程序的效率会大打折扣。基于筛法的观点,请写一个程序,在删除非质数的时候“绝对”不做重复的工作。

参考文献:
D.Gries and J.Misra. A linear sieve algorithm for finding prime numbers. Communications of ACM. Vol.21(1978), pp999 ~ 1003.
H.G.Mairson. Some new upper bounds on the generation of prime numbers. Communications of ACM. Vol.20(1977), pp.664 ~ 669.
P.Pritchard. A sublinear additive sieve for finding prime numbers, Communications of ACM. Vol.24(1981), pp.18 ~ 23.
洗镜光,求质数,微电脑时代,1986

解答:
删除的过程如下:从2开始,先删除2×2,2×2×2,2×2×2×2,...,接着删除2×3,2×2×3,2×2×2×3,...(注意,3并没有被删除),再删除2×5,2×2×5,2×2×2×5,...。
一般而言,当发现p是一个质数时,先删除p×p,p×p×p,...这些合数。因为p是质数,
p×p,p×p×p,...这些数不可能被其他数整除,所以在此之前不可能被删除。接着,找出比p大但是没有被删除的一个数,令为q,再去删除p×q,p×p×q,p×p×p×q,...,因为p是质数,q是一个在此之前没有被删除的数,因此p×p×p...×q(i个p相乘,i=1,2,...)在此之前也没有被删除(证明:如果p的i次方×q在此之前已经被删除,那么依照上面所说的删除方法,一定有一个质数a(a<p)存在使得a的k次方×b正好就是p的i次方×q,这样在删除a的倍数时就会把a的k次方×b删掉。不过,a的k次方×b=p的i次方×q,a<p,这是不可能的。因为a和p都是质数,彼此互质,所以p的i次方就一定得整除b,于是q=a的k次方×(b/(p的i次方)),即q是a的k次方的倍数,在删除a的k次方的倍数时q早就被删除了才对,这与前面所说“q是比p大的第一个没有被删除的数相矛盾。因此,p的i次方(i=1,2,...)都是第一次被删除)。所以,删除动作绝不重复。
要找出2~N之间的所有质数,那么p最大不能超过N的平方根,也即p×p<=N。同样,任何一个
p的i次方×q也不能大过N。
核心代码部分如下:
for(p; p×p<N; p=Next(p))   /*先找到一个没有被删除的质数p,然后删除p的倍数*/
    for(q=p; q×p<N; q=Next(q))   /*再找大于等于p且没有被删除的数q(q从p开始)*/
       for(m=p×q; m<=n; m=p×m)   /*这样第一个删除的就是p×p*/
          REMOVE(m);                     /*
循环删除p的i次方×q*/
最外层循环p从2开始,每次删除完p的倍数后,就用Next(p)找出比p大但没有被删除的下一个数,它一定是质数(为什么?)。找到p之后,令q从p起,删除p×q,p×p×q,....,一直到p的i次方×q>n为止,之后再找出下一个没有被删除的数作为q。如此一直循环,直到找出的q满足p×q>N为止。
可以使用双链表来保存2~N这些元素以及它们之间的关系。双链表用两个数组实现,previous[i]和next[i]分别用于保存第i个元素的前驱元素和后继元素;一开始,previous[i]=i-1,next[i]=i+1;previou[2]没有前驱,next[N]没有后继,用NULL表示。当要删除第m个元素时,只要执行next[previous[m]]=next[m]; previous[next[m]]=previous[m]即可。每次取出p的下一个元素就是next[p]。
代码:
#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 1000

void main(void)
{
    unsigned long previous[MAXSIZE+1];   /*size of array is MAXSIZE, then previous*/
    unsigned long next[MAXSIZE+1];         /*and next[MAXSIZE] can be used for */
    unsigned long prime, fact, i, mult;         /*
number MAXSIZE*/
}