我正在尝试Project Euler问题。
我正在寻找2,000,000以下所有素数的总和。
这就是我的......
int main(int argc, char *argv[]) {
unsigned long int sum = 0;
for (unsigned long int i = 0; i < 2000000; i++) {
if (isPrime(i)) {
sum += i;
}
}
printf("sum => %lu\n", sum);
}
int isPrime(int num) {
if (num < 2) {
return 0;
}
for (int i = 2; i < num; ++i) {
if (num % i == 0) {
return 0;
}
}
return 1;
}
运行需要很长时间,因此它不满足Euler问题的运行时间的一分钟规则。
当我以10的限制运行时,它会得到正确的答案,17
就像在问题中一样。
我的猜测是有一些算法可以减少正在进行的工作。问题是,我不知道它是什么。
有人可以指出我正确的方向吗?
感谢。
答案 0 :(得分:8)
i
从2
到2000000
(或任何上限),一旦确定i
为素数,您就会知道{2i, 3i, 4i, ..., ni}
是不是素数,ni <= 2000000
。
您不需要测试这些数字 - 您知道它们不是素数。
当您浏览testNumbers
数组并从此列表中删除数字时,您现在知道的数字不是素数,您可以显着减少实际需要测试的数字。
是的,这是Eratosthenes的筛子。
答案 1 :(得分:2)
你可以缩短寻找素数的时间。有百万种方法可以做到这一点。 我认为你不需要测试直到num,但只需要测试sqrt(num),但这只会对你有所帮助(在实际素数上)
有一些统计方法可以检查素数是否实际上是素数更快,而且只能在2 ^中错误一次....
找到素数的最快方法是来自印度的研究人员,但我找不到链接。
你也可以看一下:
答案 2 :(得分:2)
您可以通过传递到目前为止发现的素数数组来加速isPrime(int num)
函数,并检查num
是否是这些素数的倍数。此外,您无需循环到num
,只需sqrt(num)
。
答案 3 :(得分:0)
使用Atkin筛,它是Eratosthenes link筛的优化版本。
答案 4 :(得分:0)
<强>提示:强> 使用BitArray和seive方法。
如果你无法弄清楚,请参阅code。代码是Java的,但不难转换为C.
答案 5 :(得分:0)
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
struct prime_list_t {
long n;
struct prime_list_t *next;
};
void add_prime_list_t(struct prime_list_t** list, long n);
void free_prime_list_t(struct prime_list_t** list);
long count_prime_list_t(struct prime_list_t* list);
long last_prime_list_t(struct prime_list_t* list);
/* if one of the primes in list divides n, is not a prime */
int is_prime(struct prime_list_t* pl, long n) {
struct prime_list_t* p;
if(pl == NULL) {
abort();
}
p = pl;
while(p != NULL) {
if(n % p->n == 0) {
return 1;
}
p = p->next;
}
return 0;
}
int main() {
struct prime_list_t* pl = NULL;
struct prime_list_t* p;
long n_up = 2000000;
long long p_sum = 0;
long ppr;
int done;
/* add first prime number */
add_prime_list_t(&pl, 2);
/* from now on add to this list up to the number of items */
done = 0;
do {
/* get the last prime plus one */
ppr = last_prime_list_t(pl) + 1;
while(is_prime(pl, ppr) != 0) {
ppr++;
if(ppr >= n_up) {
/* hit the upper limit */
done = 1;
break;
}
}
if(done) {
break;
}
/* ppr is prime */
add_prime_list_t(&pl, ppr);
/* display status */
printf("%ld numbers largest prime %ld\r", count_prime_list_t(pl), last_prime_list_t(pl));
} while(!done);
p = pl;
p_sum = 0;
while(p) {
// printf("%d ", p->n);
p_sum += p->n;
p = p->next;
}
printf("\nsum: %I64d\n", p_sum);
free_prime_list_t(&pl);
return 0;
}
void add_prime_list_t(struct prime_list_t** list, long n) {
struct prime_list_t* node;
if(list == NULL) {
abort();
}
node = (struct prime_list_t*)malloc(sizeof(struct prime_list_t));
if(node == NULL) {
abort();
}
node->n = n;
node->next = NULL;
if(*list == NULL) {
*list = node;
}
else {
struct prime_list_t* p;
p = *list;
while(p->next != NULL) {
p = p->next;
}
p->next = node;
}
}
void free_prime_list_t(struct prime_list_t** list) {
struct prime_list_t* node;
if(list == NULL) {
return;
}
node = *list;
while(node != NULL) {
struct prime_list_t* p;
p = node;
node = node->next;
free(p);
}
*list = NULL;
}
long count_prime_list_t(struct prime_list_t* list) {
long c;
struct prime_list_t* p;
for(p = list, c = 0; p != NULL; p = p->next, c++)
;
return c;
}
long last_prime_list_t(struct prime_list_t* list) {
long n;
struct prime_list_t* p;
n = 0;
p = list;
while(p->next != NULL) {
p = p->next;
}
return p->n;
}
这将是输出:
$kpwr
148933 numbers largest prime 1999993
sum: 142913828922
$
答案 6 :(得分:0)
供参考(如果你在这里,那么你已经在尝试查找答案),我quote Lucy_Hedgehog(在PE开发团队):
这是一种比筛子更有效的解决方案 埃拉托色尼。它源自counting primes的类似算法。该 优点是没有必要找到所有要找到的素数 他们的总和。
主要思想如下:设S(v,m)为整数的和 范围2..v在筛选后保留所有质数小或相等 比米。也就是说,S(v,m)是直到v的整数之和 素数或素数大于m的乘积。
如果p不是素数或v小于p,则S(v,p)等于S(v,p-1) * p。否则(p prime,p * p&lt; = v)S(v,p)可以通过找到在用p筛选时去除的整数之和从S(v,p-1)计算。 如果它是p的乘积,则在此步骤中删除整数 另一个没有小于p的除数的整数。这可以 表示为
$ S(v,p)= S(v,p-1) - p(S(v / p,p-1) - S(p-1,p-1))。$
动态编程可用于实现此目的。这足够了 计算可表示为的所有正整数v的S(v,p) 对于某些整数k和所有$ p \ leq \ sqrt v $。
的floor(n / k)def P10(n): r = int(n**0.5) assert r*r <= n and (r+1)**2 > n V = [n//i for i in range(1,r+1)] V += list(range(V[-1]-1,0,-1)) S = {i:i*(i+1)//2-1 for i in V} for p in range(2,r+1): if S[p] > S[p-1]: # p is prime sp = S[p-1] # sum of primes smaller than p p2 = p*p for v in V: if v < p2: break S[v] -= p*(S[v//p] - sp) return S[n]
该算法的复杂性约为$ O(n ^ {0.75})$,需要9 ms 找到解决方案。
这些提示与finding totient sum类似,是Dirichlet hyperbola method的应用。
答案 7 :(得分:-1)
如果你可以处理case
的{{1}}测试,那么从三开始循环并将2
而不是i+=2
减少循环迭代一半