You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决三体问题模拟程序返回'inf'的问题?

解决三体问题模拟中出现inf/nan的问题

我正在研究经典物理中的三体问题,编写了如下C++程序:

#include <math.h>
#include <iostream>
using namespace std;

class System_variables
{
    private:

        // 系统变量:位置、速度、加速度
        long double body_1_pos[3];
        long double body_2_pos[3];
        long double body_3_pos[3];

        long double body_1_v[3];
        long double body_2_v[3];
        long double body_3_v[3];

        long double acc_body_1[3];
        long double acc_body_2[3];
        long double acc_body_3[3];

        // 所需常量
        const long double body_mass = 6 * pow(10, 24);
        double time_step;
        const long double G = 6.67 * pow(10, -11);

    public:


        System_variables(long double body_1_pos_i[3], long double body_2_pos_i[3], long double body_3_pos_i[3], long double body_1_v_i[3], long double body_2_v_i[3], long double body_3_v_i[3])
        {
            // 初始化位置和速度
            for (int m = 0; m<=2; m++)
            {
                body_1_pos[m] = body_1_pos_i[m];
                body_2_pos[m] = body_2_pos_i[m];
                body_3_pos[m] = body_3_pos_i[m];

                body_1_v[m] = body_1_v_i[m];
                body_2_v[m] = body_2_v_i[m];
                body_3_v[m] = body_3_v_i[m];
            }
        }
        void init_time_step(double step)
        {
            // 初始化时间步长
            time_step = step;
        }
        void calculate_acc()
        {   
            // 用牛顿方程计算天体加速度
            for (int m=0; m<=2; m++)
            {
                acc_body_1[m] = G * body_mass/pow((body_2_pos[m] - body_1_pos[m]), 2) + G * body_mass/pow((body_3_pos[m] - body_1_pos[m]), 2);
                acc_body_2[m] = G * body_mass/pow((body_1_pos[m] - body_2_pos[m]), 2) + G * body_mass/pow((body_3_pos[m] - body_2_pos[m]), 2);
                acc_body_3[m] = G * body_mass/pow((body_1_pos[m] - body_3_pos[m]), 2) + G * body_mass/pow((body_2_pos[m] - body_3_pos[m]), 2);
            };
            return;
        }

        void apply_acc_to_v()
        {   
            // 将加速度应用到速度
            for (int m=0; m<=2; m++)
            {
                body_1_v[m] += acc_body_1[m] * time_step;
                body_2_v[m] += acc_body_2[m] * time_step;
                body_3_v[m] += acc_body_3[m] * time_step;
            }
            return;
        }

        void apply_v_to_pos()
        {   
            // 将速度应用到位置
            for (int m=0; m<=2; m++)
            {
                body_1_pos[m] += body_1_v[m] * time_step;
                body_2_pos[m] += body_2_v[m] * time_step;
                body_3_pos[m] += body_3_v[m] * time_step;
            }
            return;
        }

        long double get_body_1_pos(int index)
        {
            // 返回天体1对应坐标轴的位置
            return body_1_pos[index];
        }

        long double get_body_2_pos(int index)
        {   
            // 返回天体2对应坐标轴的位置
            return body_2_pos[index];
        }

        long double get_body_3_pos(int index)
        {
            // 返回天体3对应坐标轴的位置
            return body_3_pos[index];
        }

        long double get_body_1_v(int index)
        {
            // 返回天体1对应坐标轴的速度
            return body_1_v[index];
        }

        long double get_body_2_v(int index)
        {
            // 返回天体2对应坐标轴的速度
            return body_2_v[index];
        }

        long double get_body_3_v(int index)
        {
            // 返回天体3对应坐标轴的速度
            return body_3_v[index];
        }


};

