C 需要帮助优化算法-所有素数之和在200万以下

C 需要帮助优化算法-所有素数之和在200万以下,c,C,我想问一个问题 我在寻找200万以下的所有素数之和 这就是我所拥有的 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 =>

我想问一个问题

我在寻找200万以下的所有素数之和

这就是我所拥有的

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;
}
intmain(intargc,char*argv[]){
无符号长整型和=0;
用于(无符号长整数i=0;i<2000000;i++){
如果(i){
总和+=i;
}
}
printf(“总和=>%lu\n”,总和);
}
intisprime(intnum){
if(num<2){
返回0;
}
对于(int i=2;i
它需要时间运行,因此它不满足Euler问题的一分钟运行时间规则

当我以10的限制运行它时,它会得到正确的答案,
17
就像问题中的一样

我猜有一些算法可以减少所做的工作。问题是,我不知道它是什么

有人能给我指一下正确的方向吗


谢谢。

您可以缩短查找素数的时间。有无数种方法可以做到这一点。 我认为你不需要在num之前进行测试,只需要测试sqrt(num),但这只会对你有一点帮助(在实际的素数上)

有一些统计方法可以检查质数是否真的是质数,它们的速度更快,而且在很多情况下只能出错一次

一位来自印度的研究人员找到了寻找素数的最快方法,但我找不到联系

你也可以看看这里:


如果
i
2
2000000
(或任何上限),一旦你确定
i
是素数,你就会知道
{2i,3i,4i,…,ni}
不是素数,其中
ni你可以加速你的
isPrime(int num)
传递到目前为止发现的素数数组,并检查
num
是否是这些素数的倍数。此外,您不需要循环到
num
,只需
sqrt(num)
使用Atkin的筛,它是Eratosthenes筛的优化版本

提示: 使用位数组和seive方法


如果您无法理解,请参阅。代码是Java的,但翻译成C并不困难。

如果您可以处理
2
案例
测试,然后从三开始循环,并将
i+=2
而不是
i++
将循环迭代次数减少一半

作为参考(如果您在这里,那么您已经在尝试查找答案),I(在PE开发团队中):

#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;
}
这里有一个比筛子更有效的解决方案 它是从类似的算法中派生出来的 优点是不需要找到所有要找到的素数 他们的总数

主要思想如下:设S(v,m)为 范围2..v,筛过所有小于或等于 也就是说,S(v,m)是到v的整数之和 素数或大于m的素数的乘积

如果p不是素数或v小于p,则S(v,p)等于S(v,p-1) *p.否则(p素数,p*ps[p-1]:#p是素数 sp=S[p-1]#小于p的素数之和 p2=p*p 对于v中的v: 如果v
该算法的复杂度约为$O(n^{0.75})$,需要9ms 找到解决办法


这些想法类似于,并且是的一个应用。

这的可能副本实际上不是特定于C语言的,它更多地与素数生成有关。@Billy ONeal谢谢,我将检查这个问题。从Billy ONeal的链接中,当您提前知道需要所有pri时,Eratosthenes的筛选似乎特别合适mes少于200万(与“前n个素数”相反)。这种方法称为试除法,通常可用于对相对较小的整数进行素数检查。很少有优化:(1)如果您没有内存限制,您可以存储所有发现的素数,然后只在素数中查找除数,而不是在所有整数中查找除数。(2)所有大于3的素数都可以写成(6*k+1)或(6*k-1)-因此可以显著减少主函数中的循环数。该解决方案仍然是指数型的。存在更好的解决方案,例如在我链接的副本中。你是对的,还有很多其他方法可以做到这一点(如alex建议的).我玩过一次…但从未改变过搜索的复杂性,只是让它变得更好了一点…你说的是AKS素性测试。AKS对于所有合理的数字都非常慢。对于非常大的数字,它在运行时与ECPP类似(但通常较慢).仅仅因为它是第一个被证明可以在多项式时间内运行的素性证明算法,并不意味着它是一个合理的选择!这种算法被称为。即使是埃拉托什尼的筛子也要花很长时间才能完成它的运行,它的运行时间为200万素数。这可能是真实和有趣的,但问题是要求素数在2M以下,不要求200万素数。
$kpwr
148933 numbers largest prime 1999993
sum: 142913828922

$
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]