这依然是与《三体》有关的一篇文章。空间中三个星体在万有引力作用下的运动被称之为三体问题,参见我的上一篇文章:三体运动的程序模拟。而这一节,对三体问题进行了扩展,实现了空间中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. FreeMarker快速入门

    虽然当前比较推荐使用thymeleaf替代jsp作为java网页开发的模板语言,不过公司推荐使用freemarker,那就顺势而为,速度学一发,然后迅速开始新项目了. 简介 FreeMarker第一个 ...

  2. 初识thinkphp(3)

    这篇内容主要涉及请求相应内容. 该系列主要是个人笔记,且内容是连贯的,其中涉及到的自己写的模块或者方法在前面文章中有介绍咋来的,如果您看得云里雾里,给您带来不便,真的不好意思. 0x01:请求对象 官 ...

  3. 基于.htaccess的Web Shell工具htshells

    基于.htaccess的Web Shell工具htshells   .htaccess文件是Apache服务器的配置文件.它负责相关目录下的网页配置.一旦用户获得修改该文件的权限,就可以基于该文件构建 ...

  4. [ZOJ3781]Paint the Grid Reloaded

    思路: 先用DFS缩点,然后BFS找出每个点出发能到达的最长路,取$min$. 注意多组数据,初始化一定要仔细,刚开始存边的$e$忘记初始化,一直WA,调了半个晚上. 一开始和网上的题解对拍$T=1$ ...

  5. android防止按钮连续点击方案之AOP

    转载请标明出处http://www.cnblogs.com/yxx123/p/6675567.html 防止连续点击的实现方式有很多种,比如,在所有的onclick里面加上防多次点击的代码,或者定义一 ...

  6. PHP常用设计模式

    1.单例模式指在整个应用中只有一个对象实例的设计模式 class Single { public $rand; static private $instance; // 类直接调用 final pri ...

  7. 【Go命令教程】5. go clean

    执行 go clean 命令会删除掉执行其它命令时产生的一些文件和目录,包括: 在使用 go build 命令时在当前代码包下生成的与包名同名或者与Go源码文件同名的可执行文件.在Windows下,则 ...

  8. [Asp.net mvc]Html.ValidationSummary(bool)

    摘要 对ValidationSummary是HtmlHelper的扩展方法,用来返回 System.Web.Mvc.ModelStateDictionary (即ModelState)对象中的验证消息 ...

  9. Snmp学习总结(六)——linux下安装和配置SNMP

    一.安装SNMP 1.1.下载Net-SNMP的源代码 选择一个SNMP版本,比如5.7.1,下载地址如下:http://sourceforge.net/projects/net-snmp/files ...

  10. 为什么我不再用 .NET 框架

    .NET平台很棒.真的很棒.直到它不再那么棒.我为什么不再用.NET?简单来说,它限制了我们选择的能力(对我来说很重要),转移了我们的注意力,使得我们向内认知它的安全性,替代了帮助我们认知外面广阔世界 ...