我需要做的是反向寻找第N个质数,也就是说,给定一个质数,我需要找到它在质数序列中的位置。
2, 3, 5, 7...
质数可以很大,达到10^7
的数量级。而且它们的数量很多。
我有一个预计算质数的索引,可以进行二进制搜索,但我还有50k的空间限制!能使用筛法吗?或者其他快速的方法?
编辑: 非常感谢所有精彩的回答,我没有期望这么好!我希望它们对寻找相同答案的其他人有用。
我需要做的是反向寻找第N个质数,也就是说,给定一个质数,我需要找到它在质数序列中的位置。
2, 3, 5, 7...
质数可以很大,达到10^7
的数量级。而且它们的数量很多。
我有一个预计算质数的索引,可以进行二进制搜索,但我还有50k的空间限制!能使用筛法吗?或者其他快速的方法?
编辑: 非常感谢所有精彩的回答,我没有期望这么好!我希望它们对寻找相同答案的其他人有用。
3) 使用对数积分来估算pi(n)。它是单调递增的,并且始终大于您需要的区间内的pi(n)。但是差异很小,从不超过200。因此,您可以预先计算所有小于一千万的值的差异,制作一个包含200个变化点的表格,然后在请求时计算对数积分并查找表中的校正因子。或者您可以使用黎曼R函数进行类似的操作。
第三种选择所需的空间最少,但我怀疑第一种选择所需的空间也不会太大,并且筛法可能比计算对数积分更快。因此,我将坚持我的最初建议。这里有对数积分和黎曼R函数的实现:我的博客。
编辑2:
那并没有很好地发挥作用,正如评论所指出的那样。请忽略我的第三个建议。
作为我因提出一个无法工作的解决方案而做出的赎罪,我编写了一个程序,使用pi(n)值表和分段埃拉托色尼筛法计算n < 10000000的pi(n)值。出于教学目的,我会使用Python而不是原帖中要求的C,因为Python更简单易读。def primes(n):
b, p, ps = [True] * (n+1), 2, []
for p in xrange(2, n+1):
if b[p]:
ps.append(p)
for i in xrange(p, n+1, p):
b[i] = False
return ps
primes
函数返回一个447个素数的列表:2、3、5、7、11、13、...、3121、3137、3163。我们从列表中去除2,并将446个筛选素数存储在全局ps变量中。ps = primes(3163)[1:]
sieve = [True] * 500
count
函数使用分段埃拉托色尼筛法来计算从lo到hi(lo和hi都包含在范围内)的质数数量。该函数有四个for
循环:第一个清除筛子,最后一个计算质数,另外两个以类似于上面展示的简单筛法的方式进行筛选:
def count(lo, hi):
for i in xrange(500):
sieve[i] = True
for p in ps:
if p*p > hi: break
q = (lo + p + 1) / -2 % p
if lo+q+q+1 < p*p: q += p
for j in xrange(q, 500, p):
sieve[j] = False
k = 0
for i in xrange((hi - lo) // 2):
if sieve[i]: k += 1
return k
for p in ps
,它执行筛选操作,依次取每个筛选素数p。当筛选素数的平方大于范围限制时,循环终止,因为此时所有素数都将被识别出来(我们需要比平方根大的下一个素数,以便有筛选素数停止循环)。神秘变量q是偏移量,指的是在范围lo到hi内p的最小倍数的筛子中的位置(请注意,它不是范围内最小的p的倍数,而是范围内p的最小倍数的偏移量的索引,这可能会令人困惑)。当引用完全平方数时,if
语句会增加q。然后,j的循环会从筛子中删除p的倍数。count
函数有两种方式。第一种用法是在1000的倍数处建立pi(n)值的表;第二种用法是在表中进行插值。我们将表存储在全局变量piTable中:piTable = [0] * 10000
for i in xrange(1, 10000):
piTable[i] = piTable[i-1] + \
count(1000 * (i-1), 1000 * i)
def pi(n):
if type(n) != int and type(n) != long:
raise TypeError('must be integer')
if n < 2: return 0
if n == 2: return 1
i = n // 1000
return piTable[i] + count(1000 * i, n+1)
if
语句进行类型检查。第二个if
语句针对荒谬的输入返回正确响应。第三个if
语句特殊处理2;我们的筛法使1成为质数,2成为合数,两者都是不正确的,所以我们在这里进行修正。然后,i被计算为piTable中小于请求n的最大索引,而返回语句将piTable的值加到表值和请求值之间的质数计数中;上限hi为n+1,因为如果n是质数,则不会被计算。例如,说:print pi(6543223)
这将导致数字447519在终端上显示。
pi
函数非常快速。在ideone.com,对pi(n)的一千个随机调用在大约半秒钟内计算完成,因此每个调用大约需要半毫秒; 这包括生成质数和求和结果的时间,因此实际计算pi函数的时间甚至少于半毫秒。这是我们在构建表格方面投资的相当不错的回报。
如果您对使用质数编程感兴趣,我在我的博客上做了很多工作。请来访问。
a==2
),并预先计算错误的结果。基于输入的最大大小和存储限制的微调应该很容易解决。这里有一些可用的代码。你应该使用一个适用于你输入范围的确定性Miller-Rabin测试来替换基于试除法的素数测试。在适当的小范围内筛选素数比试除法更好,但这是朝着错误的方向迈出的一步。
#include <stdio.h>
#include <bitset>
using namespace std;
short smallprimes[549]; // about 1100 bytes
char in[19531]; // almost 20k
// Replace me with Miller-Rabin using 2, 7, and 61.
int isprime(int j) {
if (j<3) return j==2;
for (int i = 0; i < 549; i++) {
int p = smallprimes[i];
if (p*p > j) break;
if (!(j%p)) return 0;
}
return 1;
}
void init() {
bitset<4000> siv;
for (int i = 2; i < 64; i++) if (!siv[i])
for (int j = i+i; j < 4000; j+=i) siv[j] = 1;
int k = 0;
for (int i = 3; i < 4000; i+=2) if (!siv[i]) {
smallprimes[k++] = i;
}
for (int a0 = 0; a0 < 10000000; a0 += 512) {
in[a0/512] = !a0;
for (int j = a0+1; j < a0+512; j+=2)
in[a0/512] += isprime(j);
}
}
int whichprime(int k) {
if (k==2) return 1;
int a = k/512;
int ans = 1 + !a;
for (int i = 0; i < a; i++) ans += in[i];
for (int i = a*512+1; i<k; i+=2) ans += isprime(i);
return ans;
}
int main() {
int k;
init();
while (1 == scanf("%i", &k)) printf("%i\n", whichprime(k));
}
以下是您正在寻找的内容。http://www.geekviewpoint.com/java/numbers/index_of_prime。在那里,您将找到代码和单元测试。由于您的列表相对较小(即10^7
),因此它应该可以处理。
基本上,您需要找到2
和n
之间的所有质数,然后计算小于n
的所有质数以查找索引。此外,如果n
不是质数,则函数返回-1
。
我曾经做过这件事。编写了一段代码,可以快速找到第n个质数,直到n = 203542528,大约2e8。或者,它可以向后查找,对于任何数字n,可以告诉有多少个质数小于n。
使用数据库。我存储了所有的质数,直到某个点(我的上限的平方根)。在你的情况下,这意味着你将存储所有的质数,直到sqrt(1e7)。有446个质数,你可以以压缩形式存储该列表,因为到那个点为止的最大差异仅为34。超过那个点,存储每k个质数(对于某个k值)。然后,一个快速筛法就足以在短时间内生成所有质数。
所以在MATLAB中,要找到第1e7个质数:
nthprime(1e7)
ans =
179424673
或者,它可以找到小于1e7的质数数量:
nthprime(1e7,1)
ans =
664579
关键是,这样的数据库易于构建和搜索。如果你的数据库不超过50k,那么就没有问题了。