使用欧拉筛法求素数和

上一次写到了厄拉多塞筛法,说到厄拉多塞筛法随着计算上限的成倍增加,计算时间也会成倍增加。

这次的欧拉筛法在时间这方面会比厄拉多塞筛法好很多。

先上代码:

#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <stdint.h>

#define MAX 1000000

int main()
{
	uint64_t* prime;
	prime = (uint64_t*)malloc(MAX*sizeof(uint64_t));
	memset(prime, 0, MAX);
	bool isprime[MAX];
	memset(isprime, true, sizeof(isprime));

	uint64_t index = 0;
	
	for (uint64_t i = 2; i < MAX; ++i)
	{
		if (isprime[i])
		{
			prime[index] = i;
			++index;
		}
		for (uint64_t j = 0; j < index && i * prime[j] < MAX; ++j)
		{
			isprime[i*prime[j]] = false;
			if (i % prime[j] == 0)
			{
				break;
			}
		}
	}
	uint64_t sum = 0;
	for (uint64_t i = 0; i < index; ++i){
		sum += prime[i];
	}
	printf("%llu\n", sum);
	free(prime);

	return 0;
}

欧拉筛法的思路和厄拉多塞筛法很像,素数的倍数标记成合数,但是标记的方法有区别。

欧拉筛法把数字i和已知的所有素数相乘,然后把结果标记为合数。

重要的是计算i%prime[j]是否为0,如果为0则break掉现在的循环。

这一步的意思是,每一个合数只被它最小的素数因数排除一次。

比如说12,12的第一次可能被排除的情况是i=4,此时经过for循环以后,因为4*prime[0]=8,所以首先把8给排除掉,然后因为4可以整除prime[0]也就是2,所以break掉for循环,也就是没有排除掉12.

实际上12会在6的时候被排除,此时6在排除过12以后,break掉for循环,不会排除18,因为18是需要9来排除的。

这样在厄拉多塞筛法中重复被排除的12,18之类的数字就不会被重复排除,算法的效率也就得到了提高。

但是我在想要把MAX设置为十亿,跟厄拉多塞筛法做一个比拼时,发现了一个问题,就是欧拉筛法需要一个数组来保存目前已知的所有素数。

但是即使我已经用了malloc,但是还是不能分配到这么大的内存,直接StackOverflow了。

所以先把算法的实现方法写在这里,等我Google到方法后再修改。

点赞