200 万以下的素数之和。埃拉托斯特尼筛法
解决问题时遇到一点困难:“计算 200 万以下的素数之和”。我正在使用“埃拉托斯特尼筛法”。我的方法可以很好地查找 100 以内的素数,但是当我尝试查找 2,000,000 以内的素数之和时,我得到了错误的答案。
#include <iostream>
using namespace std;
long long unsigned int number[2000008];
int x=2000000LLU;
int sum()
{
int s=0LLU; //stores sum
for(int y=2; y<=x; y++) //add all the numers in the array from 2 to 2 million
{
s+=number[y];
}
return s;
}
int main()
{
int k=2;
for(int i=2; i<=x; i++) //fills in numbers from 2 to 2 million in the array
{
number[i]=i;
}
for(int j=2; j<=x; j+=1) //starts eliminating multiples of prime numbers from the grid
{
if(number[j]!=0) //moves through the grid till it finds a number that hasnt been crossed out. ie. isnt zero
{
for(int y=j+j; y<=x; y+=j) //when it finds a number, it removes all subsequent multiples of it
{
number[y]=0;
}
}
}
cout<<endl<<"done"; //shows that the loop has been completed
cout<<sum(); //outputs the sum of the grid
return 0;
}
Having a little trouble solving Problem: 'Calculate the sum of primes below two million'. I'm using the 'Sieve of Eratosthenes' method. My method works fine for finding primes till hundred but when I try to find the sum of primes till 2,000,000 I get an incorrect answer.
#include <iostream>
using namespace std;
long long unsigned int number[2000008];
int x=2000000LLU;
int sum()
{
int s=0LLU; //stores sum
for(int y=2; y<=x; y++) //add all the numers in the array from 2 to 2 million
{
s+=number[y];
}
return s;
}
int main()
{
int k=2;
for(int i=2; i<=x; i++) //fills in numbers from 2 to 2 million in the array
{
number[i]=i;
}
for(int j=2; j<=x; j+=1) //starts eliminating multiples of prime numbers from the grid
{
if(number[j]!=0) //moves through the grid till it finds a number that hasnt been crossed out. ie. isnt zero
{
for(int y=j+j; y<=x; y+=j) //when it finds a number, it removes all subsequent multiples of it
{
number[y]=0;
}
}
}
cout<<endl<<"done"; //shows that the loop has been completed
cout<<sum(); //outputs the sum of the grid
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不确定 int 是否足以保存答案...它可能大于 32 位值。
尝试在整个过程中使用
long long
。I'm not sure an int is enough to hold the answer... It could be larger than a 32-bit value.
Try using
long long
throughout.通过有效地使用埃拉托斯特尼筛,我解决了这个问题,这是我的代码,它是高度优化的
输出是
如果您有疑问,请告诉
by using Sieve of Eratosthenes effectively, i solved the problem, here is my code , it is highly optimized
and the output is
if you have doubt, please do tell
使用无符号 64 位整数计算
sum
@Omri Barel 的回答确定了OP代码中需要的最小修改:
这可行,但太过分了。仅必须使用 64 位变量计算总和。数组中的数字最好存储为 32 位数量。对于高达 200 万的值,不需要 64 位。使用 std::uint32_t 可以节省大量存储空间。
我的偏好是使用标头
中的类型别名。它们允许您说出您想要多少位。此外,我对所有下标都谨慎使用std::size_t
类型。如果您继续使用 OP 使用的“全局”变量,则会给出以下声明。
enum
用于创建编译时常量:请参阅下面的消除全局变量的方法。
使用
std::accumulate
添加数字如果您小心地将数组
number
的前两个元素清零,则可以使用std::accumulate< /code> 计算总和。
std::cbegin
和std::cend
具有接受内置数组作为参数的重载。std::uint64_t{}
强制std::accumulate
使用无符号 64 位算术进行计算。类型之间的转换
由于数组
number
的元素具有std::uint32_t
类型,而其下标具有std::size_t
类型,因此必须使用将下标分配给数组时进行强制转换。否则,您的编译器可能会发出警告,指出赋值涉及“缩小”值。重新访问 OP 中的程序
这是 OP 中的完整程序,并进行了上述修改。您应该能够复制、编译和执行,而无需摆弄。
以下部分展示了如何在不使用全局变量的情况下解决此问题。
交错筛法和求和
我最初的方法是直接方法:创建素数向量,然后对其元素求和。函数
make_vector_of_primes
(未显示)实现埃拉托斯特尼筛算法,来自维基百科。经过尝试后,我发现您可以交错筛和总和的计算。该代码并不像原始代码那么干净,但程序运行速度快了大约 15% 到 20%。它还使用更少的存储空间,因为素数向量永远不会被计算。
下面的程序包含两个OP中未使用的额外优化。
i
超过n
的平方根时,筛子的外循环停止。j
开始,设置为i * i
,而不是i + i
。main.cpp
输出
第二个示例对可以由
std::uint32_t
类型表示的所有素数求和。其中有超过 2.03 亿个,分散在 32 位可以表示的 40 亿多个数字中。在发布模式下,启用编译器优化后,只需 40 多秒即可找到所有素数并计算它们的总和。如果搜索空间减少到区区 200 万(如 OP 所示),那么即使是这台笨重的计算机也只需几毫秒即可完成这项工作。
Compute
sum
using unsigned, 64-bit integersThe answer by @Omri Barel identified the minimal modification needed in the OP's code:
This works, but it goes too far. Only the sum must be computed using 64-bit variables. The numbers in the array are better stored as 32-bit quantities. For values up to 2 million, you don't need 64-bits. Using
std::uint32_t
saves a ton of storage.My preference is to use the type aliases from header
<cstdint>
. They allow you to say how many bits you want. In addition, I am scrupulous in using typestd::size_t
for all subscripts.If you stay with the "global" variables used by the OP, that gives you the following declarations.
enum
is used to create compile-time constants:See below for a way to eliminate the globals.
Use
std::accumulate
to add the numbersIf you are careful to zero-out the first two elements of array
number
, you can usestd::accumulate
to calculate the sum.std::cbegin
andstd::cend
have overloads that accept built-in arrays as arguments.std::uint64_t{}
, forcesstd::accumulate
to do the computation using unsigned, 64-bit arithmetic.Casting between types
Because the elements of array
number
have typestd::uint32_t
, while its subscripts have typestd::size_t
, you must use a cast when a subscript is assigned into the array. Otherwise, your compiler may emit a warning that the assignment involves "narrowing" the value.Revisiting the program from the OP
Here is the complete program from the OP, with the modifications discussed above. You should be able to copy, compile, and execute, without fiddling.
The following section shows how to solve this without using global variables.
Interleaving sieve and sum
My original approach was the direct one: create a vector of primes, and then sum its elements. Function
make_vector_of_primes
(not shown) implements the Sieve of Eratosthenes algorithm, from Wikipedia.After playing around with this, I discovered that you can interleave the computation of sieve and sum. The code is not nearly as clean as the original, but the program runs about 15% to 20% faster. It also uses way less storage, because the vector of primes is never computed.
The program below contains two additional optimizations that were not used in the OP.
i
exceeds the square root ofn
.j
set toi * i
, rather thani + i
.main.cpp
Output
The second example sums all primes that can be represented by type
std::uint32_t
. There are more than 203 million of them, scattered among the 4 billion-plus numbers that can be represented using 32-bits. In release mode, with compiler optimizations enabled, it took just over 40 seconds to find all the primes, and calculate their sum.If the search space is reduced to a paltry 2 million, as in the OP, then even this clunky computer requires mere milliseconds to do the job.