大数组大小 C++ 的问题
Issue with big array size c++
我有一个硕士课程的任务,涉及通过 Monte Carlo 方法对概率分布进行抽样。我之前想 运行 一些测试,所以我用 2 自由度对卡方分布进行采样(可以用逆方法完成)。无论如何,我 运行 遇到数组大小的问题,任何超过 ~30000 个元素的东西都会导致概率分布的所有点累积为 1,甚至导致程序崩溃。
在post的最后可以看到10000分的结果截图和我写的代码。我使用过许多版本的 g++ 编译器,最高为 4.9.2,其中 none 有效。 OS 是 Windows 7. Aaa 并且相同的代码在朋友的 gentoo 计算机上完美运行。有什么建议吗?
提前致谢!
曼努埃尔·J.
这是我使用的代码:
I've removed it to keep the post shorter. Please see edited code
编辑:我对代码做了几处更改,主要是从普通数组到向量的转换,但问题仍然存在:超过 ~10000 个元素的任何内容都不起作用。问题肯定是 histograma
函数:当你给它一个太大的向量时 histo.dat
文件的内容是:
inf 100000
inf 0
inf 0
inf 0
...
inf 0
现在的代码如下。问题肯定出在max和min函数上,但我完全不知道问题出在哪里。
#include <iostream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <vector>
#define PI 3.14159265359
#define NHISTOMAX 100
#define N 100000
#define GNUPLOT_PATH "D:\gnuplot\bin\gnuplot.exe -persist"
using namespace std;
double max (vector<double> v);
double min (vector<double> v);
void histoplot (string name1);
void histograma (string name, vector<double> v, size_t num2);
vector<double> v(N);
int main (void)
{
srand(time(NULL));
for(size_t i=0;i<N;i++)
v[i]=-2*log(1.0*rand()/RAND_MAX);
histograma("hist.dat",v,NHISTOMAX);
histoplot("hist.dat");
system("pause");
return 0;
}
void histograma (string name, vector<double> v, size_t num2)
{
ofstream fileout;
double max1, min1, delta;
size_t i, j, num=v.size();
vector<int> histo(NHISTOMAX);
if(num2>NHISTOMAX) cout << "Too many intervals. " << endl;
else
{
for(i=0;i<num2;i++)
histo[i]=0;
max1=max(v);
min1=min(v);
delta=(max1-min1)/num2;
for(i=0;i<num;i++)
{
j=(size_t)((v[i]-min1)/delta);
if(j==NHISTOMAX) j--;
histo[j]++;
}
fileout.open(name.c_str());
for(i=0;i<num2;i++)
fileout << min1+(i+0.5)*delta << "\t" << histo[i] << endl;
fileout.close();
cout << "Histogram generated! Output file: " << name << endl << endl;
}
return;
}
void histoplot (string name1)
{
FILE *gp1;
gp1=popen(GNUPLOT_PATH,"w");
if(gp1==NULL)
cout << "Unable to open pipe. Check gnuplot.exe's path" << endl;
else
{
fprintf(gp1,"unset key\n");
fprintf(gp1,"set term wxt\n");
fprintf(gp1,"set output\n");
fprintf(gp1,"plot '");
fprintf(gp1,name1.c_str());
fprintf(gp1,"' w histeps\n");
fflush(gp1);
pclose(gp1);
}
return;
}
double max (vector<double> v)
{
double aux=v[0];
size_t i, num=v.size();
for(i=1;i<num;i++)
if(aux<v[i])
aux=v[i];
return aux;
}
double min (vector<double> v)
{
double aux=v[0];
size_t i, num=v.size();
for(i=1;i<num;i++)
if(aux>v[i])
aux=v[i];
return aux;
}
编辑 2:v 向量中值的典型图。他们都是积极的,而且都在 25 岁以下。
使用std::vector:
#include <vector>
#define N 10000
int main (void)
{
std::vector<double> v(N);
//...
histograma("hist.dat",v.data(), N, NHISTOMAX);
// or
// histograma("hist.dat", &v[0], N, NHISTOMAX);
请注意,不需要更改 histograma
函数本身。调用中的唯一区别是 v.data()
(或 &v[0]
)用于 return 指向存储 double
数组的内部缓冲区开始的指针。
此外,不要编写 min
和 max
函数,而是使用 <algorithm>
header 中的 std::min_element
和 std::max_element
:
max1 = *std::max_element(v.begin(), v.end());
min1 = *std::min_element(v.begin(), v.end());
或者如果使用 C++ 11,std::minmax_element 获得两者:
auto pr = std::minmax_element(v.begin(), v.end());
min1 = *(pr.first);
max1 = *(pr.second);
此外,如果按照其他答案的建议,并且您有内存覆盖,我建议您从使用 operator [ ]
更改为使用 vector::at()
来访问您的元素。使用 at()
将在您进行 out-of-bounds 访问时立即抛出 out_of_range
异常。然后在转换回对向量使用 [ ]
之前修复这些错误。
例如,这段代码:
j=(size_t)((v[i]-min1)/delta);
if(j==NHISTOMAX) j--;
histo[j]++;
如果 j
是一个巨大的数字,远远超过 histo
向量的范围怎么办?将 j
减 1 无济于事。要查看这是否是一个问题,可以在此处使用 at()
:
j=(size_t)((v[i]-min1)/delta);
if(j==NHISTOMAX) j--;
histo.at(j)++;
如果您 运行 程序,如果 j
超出范围,一旦您尝试递增 histo[j]
,就会抛出异常。然后您可以检查问题并修复错误。
看起来您在以下位置可能存在数组溢出:
delta = (max1-min1)/num2;
...
j = (int)((v[i]-min1)/delta);
histo[j]++;
当 v[i] == max1
这使得 j == num2 == NHISTOMAX
但 histo[NHISTOMAX]
超出范围(最后一个有效元素是 NHISTOMAX-1
,因为数组索引从 0 开始)。
由于您将问题标记为 C++,因此我会使用 std::vector<double>
而不是原始数组:
#include <vector>
...
std::vector<double> v(N);
...
void histograma (string name, const std::vector<double> v, const int maxHistoSize)
{
std::vector<double> histo(maxHistoSize + 1);
...
delta = (max1 - min1) / maxHistoSize;
...
// There's likely a more C++ idiomatic way to get the min/max of a
// std::vector<> but this works fine with a few changes.
double max (const std::vector<double> v)
{
double minValue = v[0];
for(int i = 1; i< v.size(); i++)
{
if (minValue < v[i]) minValue = v[i];
}
return aux;
}
// Same for min()
我有一个硕士课程的任务,涉及通过 Monte Carlo 方法对概率分布进行抽样。我之前想 运行 一些测试,所以我用 2 自由度对卡方分布进行采样(可以用逆方法完成)。无论如何,我 运行 遇到数组大小的问题,任何超过 ~30000 个元素的东西都会导致概率分布的所有点累积为 1,甚至导致程序崩溃。
在post的最后可以看到10000分的结果截图和我写的代码。我使用过许多版本的 g++ 编译器,最高为 4.9.2,其中 none 有效。 OS 是 Windows 7. Aaa 并且相同的代码在朋友的 gentoo 计算机上完美运行。有什么建议吗?
提前致谢!
曼努埃尔·J.
这是我使用的代码:
I've removed it to keep the post shorter. Please see edited code
编辑:我对代码做了几处更改,主要是从普通数组到向量的转换,但问题仍然存在:超过 ~10000 个元素的任何内容都不起作用。问题肯定是 histograma
函数:当你给它一个太大的向量时 histo.dat
文件的内容是:
inf 100000
inf 0
inf 0
inf 0
...
inf 0
现在的代码如下。问题肯定出在max和min函数上,但我完全不知道问题出在哪里。
#include <iostream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <vector>
#define PI 3.14159265359
#define NHISTOMAX 100
#define N 100000
#define GNUPLOT_PATH "D:\gnuplot\bin\gnuplot.exe -persist"
using namespace std;
double max (vector<double> v);
double min (vector<double> v);
void histoplot (string name1);
void histograma (string name, vector<double> v, size_t num2);
vector<double> v(N);
int main (void)
{
srand(time(NULL));
for(size_t i=0;i<N;i++)
v[i]=-2*log(1.0*rand()/RAND_MAX);
histograma("hist.dat",v,NHISTOMAX);
histoplot("hist.dat");
system("pause");
return 0;
}
void histograma (string name, vector<double> v, size_t num2)
{
ofstream fileout;
double max1, min1, delta;
size_t i, j, num=v.size();
vector<int> histo(NHISTOMAX);
if(num2>NHISTOMAX) cout << "Too many intervals. " << endl;
else
{
for(i=0;i<num2;i++)
histo[i]=0;
max1=max(v);
min1=min(v);
delta=(max1-min1)/num2;
for(i=0;i<num;i++)
{
j=(size_t)((v[i]-min1)/delta);
if(j==NHISTOMAX) j--;
histo[j]++;
}
fileout.open(name.c_str());
for(i=0;i<num2;i++)
fileout << min1+(i+0.5)*delta << "\t" << histo[i] << endl;
fileout.close();
cout << "Histogram generated! Output file: " << name << endl << endl;
}
return;
}
void histoplot (string name1)
{
FILE *gp1;
gp1=popen(GNUPLOT_PATH,"w");
if(gp1==NULL)
cout << "Unable to open pipe. Check gnuplot.exe's path" << endl;
else
{
fprintf(gp1,"unset key\n");
fprintf(gp1,"set term wxt\n");
fprintf(gp1,"set output\n");
fprintf(gp1,"plot '");
fprintf(gp1,name1.c_str());
fprintf(gp1,"' w histeps\n");
fflush(gp1);
pclose(gp1);
}
return;
}
double max (vector<double> v)
{
double aux=v[0];
size_t i, num=v.size();
for(i=1;i<num;i++)
if(aux<v[i])
aux=v[i];
return aux;
}
double min (vector<double> v)
{
double aux=v[0];
size_t i, num=v.size();
for(i=1;i<num;i++)
if(aux>v[i])
aux=v[i];
return aux;
}
编辑 2:v 向量中值的典型图。他们都是积极的,而且都在 25 岁以下。
使用std::vector:
#include <vector>
#define N 10000
int main (void)
{
std::vector<double> v(N);
//...
histograma("hist.dat",v.data(), N, NHISTOMAX);
// or
// histograma("hist.dat", &v[0], N, NHISTOMAX);
请注意,不需要更改 histograma
函数本身。调用中的唯一区别是 v.data()
(或 &v[0]
)用于 return 指向存储 double
数组的内部缓冲区开始的指针。
此外,不要编写 min
和 max
函数,而是使用 <algorithm>
header 中的 std::min_element
和 std::max_element
:
max1 = *std::max_element(v.begin(), v.end());
min1 = *std::min_element(v.begin(), v.end());
或者如果使用 C++ 11,std::minmax_element 获得两者:
auto pr = std::minmax_element(v.begin(), v.end());
min1 = *(pr.first);
max1 = *(pr.second);
此外,如果按照其他答案的建议,并且您有内存覆盖,我建议您从使用 operator [ ]
更改为使用 vector::at()
来访问您的元素。使用 at()
将在您进行 out-of-bounds 访问时立即抛出 out_of_range
异常。然后在转换回对向量使用 [ ]
之前修复这些错误。
例如,这段代码:
j=(size_t)((v[i]-min1)/delta);
if(j==NHISTOMAX) j--;
histo[j]++;
如果 j
是一个巨大的数字,远远超过 histo
向量的范围怎么办?将 j
减 1 无济于事。要查看这是否是一个问题,可以在此处使用 at()
:
j=(size_t)((v[i]-min1)/delta);
if(j==NHISTOMAX) j--;
histo.at(j)++;
如果您 运行 程序,如果 j
超出范围,一旦您尝试递增 histo[j]
,就会抛出异常。然后您可以检查问题并修复错误。
看起来您在以下位置可能存在数组溢出:
delta = (max1-min1)/num2;
...
j = (int)((v[i]-min1)/delta);
histo[j]++;
当 v[i] == max1
这使得 j == num2 == NHISTOMAX
但 histo[NHISTOMAX]
超出范围(最后一个有效元素是 NHISTOMAX-1
,因为数组索引从 0 开始)。
由于您将问题标记为 C++,因此我会使用 std::vector<double>
而不是原始数组:
#include <vector>
...
std::vector<double> v(N);
...
void histograma (string name, const std::vector<double> v, const int maxHistoSize)
{
std::vector<double> histo(maxHistoSize + 1);
...
delta = (max1 - min1) / maxHistoSize;
...
// There's likely a more C++ idiomatic way to get the min/max of a
// std::vector<> but this works fine with a few changes.
double max (const std::vector<double> v)
{
double minValue = v[0];
for(int i = 1; i< v.size(); i++)
{
if (minValue < v[i]) minValue = v[i];
}
return aux;
}
// Same for min()