我有以下代码用于计算形式不超过N的x ^ 2 + ny ^ 2质数。当N大约为80000时此代码运行良好,但是当N大约为10 ^ 5时该代码崩溃了。为什么会发生这种情况以及如何解决呢?
#include <iostream>
#include<iostream>
#include<vector>
const int N = 100000; //Change N in this line
using namespace std;
typedef long long int ll;
bool isprime[N] = {};
bool zprime[N] = {};
vector<int> primes;
vector<int> zprimes;
void calcprime(){
for (int i = 2; i < N; i+=1){isprime[i] = true;}
for (int i = 2; i < N; i+=1){
if (isprime[i]){
primes.push_back(i);
for (int j = 2; i*j < N; j+=1){
isprime[i*j] = false;
}
}
}
}
void zcalc(){
int sqrt = 0; for (int i = 0; i < N; i+=1){if(i*i >= N){break;} sqrt = i;}
for (int i = 0; i <= sqrt; i +=1){
for (int j = 0; j <= sqrt; j+=1){
ll q = (i*i)+(j*j);
if (isprime[q] && !zprime[q] && (q < N)){
zprimes.push_back(q);
zprime[q] = true;
}
}
}
}
int main(){
calcprime();
zcalc();
cout<<zprimes.size();
return 0;
}
答案 0 :(得分:4)
超出范围的访问权限。该代码中断,因为您在此处对以下行进行了超出范围的内存访问:
if (isprime[q] && !zprime[q] && (q < N)) {
如果q
大于N
,则说明您正在访问的内存在技术上不属于您。这会调用未定义的行为,如果N
足够大,则会导致代码中断。
如果我们更改顺序以使其在进行其他检查之前先检查q < N
,则不会出现此问题:
// Does check first
if((q < N) && isprime[q] && !zprime[q]) {
不建议将非常大的c数组用作全局变量。它可能会引起问题并增加可执行文件的大小。
(可能)非常大的全局数组。您将isprime
和zprime
定义为c数组:
bool isprime[N] = {};
bool zprime[N] = {};
这可能会为N
的很大的值带来问题,因为c-arrays
是静态分配内存的。
如果将isprime
和zprime
更改为向量,则即使N
的值大于一千万,程序也会编译并运行。这是因为使用vector
使分配动态化,而堆是存储大量数据的更好位置。
std::vector<bool> isprime(N);
std::vector<bool> zprime(N);
这是完全更新的代码!我也将i
和j
设置为long long
的值,所以您不必担心整数溢出,并且我使用了标准库sqrt
函数来计算sqrt
ofN。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
typedef long long int ll;
constexpr long long N = 10000000; //Change N in this line
std::vector<bool> isprime(N);
std::vector<bool> zprime(N);
vector<int> primes;
vector<int> zprimes;
void calcprime() {
isprime[0] = false;
isprime[1] = false;
for (ll i = 2; i < N; i+=1) {
isprime[i] = true;
}
for (ll i = 2; i < N; i+=1) {
if (isprime[i]) {
primes.push_back(i);
for (ll j = 2; i*j < N; j+=1){
isprime[i*j] = false;
}
}
}
}
void zcalc(){
ll sqrtN = sqrt(N);
for (ll i = 0; i <= sqrtN; i++) {
for (ll j = 0; j <= sqrtN; j++) {
ll q = (i*i)+(j*j);
if ((q < N) && isprime[q] && !zprime[q]) {
zprimes.push_back(q);
zprime[q] = true;
}
}
}
}
int main(){
calcprime();
zcalc();
cout << zprimes.size();
return 0;
}
答案 1 :(得分:2)
在您的代码中q
的值可能超过N
的值,并且在访问zprime[q]
,isprime[q]
时可能导致分段错误。您要迭代i
,j
到sqrt(N)
,并为zprime
,isprime
分配了N
个布尔值。 q的值可以从0
到2N
。
ll q = (i*i)+(j*j);
您可以用
替换bool zprime[N] = {};
和bool isprime[N] = {};
bool zprime[N * 2 + 1] = {};
和
bool isprime[N * 2 + 1] = {};
分别。
该程序将不再存在段错误。或者,您可以在访问q < N
和isprime[q]
之前检查zprime[q]
。
此外,正如评论中已经指出的那样,(i*i)+(j*j)
是int
。将该值分配给long long
是没有用的。如果要防止溢出,请用((ll)i*i)+(j*j)
替换它。
此外,对于大型数组,您应该更喜欢在堆上分配它。