int main()
{
    long double array_1[3] = {100,100,100}; 
    long double array_2[3] ={10000000000, 10000000000, 10000000000};
    long double array_3[3] = {10000000000, 10000000000, -10000000000};
    long double array_0[3] =  {0,0,0};

    System_variables system(array_1, array_2, array_3, array_0, array_0, array_0);
    system.init_time_step(100);

    for (int time = 0; time <= 10; time++)
    {
        cout << "x position of body 1: " << system.get_body_1_pos(0) << endl 
        << "y position of body 1: " << system.get_body_1_pos(1) << endl 
        << "z position of body 1: " << system.get_body_1_pos(2) << endl << endl;

        cout << "x position of body 2: " << system.get_body_2_pos(0) << endl 
        << "y position of body 2: " << system.get_body_2_pos(1) << endl 
        << "z position of body 2: " << system.get_body_2_pos(2) << endl << endl;

        cout << "x position of body 3: " << system.get_body_3_pos(0) << endl 
        << "y position of body 3: " << system.get_body_3_pos(1) << endl 
        << "z position of body 3: " << system.get_body_3_pos(2) << endl << endl;

        system.calculate_acc();
        system.apply_acc_to_v();
        system.apply_v_to_pos();
    }
}

我尝试过用long double替代double,以为更高精度能解决运行后输出inf的问题,但没用;换成float也一样,输出结果如下:

x position of body 1: 100
y position of body 1: 100
z position of body 1: 100

x position of body 2: 1e+10
y position of body 2: 1e+10
z position of body 2: 1e+10

x position of body 3: 1e+10
y position of body 3: 1e+10
z position of body 3: -1e+10

x position of body 1: 100.08
y position of body 1: 100.08
z position of body 1: 100.08

x position of body 2: inf
y position of body 2: inf
z position of body 2: 1e+10

x position of body 3: inf
y position of body 3: inf
z position of body 3: -1e+10

x position of body 1: 100.16
y position of body 1: 100.16
z position of body 1: 100.24

x position of body 2: -nan(ind)
y position of body 2: -nan(ind)
z position of body 2: 1e+10

x position of body 3: -nan(ind)
y position of body 3: -nan(ind)
z position of body 3: -1e+10

x position of body 1: -nan(ind)
y position of body 1: -nan(ind)
z position of body 1: 100.48

x position of body 2: -nan(ind)
y position of body 2: -nan(ind)
z position of body 2: 1e+10

x position of body 3: -nan(ind)
y position of body 3: -nan(ind)
z position of body 3: -1e+10

我考虑过用指针,但不确定是不是正确方向,请问怎么解决inf的问题?


问题根源分析

  • 引力加速度计算逻辑错误:代码中按单个坐标轴的距离平方计算加速度,完全不符合万有引力定律。万有引力加速度是基于三维空间中天体的直线距离,且是矢量,需要用单位矢量修正方向。
  • 初始位置导致分母为0:天体2和天体3的x、y坐标完全相同,计算它们之间x、y轴的加速度时,分母为0,直接产生inf,后续计算扩散为nan。
  • 时间步长过大:100秒的步长对于天体运动来说太大,数值积分误差快速累积导致计算发散。

修复方案

1. 修正加速度计算逻辑

重新实现符合物理规律的加速度计算,先算三维距离,再通过单位矢量得到各轴加速度分量:

