用q實現篩法求1-n的質數

lt發表於2016-07-03

篩法是一種求一個範圍內的質數的方法。思路是:先列出1-n之間的全體整數, 然後依次劃去2的倍數(不包括2)、3的倍數...一直到sqrt(n)取整的倍數。 自然地,我們有第一種使用列表的方法:

lsieve:{t:1+til x;  //初始化1-x列表
n:2;                //要劃去的數字
do["j"$sqrt(x);
    k:n-1;          //要劃去的數字的索引下標
    do["j"$(x%n)-1;
       if[x>k+n;t[k:k+n]:0] //所有的倍數都設定標誌0
    ];
    n:n+1
];
t[0]:0;             //1不是質數
1+(where t<>0)}     //返回所有標誌不為0的元素組成的列表

觀察這個方法,我們發現有一些多餘操作,比如:
1.所有偶數n的倍數在n=2時,都已經劃去了,因此n從3開始,只需要劃奇數n。
2.作為倍數,偶數倍也都在n=2時劃去了,因此n從3開始,只需要劃去它的奇數倍。
3.除2外,所有質數都是奇數,根本不用劃,因此只要構造1-x之間的奇數列表。劃完倍數後補充2即可。

q語言的程式通常在一行中,因此下面給出原始演算法0,改進1,改進1+2,改進1+2+3的語句:

lsieve0:{t:1+til x;n:2;do["j"$sqrt(x);k:n-1;do["j"$(x%n)-1;if[x>k+n;t[k:k+n]:0]];n:n+1;];t[0]:0;1+(where t<>0)}

lsieve1:{t:1+til x;n:2;do["j"$sqrt(x);k:n-1;do["j"$(x%n)-1;if[x>k+n;t[k:k+n]:0]];$[n=2;n:n+1;n:n+2]];t[0]:0;1+(where t<>0)}

lsieve2:{t:1+til x;n:2;do["j"$(sqrt(x)%2)+1;k:n-1;do["j"$(x%n)-1;$[n=2;if[x>k+n;t[k:k+n]:0];if[x>k+n+n;t[k:k+n+n]:0]]];$[n=2;n:n+1;n:n+2]];t[0]:0;1+(where t<>0)}

lsieve3:{t:1+2*til "j"$(x%2);n:3;do["j"$(sqrt(x)%2)+1;k:"j"$((n*n)-1)%2;do["j"$((x%n)%2)-1;if[(x%2)>k;t[k]:0];k:k+n];n:n+2];t[0]:0;2,(1+2*(where t<>0))}

q)\t lsieve0 500000
1761
q)\t lsieve1 500000
1106
q)\t lsieve2 500000
1075
q)\t lsieve3 500000
966

一個老外的天書寫法原文連線

soe:{[t] if[0<count t;$[(t[0]>sqrt last t);:t;[k:t[0]; t:1_t; :k,.z.s raze each[{$[0=x mod y;();:x]}[;k]] t]]]}

sieve:{soe 2+til(x-1)}

q)sieve 53
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53

解釋:
(待補充)

相關文章