这依然是与《三体》有关的一篇文章。空间中三个星体在万有引力作用下的运动被称之为三体问题,参见我的上一篇文章:三体运动的程序模拟。而这一节,对三体问题进行了扩展,实现了空间中N个星体在万有引力下的运动模拟。

程序中使用了两个物理定律:

(1)万有引力定律

这是牛顿发现的:任意两个质点有通过连心线方向上的力相互吸引。该引力的大小与它们的质量乘积成正比,与它们距离的平方成反比,与两物体的化学本质或物理状态以及中介物质无关。

以数学表示为:

其中:

  • : 两个物体之间的引力
  • : 万有引力常数
  • : 物体1的质量
  • : 物体2的质量
  • : 两个物体之间的距离

依照国际单位制,F的单位为牛顿(N),m1m2的单位为千克(kg),r 的单位为米(m),常数G近似地等于6.67 × 10−11 N m2 kg−2(牛顿米的平方每千克的平方)。当然程序中G不可能设置为这么小的一个数,用户可以自己设置万有引力常数,以实现合理的运动.

(2)势能与动能守恒定律

引力位能或引力势能是指物体因为大质量物体的万有引力而具有的位能,其大小与其到大质量的距离有关。

其中G为万有引力常数,M、m分别为两物体质量,r为两者距离。

动能,简单的说就是指物体因运动而具有的能量。数值上等于(1/2)Mv^2. 动能是能量的一种,它的国际单位制下单位是焦耳(J),简称焦。

势能与动能守恒即在整个运动过程中,其总能量是不变的,即N体的动能与势能之和为固定值.

本来我只打算使用万有引力定律,没有考虑势能动能守恒定律.但程序运行后发现,由于没有使用微积分,即使采样间隔时间再小,误差也很大.这让我想起当年学大学物理的时候,大一上学期开高数,然后下学期开了物理.现在对大学物理讲的东西,只记得是用高数中的微积分解物理问题.可那时,我高数学得就是个渣,所以物理也没办法学好.当然就算物理学好了,现在也早忘光了.没有用微积分,这个程序中算法误差不小,模拟一下吧,不要太计较.