void calculate_acc()
{   
    // 计算天体1受到的加速度
    long double dx12 = body_2_pos[0] - body_1_pos[0];
    long double dy12 = body_2_pos[1] - body_1_pos[1];
    long double dz12 = body_2_pos[2] - body_1_pos[2];
    long double dist12_sq = dx12*dx12 + dy12*dy12 + dz12*dz12;
    long double dist12 = sqrt(dist12_sq);
    long double acc_mag12 = G * body_mass / dist12_sq;

    long double dx13 = body_3_pos[0] - body_1_pos[0];
    long double dy13 = body_3_pos[1] - body_1_pos[1];
    long double dz13 = body_3_pos[2] - body_1_pos[2];
    long double dist13_sq = dx13*dx13 + dy13*dy13 + dz13*dz13;
    long double dist13 = sqrt(dist13_sq);
    long double acc_mag13 = G * body_mass / dist13_sq;

    acc_body_1[0] = acc_mag12 * dx12 / dist12 + acc_mag13 * dx13 / dist13;
    acc_body_1[1] = acc_mag12 * dy12 / dist12 + acc_mag13 * dy13 / dist13;
    acc_body_1[2] = acc_mag12 * dz12 / dist12 + acc_mag13 * dz13 / dist13;

    // 计算天体2受到的加速度
    long double dx21 = body_1_pos[0] - body_2_pos[0];
    long double dy21 = body_1_pos[1] - body_2_pos[1];
    long double dz21 = body_1_pos[2] - body_2_pos[2];
    long double dist21_sq = dx21*dx21 + dy21*dy21 + dz21*dz21;
    long double dist21 = sqrt(dist21_sq);
    long double acc_mag21 = G * body_mass / dist21_sq;

    long double dx23 = body_3_pos[0] - body_2_pos[0];
    long double dy23 = body_3_pos[1] - body_2_pos[1];
    long double dz23 = body_3_pos[2] - body_2_pos[2];
    long double dist23_sq = dx23*dx23 + dy23*dy23 + dz23*dz23;
    long double dist23 = sqrt(dist23_sq);
    long double acc_mag23 = G * body_mass / dist23_sq;

    acc_body_2[0] = acc_mag21 * dx21 / dist21 + acc_mag23 * dx23 / dist23;
    acc_body_2[1] = acc_mag21 * dy21 / dist21 + acc_mag23 * dy23 / dist23;
    acc_body_2[2] = acc_mag21 * dz21 / dist21 + acc_mag23 * dz23 / dist23;

    // 计算天体3受到的加速度
    long double dx31 = body_1_pos[0] - body_3_pos[0];
    long double dy31 = body_1_pos[1] - body_3_pos[1];
    long double dz31 = body_1_pos[2] - body_3_pos[2];
    long double dist31_sq = dx31*dx31 + dy31*dy31 + dz31*dz31;
    long double dist31 = sqrt(dist31_sq);
    long double acc_mag31 = G * body_mass / dist31_sq;

    long double dx32 = body_2_pos[0] - body_3_pos[0];
    long double dy32 = body_2_pos[1] - body_3_pos[1];
    long double dz32 = body_2_pos[2] - body_3_pos[2];
    long double dist32_sq = dx32*dx32 + dy32*dy32 + dz32*dz32;
    long double dist32 = sqrt(dist32_sq);
    long double acc_mag32 = G * body_mass / dist32_sq;

    acc_body_3[0] = acc_mag31 * dx31 / dist31 + acc_mag32 * dx32 / dist32;
    acc_body_3[1] = acc_mag31 * dy31 / dist31 + acc_mag32 * dy32 / dist32;
    acc_body_3[2] = acc_mag31 * dz31 / dist31 + acc_mag32 * dz32 / dist32;
}

2. 修改初始位置避免分母为0

调整天体2和3的坐标,让它们的x/y轴存在差值:

long double array_2[3] ={10000000000, 10000000000, 10000000000};
long double array_3[3] = {10000000000 + 1000, 10000000000, -10000000000};

3. 缩小时间步长

将步长改为1秒,降低积分误差:

system.init_time_step(1);

4. 数值计算优化

  • 用直接相乘替代pow计算平方,精度更高且速度更快,比如dx*dx代替pow(dx,2)。
  • 可以改用Verlet积分替代当前的欧拉积分,Verlet积分在长期模拟中稳定性更好,不易发散。

额外提示

指针解决不了这个问题,核心问题是物理公式错误和初始条件设置不当,和数据存储方式无关。

内容的提问来源于stack exchange,提问作者Ronny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:44:51