如何在蒙特卡洛法计算圆周率时使用100万采样点而不崩溃?
解决蒙特卡洛计算圆周率大样本点崩溃的问题
嘿,我来帮你搞定这个问题!你程序崩溃的核心原因其实和编译器无关,是内存分配方式出了问题——咱们一步步来看:
为什么超过10万采样点就崩溃?
你代码里用了变长数组(VLA),比如float x[dot]、float y[dot]这些,这些数组是分配在栈上的。而栈的空间非常有限,Linux系统默认一般只有8MB左右,Windows可能更小。
当dot达到100万时,光是x、y、distance、pi这四个float数组加起来就需要:1,000,000 * 4字节 * 4 = 16MB,这远远超过了栈的默认容量,直接导致栈溢出(Stack Overflow),程序自然就崩溃了。
两种解决方案,推荐第二种更高效的
方案1:改用堆内存分配
堆的空间比栈大得多(可以用到系统大部分可用内存),我们用malloc在堆上分配数组,用完记得用free释放:
#include <time.h> #include <stdlib.h> #include <stdio.h> #include <math.h> int main() { srand(time(NULL)); int dot; int dotC = 0; int dotS = 0; printf("How many dot do you want to use?: "); scanf("%d", &dot); // 堆内存分配,一定要检查是否分配成功 float *x = malloc(dot * sizeof(float)); float *y = malloc(dot * sizeof(float)); float *distance = malloc(dot * sizeof(float)); float *pi = malloc(dot * sizeof(float)); if (!x || !y || !distance || !pi) { fprintf(stderr, "内存分配失败!\n"); return 1; } // 生成x坐标 for (int i = 0; i < dot; i++) { x[i] = (float)rand() / (float)RAND_MAX; } // 生成y坐标 for (int i = 0; i < dot; i++) { y[i] = (float)rand() / (float)RAND_MAX; } // 计算距离 for (int i = 0; i < dot; ++i) { distance[i] = sqrt(pow(x[i], 2) + pow(y[i], 2)); } // 统计圆内点数量 for (int i = 0; i < dot; ++i) { if (distance[i] < 1) { dotC++; } dotS++; } // 计算Pi值 for (int i = 0; i < dot; ++i) { pi[i] = (float)dotC / (float)dotS * 4; } printf("approximation of Pi is: %f\n", pi[0]); // 释放堆内存,避免内存泄漏 free(x); free(y); free(distance); free(pi); return 0; }
方案2:彻底优化代码,完全不用数组(强烈推荐)
其实你的代码里,所有数组都是多余的!我们根本不需要存储所有点的坐标、距离和Pi值,完全可以边生成点边计算,这样内存占用几乎为0,不管多少采样点都不会崩溃,还能提升运行速度:
#include <time.h> #include <stdlib.h> #include <stdio.h> #include <math.h> int main() { srand(time(NULL)); int dot; int dotC = 0; printf("How many dot do you want to use?: "); scanf("%d", &dot); for (int i = 0; i < dot; ++i) { // 直接生成当前点的x、y,不用存储 float x = (float)rand() / (float)RAND_MAX; float y = (float)rand() / (float)RAND_MAX; // 这里用距离平方比较,省去开根号的计算,更快! float dist_sq = x*x + y*y; if (dist_sq < 1.0f) { dotC++; } } // 直接计算最终Pi值 float pi = (float)dotC / dot * 4.0f; printf("approximation of Pi is: %f\n", pi); return 0; }
这个版本的好处:
- 内存占用极小,完全避免栈溢出问题
- 运行效率更高:去掉了数组的存储和遍历,还省去了
sqrt的计算(比较距离平方和1的大小,结果和比较距离与1完全一致)
额外小提示
如果以后需要处理超大数量的采样点(比如1e9),int类型可能会溢出,建议把dot和dotC改成long long类型,避免数值溢出。
内容的提问来源于stack exchange,提问作者Francesco Procopio
相关产品推荐
相关产品推荐

