从 3 到 n 的 C++ 素数
Posted
技术标签:
【中文标题】从 3 到 n 的 C++ 素数【英文标题】:C++ Prime Numbers from 3 to n 【发布时间】:2017-10-18 07:09:58 【问题描述】:我编写了这段代码来打印用户输入的所有介于 3 和 'n' 之间的素数,但在运行时,它什么也没产生。
你能帮忙吗?
#include <iostream>
using namespace std;
int main()
int n;
cin >> n;
for (int candidate = 3; candidate < n; ++candidate)
bool isPrime = true;
for (int x = 2; x < n; x++)
if (candidate % x == 0)
isPrime = false;
if (isPrime)
cout << n << "";
【问题讨论】:
你试过调试了吗? 你不应该打印出candidate
而不是n
吗?
for 循环应该是 for x
如前所述为什么不打印出candidate
,还有""
在打印出n
之后的目的是什么,不应该是换行还是其他空格?
顺便说一句:你不需要检查偶数。
【参考方案1】:
你应该知道的一件事。如果我们开始通过除法运算检查 n 是否为素数,那么检查的次数不应超过 sqrt(n)
。
for (int candidate = 3; candidate < n; ++candidate)
bool isPrime = true;
for (int x = 2; x*x < candidate; x++)
if (candidate % x == 0)
isPrime = false;
break;
if (isPrime)
cout << candidate << "";
更好的方法是为此使用Sieve of Eratosthenes:
isPrime: initialize with all true
for(int i=2;i*i<=n;i++)
for(int j =i*i;j<=n;j+=i)
isPrime[j]=false; // the if was not needed.
现在您知道了 3 和 n
之间的质数。
【讨论】:
你不需要在筛算法中使用if
:无论如何设置isPrime[j]=false
。这大概比分支快。【参考方案2】:
在您的代码中可以节省多种效率。最有效的方法(除了对小型 (n 使用预先计算的查找表)是 sieve of Erastosthenes。
这个算法的一个非常幼稚的版本(另见answer by coderredoc)
std::vector<int> primes(int n)
std::vector<bool> is_prime(n+1,true);
for(auto divisor=2; divisor*divisor <= n; ++divisor)
for(auto candidate=divisor*divisor; candidate <= n; candidate+=divisor)
is_prime[candidate]=false;
std::vector<int> result;
for(auto candidate=2; candidate <= n; ++candidate)
if(is_prime[candidate]) result.push_back(candidate);
return result;
与您的原始算法相比,基本上反转了candidate
s 和divisor
s 上的循环,但仅测试满足divisor*divisor<=candidate
的divisor
s。
通过意识到我们只需要测试素数除数(这是筛子的主要技巧),可以大幅改进该算法
std::vector<int> primes(int n)
std::vector<bool> is_prime(n+1,true);
for(auto divisor=2; divisor*divisor <= n; ++divisor)
if(is_prime[divisor])
for(auto candidate=divisor*divisor; candidate <= n; candidate+=divisor)
is_prime[candidate]=false;
std::vector<int> result;
for(auto candidate=2; candidate <= n; ++candidate)
if(is_prime[candidate]) result.push_back(candidate);
return result;
这减少了对大型 n
的测试。甚至避免candidate
s 和divisor
s 也可以进一步节省效率(空间和时间减少约2 倍):
std::vector<int> primes(int n)
if(n<2) return ;
if(n==2) return 2;
std::vector<bool> is_prime((n+1)>>1,true);
for(auto divisor=3; divisor*divisor <= n; divisor+=2)
if(is_prime[divisor>>1])
for(auto candidate=divisor*divisor; candidate <= n; candidate+=2*divisor)
is_prime[candidate>>1]=false;
std::vector<int> result(1,2);
for(auto candidate=3; candidate <= n; candidate+=2)
if(is_prime[candidate>>1]) result.push_back(candidate);
return result;
该算法的内存访问模式很差(进入is_prime[]
),这对于非常大的n
来说是个问题。一种更复杂的方法,分段筛,可以避免这种情况,请参见上面的链接。
【讨论】:
仍然 +1 以获得简洁的答案 =) @Sergey.quixoticaxis.Ivanov 实际上,我对此进行了实验并将其替换为std::vector<char>
。令我惊讶的是,代码变慢了(Apple clang 编译器,-O3)。
这很奇怪,我不知道 char
,但在 Intel i5 6600 和 Intel i7 4790k 上,将 ::std::vector<bool>
更改为 bool*
(我不会“push_back”任何东西并且预先只为is_prime
分配缓冲区一次)对 msvc 和 Intel C++ 17 都有很大的影响。
*效果(对不起^-^)
我也尝试过(虽然使用std::unique_ptr<bool[]> is_primenew bool[n+1];
),然后使用true
进行初始化。我想,后者效率低下(最好一次初始化 8 个字节,这大概是 std::vector<bool>
所做的)。但是初始化不应该主导成本。无论如何,这也比 std::vector<bool>
慢,也许 std::vector<bool>
的 msvc 或 Intel (icpc) 实现较差?【参考方案3】:
改变你的内部循环
for (int x = 2; x < n; x++)
if (candidate % x == 0)
isPrime = false;
到
for (int x = 2; x < candidate; x++)
if (candidate % x == 0)
isPrime = false;
break;
否则x
最终将成为candidate
本身,而candidate%candidate
是0
,这将导致isPrime
成为false
。
使用break
语句是因为在确定数字不是素数之后,就不需要进一步的迭代了。
由于您只考虑来自 3
的数字,您可以更改外部循环以节省一些迭代,例如
for (int candidate = 3; candidate < n; candidate+=2)
这将使candidate
每次增加2
。这没关系,因为没有大于 2 的偶数不是素数。
另外,如果您考虑的数字范围包括n
,您可以将外部for循环修改为
for (int candidate = 3; candidate < n; candidate+=2)
也可以考虑n
。
【讨论】:
这是错误的。它只会打印一个素数。他想把它们都打印出来。 @MartinBonner 哎呀!我没有意识到。已编辑。感谢您指出。 我只会删除旧的错误答案,而不是保留它。 删除旧部分【参考方案4】:不建议使用 using namespace std; — 因为这会从 std.xml 导入所有标识符。请在 Stack Overflow 上查看此问题。
第二个循环的结束条件应该是candidate
而不是n
,即
for (int x = 2; x < candidate; x++)//not before N prime numbers divine up to them
if (candidate % x == 0)
isPrime = false;
你不应该发出candidate
而不是n
【讨论】:
【参考方案5】:我认为这应该可行
#include <iostream>
#include <vector>
int main()
//setup start parameters
//test sequence
std::vector<int> test_sequence 2;
int end_number, start_number;
start_number = 3;
//take wished end number
std::cin >> end_number;
// test is wished number smaler than start number
if (start_number < end_number)
// loop which goes trough every number between start and end number
for (start_number; start_number <= end_number; start_number++)
bool isPrime = true;
//test is this number prime
for (int n = 0; n < test_sequence.size(); n++)
if (start_number % test_sequence[n] == 0 && start_number != test_sequence[n] )
isPrime = false;
if (isPrime)
std::cout << start_number << std::endl;
//hold result for next test
test_sequence.push_back(start_number);
else
std::cout << "Wrong input";
前 200 名的结果
【讨论】:
执行时间 10.115 s 这太可怕了。对于高达 200 的素数,一个好的算法需要大约 4μs(任何现代 CPU 的时间),比它短 1000 万倍。 1000 万个第一个素数可以用简单的算法在 3-4 秒内计算出来,写成一个简短的练习 不是计算结果的时间,我只是程序启动时没有立即输入结束号。以上是关于从 3 到 n 的 C++ 素数的主要内容,如果未能解决你的问题,请参考以下文章