核心代码也发一下,希望有兴趣的人可以对其做出优化:

 /****************************************************************

   File name   :  NBodyKernel.h
Author : 叶峰
Version : 1.0a
Create Date : 2014/09/19
Description : *****************************************************************/ // -------------------------------------------------------------------------------------- #ifndef _NBodyKernel_H_
#define _NBodyKernel_H_ // INCLUDES ----------------------------------------------------------------------------- #include "..\..\..\Why\YCommon_h\WhyMath.h"
#include "..\..\..\Why\YInterface\Others\YdD3D9Math.h" // -------------------------------------------------------------------------------------- #define YD_ORBIT_VERTICES_COUNT 1024 // -------------------------------------------------------------------------------------- class Star
{
public:
Vector3 vPosition; // 位置
Vector3 vVelocity; // 速度
Vector3 vForce; // 受力
Yreal fWeight; // 质量
Yreal fRadiusScale; // 缩放
Yreal fKineticEnergy; // 动能
Ycolor color; // 颜色
Ybool bOrbitVisible; // 轨迹可见
Yuint id;
Vector3 verticesOrbit[YD_ORBIT_VERTICES_COUNT];
Star* pNext; Star()
{
fWeight = 0.0f;
fRadiusScale = 1.0f;
fKineticEnergy = 0.0f;
color = 0xffffffff;
bOrbitVisible = YD_FALSE;
id = -;
memset(verticesOrbit, , sizeof(verticesOrbit));
pNext = NULL;
} // 根据速度计算动能
void CalculateKineticEnergyByVelocity()
{
Yreal sqv = D3DXVec3LengthSq(&vVelocity);
fKineticEnergy = 0.5f*fWeight*sqv;
} // 根据动能计算速度
void CalculateVelocityByKineticEnergy()
{
float v = sqrt(*fKineticEnergy/fWeight);
float w = D3DXVec3Length(&vVelocity);
vVelocity *= v/w;
};
}; // -------------------------------------------------------------------------------------- class CNBodyKernel
{
public:
CNBodyKernel(); ~CNBodyKernel(); // 更新N体
void UpdateNBody(Yreal deltaTime); // 清空N体
void Clear(); // 设置引力系数
void SetGravitationCoefficient(Yreal c); // 获得引力系数
Yreal GetGravitationCoefficient() const
{
return m_fGravitationCoefficient;
} // 返回星体数目
Yuint GetStarsCount() const
{
return m_starsCount;
} // 返回星体链表
const Star* GetStarsList() const
{
return m_starsList;
} // 返回星体对象
const Star* GetStar(Yuint index) const; // 移除星体
bool RemoveStar(Yuint index); // 添加星体
void AddStar(
const Vector3& vPosition,
const Vector3& vVelocity,
const Yreal fWeight,
Ycolor color); // 设置星体的重量
void SetStarWeight(Yuint index, Yreal weight); // 设置星体的轨迹是否可见
void SetStarOrbitVisible(Yuint index, bool visible); // 设置星体的颜色
void SetStarColor(Yuint index, Ycolor color); Yuint GetCurrentOrbit() const
{
return m_currentOrbit;
} const Vector3* GetStarPosition(Yuint index) const; Yreal GetStarWeight(Yuint index) const; bool GetStarOrbitVisible(Yuint index) const; Ycolor GetStarColor(Yuint index) const; Yuint GetStarID(Yuint index) const; void GetSpaceCenter(Vector3& vCenter) const; void GetWeightCenter(Vector3& vCenter) const; private:
// 更新星体的位置,速度,动能
void UpdateStar(Star& star, Yreal t); // 根据动能重新计算速度
void RecalculateStarVelocity(Star& star); // 计算每个星体的当前受力
void CalculateStarsForce(); // 计算总势能
void CalculateAmountOfPotentialEnergy(); // 计算总动能
void CalculateAmountOfKineticEnergy(); // 计算总能量
void CalculateAmountEnergy()
{
CalculateAmountOfPotentialEnergy();
CalculateAmountOfKineticEnergy();
m_fAmountEnergy = m_fAmountOfPotentialEnergy + m_fAmountOfKineticEnergy;
} private:
Yreal m_fGravitationCoefficient; // 引力系数
Yreal m_fAmountOfPotentialEnergy; // 当前总势能
Yreal m_fAmountOfKineticEnergy; // 当前总动能
Yreal m_fAmountEnergy; // 当前能量m_fAmountOfPotentialEnergy + m_fAmountOfKineticEnergy; Star* m_starsList; // N体链表
Yuint m_starsCount; // N体数目 Yuint m_currentOrbit;
}; // -------------------------------------------------------------------------------------- #endif
 /****************************************************************

   File name   :  NBodyKernel.cpp
Author : 叶峰
Version : 1.0a
Create Date : 2014/09/19
Description : *****************************************************************/ // -------------------------------------------------------------------------------------- #include "..\..\..\Why\YCommon_h\WhyMemoryDefines.h"
#include "NBodyKernel.h"
#include <assert.h> // -------------------------------------------------------------------------------------- #define YD_ESP 0.00001f // -------------------------------------------------------------------------------------- CNBodyKernel::CNBodyKernel()
{
m_fGravitationCoefficient = 100.0f;
m_fAmountOfPotentialEnergy = 0.0f;
m_fAmountOfKineticEnergy = 0.0f;
m_fAmountEnergy = 0.0f; m_starsList = NULL;
m_starsCount = ; m_currentOrbit = ;
} CNBodyKernel::~CNBodyKernel()
{
Clear();
} // 清空N体
void CNBodyKernel::Clear()
{
Star* pDel = m_starsList;
Star* pNext;
while (pDel)
{
pNext = pDel->pNext;
YD_DELETE(pDel);
pDel = pNext;
} m_starsList = NULL;
m_starsCount = ;
} // 获得引力系数
void CNBodyKernel::SetGravitationCoefficient(Yreal c)
{
m_fGravitationCoefficient = c; // 计算总能量
CalculateAmountEnergy();
} // 计算每个星体的当前受力
void CNBodyKernel::CalculateStarsForce()
{
// 清空所有引力
Star* pStar = m_starsList;
while (pStar)
{
pStar->vForce = Vector3(0.0f, 0.0f, 0.0f);
pStar = pStar->pNext;
} // 计算引力
Star* pCurrent = m_starsList;
Star* pNext;
while (pCurrent)
{
pNext = pCurrent->pNext;
while (pNext)
{
Vector3 vAB = pNext->vPosition - pCurrent->vPosition;
float disSqAB = D3DXVec3LengthSq(&vAB);
if (disSqAB < YD_ESP)
{
disSqAB = YD_ESP;
}
float disAB = sqrt(disSqAB);
Vector3 vForceDir = vAB/disAB;
Yreal fForce = m_fGravitationCoefficient*pCurrent->fWeight*pNext->fWeight/disSqAB;
Vector3 vForce = vForceDir*fForce; pCurrent->vForce += vForce;
pNext->vForce -= vForce; pNext = pNext->pNext;
}
pCurrent = pCurrent->pNext;
}
} void CNBodyKernel::UpdateStar(Star& star, Yreal t)
{
// 加速度
const Vector3 acc = star.vForce/star.fWeight; star.vPosition.x = star.vPosition.x + star.vVelocity.x*t + 0.5f*acc.x*t*t;
star.vPosition.y = star.vPosition.y + star.vVelocity.y*t + 0.5f*acc.y*t*t;
star.vPosition.z = star.vPosition.z + star.vVelocity.z*t + 0.5f*acc.z*t*t; star.vVelocity.x += acc.x*t;
star.vVelocity.y += acc.y*t;
star.vVelocity.z += acc.z*t; // 重新计算动能
star.CalculateKineticEnergyByVelocity();
} void CNBodyKernel::UpdateNBody(Yreal deltaTime)
{
// 计算每个星体的当前受力
CalculateStarsForce(); // 更新星体的位置与速度
Star* pStar = m_starsList;
while (pStar)
{
UpdateStar(*pStar, deltaTime);
pStar = pStar->pNext;
} // 能量守恒
CalculateAmountOfKineticEnergy();
CalculateAmountOfPotentialEnergy(); // 理论上的动能
Yreal fKineticEnergy = m_fAmountEnergy - m_fAmountOfPotentialEnergy;
if (fKineticEnergy < 0.0f)
{
fKineticEnergy = 0.0f;
} Yreal r = fKineticEnergy/m_fAmountOfKineticEnergy;
pStar = m_starsList;
while (pStar)
{
if (r > YD_ESP)
{
pStar->fKineticEnergy *= r;
}
else
{
pStar->fKineticEnergy = 1.0f;
}
// 根据动能计算速度
pStar->CalculateVelocityByKineticEnergy();
pStar = pStar->pNext;
} // 更新轨迹点
pStar = m_starsList;
while (pStar)
{
pStar->verticesOrbit[m_currentOrbit] = pStar->vPosition;
pStar = pStar->pNext;
}
m_currentOrbit++;
if (m_currentOrbit >= YD_ORBIT_VERTICES_COUNT)
{
m_currentOrbit = ;
}
} // 返回星体对象
const Star* CNBodyKernel::GetStar(Yuint index) const
{
if (index >= m_starsCount)
{
return NULL;
} const Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} return pStar;
} // 移除星体
bool CNBodyKernel::RemoveStar(Yuint index)
{
if (index >= m_starsCount)
{
return false;
} if (index == )
{
Star* pStar = m_starsList->pNext;
YD_DELETE(m_starsList);
m_starsList = pStar;
}
else
{
Star* pStar = m_starsList;
Yuint t = index - ;
while (t && pStar)
{
t--;
pStar = pStar->pNext;
}
assert(pStar); Star* pDel = pStar->pNext;
pStar->pNext = pDel->pNext;
YD_DELETE(pDel);
} m_starsCount--; Yuint id = ;
Star* pStar = m_starsList;
while (pStar)
{
pStar->id = id;
id++;
pStar = pStar->pNext;
} // 重算能量
CalculateAmountEnergy(); return true;
} // 添加星体
void CNBodyKernel::AddStar(
const Vector3& vPosition,
const Vector3& vVelocity,
const Yreal fWeight,
Ycolor color)
{
Star* pNewStar = YD_NEW(Star);
pNewStar->vPosition = vPosition;
pNewStar->vVelocity = vVelocity;
pNewStar->fWeight = fWeight;
pNewStar->fRadiusScale = yf_pow(fWeight, 1.0f/3.0f);
pNewStar->CalculateKineticEnergyByVelocity();
pNewStar->color = color;
pNewStar->id = m_starsCount;
for (Yuint i = ; i < YD_ORBIT_VERTICES_COUNT; i++)
{
pNewStar->verticesOrbit[i] = vPosition;
} if (!m_starsList)
{
m_starsList = pNewStar;
}
else
{
Star* pStar = m_starsList;
while (pStar->pNext)
{
pStar = pStar->pNext;
}
pStar->pNext = pNewStar;
} m_starsCount++; // 重算能量
CalculateAmountEnergy();
} // 设置星体的重量
void CNBodyKernel::SetStarWeight(Yuint index, Yreal weight)
{
if (index >= m_starsCount)
{
return;
} Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} pStar->fWeight = weight;
pStar->fRadiusScale = yf_pow(weight, 1.0f/3.0f);
pStar->CalculateKineticEnergyByVelocity(); // 重算能量
CalculateAmountEnergy();
} // 设置星体的轨迹是否可见
void CNBodyKernel::SetStarOrbitVisible(Yuint index, bool visible)
{
if (index >= m_starsCount)
{
return;
} Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} pStar->bOrbitVisible = visible;
} // 设置星体的颜色
void CNBodyKernel::SetStarColor(Yuint index, Ycolor color)
{
if (index >= m_starsCount)
{
return;
} Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} pStar->color = color;
} // 计算总动能
void CNBodyKernel::CalculateAmountOfKineticEnergy()
{
// 动能
m_fAmountOfKineticEnergy = 0.0f;
Star* pStar = m_starsList;
while (pStar)
{
//pStar->CalculateKineticEnergyByVelocity();
m_fAmountOfKineticEnergy += pStar->fKineticEnergy;
pStar = pStar->pNext;
}
} // 计算总势能
void CNBodyKernel::CalculateAmountOfPotentialEnergy()
{
m_fAmountOfPotentialEnergy = 0.0f; Star* pCurrent = m_starsList;
Star* pNext;
while (pCurrent)
{
pNext = pCurrent->pNext;
while (pNext)
{
Vector3 vAB = pCurrent->vPosition - pNext->vPosition;
float disAB = D3DXVec3Length(&vAB);
float fPotentialEnergyAB = -m_fGravitationCoefficient*pCurrent->fWeight*pNext->fWeight/disAB;
m_fAmountOfPotentialEnergy += fPotentialEnergyAB;
pNext = pNext->pNext;
}
pCurrent = pCurrent->pNext;
}
} const Vector3* CNBodyKernel::GetStarPosition(Yuint index) const
{
if (index >= m_starsCount)
{
return ;
} const Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} assert(pStar);
return &pStar->vPosition;
} Yreal CNBodyKernel::GetStarWeight(Yuint index) const
{
if (index >= m_starsCount)
{
return 0.0f;
} const Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} assert(pStar);
return pStar->fWeight;
} bool CNBodyKernel::GetStarOrbitVisible(Yuint index) const
{
if (index >= m_starsCount)
{
return false;
} const Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} assert(pStar);
return pStar->bOrbitVisible != YD_FALSE;
} Ycolor CNBodyKernel::GetStarColor(Yuint index) const
{
if (index >= m_starsCount)
{
return ;
} const Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} assert(pStar);
return pStar->color;
} Yuint CNBodyKernel::GetStarID(Yuint index) const
{
if (index >= m_starsCount)
{
return ;
} const Star* pStar = m_starsList;
while (index && pStar)
{
index--;
pStar = pStar->pNext;
} assert(pStar);
return pStar->id;
} void CNBodyKernel::GetSpaceCenter(Vector3& vCenter) const
{
vCenter = Vector3(0.0f, 0.0f, 0.0f);
if (!m_starsCount)
{
return;
} Star* pStar = m_starsList;
while (pStar)
{
vCenter += pStar->vPosition;
pStar = pStar->pNext;
} vCenter /= (Yreal)m_starsCount;
} void CNBodyKernel::GetWeightCenter(Vector3& vCenter) const
{
vCenter = Vector3(0.0f, 0.0f, 0.0f);
if (!m_starsCount)
{
return;
} Yreal weights = 0.0f;
Star* pStar = m_starsList;
while (pStar)
{
vCenter += pStar->vPosition*pStar->fWeight;
weights += pStar->fWeight;
pStar = pStar->pNext;
} vCenter /= weights;
}

程序启动后,会出现4个球体在运动。不知道大家有没有注意到:Win7的开机动画就是四个球体的运动,所以我这程序的默认也是生成四个球体。

通过UI控件,可以添加删除星体。没有上限,当然星体越多程序越慢。

可以显示每一个星体的运动轨迹

用户可以实时修改任意一个星体的质量

鼠标右键用于控制视角
键盘U用于开关UI用户界面.
通过UI用户界面可以设置三个球体的质量,设置万有引力系数,设置天体运行速度,设置球体的显示大小.

键盘0~9用于开关第N个球体运动轨迹的显示

键盘G,用于开关三维网格的显示
键盘C,用于开关坐标轴的显示
键盘P,用于暂停
键盘R,用于重置.

F11 全屏切换

软件下载地址:http://files.cnblogs.com/WhyEngine/NBody.zip

N体运动的程序模拟的更多相关文章

  1. C程序模拟实现银行家算法

    C程序模拟实现银行家算法 上周又做操作系统实验,题目是用程序模拟实现银行家算法,写了半天还真有点晕,主要是因为想尽可能符合课本上的描述,所以写出来的程序就比较恶心了,好了,银行家算法就不多说了,不了解 ...

  2. WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构

    原文:WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构 细算起来,已经有好几个月没有真正的写过文章了.近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析>的写作,一直 ...

  3. uva 210 - Concurrency Simulator (并行程序模拟)

    from CSDN: https://blog.csdn.net/su_cicada/article/details/87898579 例题6-1 并行程序模拟( Concurrency Simula ...

  4. C# 为VB6.0程序模拟串口数据

    为VB6.0编写程序模拟数据测试使用. 一.VB6.0 控件MSComm,来发送接收串口数据 CommPort 属性设置并返回通讯端口号,虚拟端口为COM2. Settings 属性设置并返回端口的波 ...

  5. 编写程序模拟strlwr()和strupr()函数功能

    strlwr(字符串)strlwr()的作用是将字符串中大写字母转换成小写字母 strupr(字符串)strupr()的作用是将字符串中小写字母转换成大写字母 /* strlwr(字符串) strlw ...

  6. 编写Java程序,车站只剩 50 张从武汉到北京的车票,现有 3 个窗口售卖,用程序模拟售票的过程,使用Runnable解决线程安全问题

    查看本章节 查看作业目录 需求说明: 车站只剩 50 张从武汉到北京的车票,现有 3 个窗口售卖,用程序模拟售票的过程,要求使用同步方法保证售票过程中票数的正确性 实现思路: 创建 Java 项目,在 ...

  7. Android应用程序模拟手机按键

    记得以前在做一个C++项目时,需要在某一步操作之后人为用代码模拟敲键盘上的回车键(Enter)效果. 出于好奇,这几天研究了一下Android中手机(或平板)上各种按键的键值.模拟方法及最终效果. 1 ...

  8. 6-1 并行程序模拟 uva210

    用到了 deque 和queue 操作说明: queue  qu:      qu.push(x); int d=qu.front(); qu.pop();        和栈一样只有push和pop ...

  9. 使用java程序模拟页面发送http的post请求

    在web应用程序中,一般都是通过页面发送http的post请求,但也可以使用java程序来模拟页面发送请求,代码如下: import java.io.BufferedReader; import ja ...

随机推荐

  1. SpringMVC框架06——文件上传与下载

    1.文件上传 Spring MVC框架的文件上传是基于commons-fileupload组件的文件上传,只不过Spring MVC框架在原有文件上传组件上做了进一步封装,简化了文件上传的代码实现. ...

  2. 使用Runnable接口创建线程

    实现Runnable接口的类必须使用Thread类的实例才能创建线程.通过Runnable接口创建线程分为两步: 1.将实现Runnable接口的类实例化. 2.建立一个Thread对象,并将第一步实 ...

  3. Codeforces.666A.Reberland Linguistics(DP)

    题目链接 \(Description\) 给定串s,其由一个基本串后加任意多个长度为2或3的后缀串构成,要求基本串长度>4且相邻后缀串不相同.在基本串任意确定的情况下,求所有可能的后缀串. \( ...

  4. BZOJ.3998.[TJOI2015]弦论(后缀自动机)

    题目链接 \(Description\) 给定字符串S,求其第K小子串.(若T=0,不同位置的相同子串算1个:否则算作多个) \(Solution\) 建SAM,处理出对于每个节点,它和它的所有后继包 ...

  5. DEX文件类型和虚拟机(摘抄)

    DEX文件类型是Android平台上可执行文件的类型. Dalvik是Google公司自己设计用于Android平台的Java虚拟机.Dalvik虚拟机是Google等厂商合作开发的Android移动 ...

  6. mybatis学习笔记(三)-- 优化数据库连接配置

    原来直接把数据库连接配置信息写在conf.xml配置中,如下 <?xml version="1.0" encoding="UTF-8"?> < ...

  7. Codeforces Round #371 (Div. 2) A. Meeting of Old Friends 水题

    A. Meeting of Old Friends 题目连接: http://codeforces.com/contest/714/problem/A Description Today an out ...

  8. Jquery DataTable基本使用

    1,首先需要引用下面两个文件 <link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css ...

  9. 在Visual Studio中使用序列图描述对象之间的互动

    当需要描述多个对象之间的互动,可以考虑使用序列图. 在建模项目下添加一个名称为"Basic Flow"的序列图. 比如描述客户是如何在MVC下获取到视图信息的. 备注: ● 通常是 ...

  10. 在ASP.NET MVC中使用typeahead.js支持预先输入,即智能提示

    使用typeahead.js可以实现预先输入,即智能提示,本篇在ASP.NET MVC下实现.实现效果如下: 首先是有关城市的模型. public class City { public int Id ...