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

    简介 shell进程会在其会话中保存此前用户执行过的命令. 历史列表(history list):当前shell所使用的历史命令存储位置. 历史文件(history file):每次登入shell,就 ...

  2. C# Socket异步实现消息发送--附带源码

    前言 看了一百遍,不如动手写一遍. Socket这块使用不是特别熟悉,之前实现是公司有对应源码改改能用. 但是不理解实现的过程和步骤,然后最近有时间自己写个demo实现看看,熟悉熟悉Socket. 网 ...

  3. 减少Building 'Xxx' Gradle project info等待时间

    转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6640279.html 从Github上看到好的Demo想要Download下来学习.导入到Android ...

  4. Nmap扫描教程之基础扫描详解

    Nmap扫描教程之基础扫描详解 Nmap扫描基础扫描 当用户对Nmap工具了解后,即可使用该工具实施扫描.通过上一章的介绍,用户可知Nmap工具可以分别对主机.端口.版本.操作系统等实施扫描.但是,在 ...

  5. 分位函数(四分位数)概念与pandas中的quantile函数

    p分位函数(四分位数)概念与pandas中的quantile函数 函数原型 DataFrame.quantile(q=0.5, axis=0, numeric_only=True, interpola ...

  6. 12、Redis的事务

    写在前面的话:读书破万卷,编码如有神 --------------------------------------------------------------------------------- ...

  7. MikroTik RouterOS安装chr授权到阿里云虚拟机(转)

    CHR介绍 CHR(Cloud Hosted Router) 是用于在虚拟机上运行的 RouterOS 版本,它支持x86_64架构,支持大多数流行的虚拟化技术,如 VMWare, Hyper-V, ...

  8. 关于MongoDB时区问题

    由于MongoDb存储时间按照UTC时间存储的,其官方驱动MongoDB.driver存储时间的时候将本地时间转换为了utc时间,但它有个蛋疼的bug,读取的时候非常蛋疼的是返回的是utc使时间.一个 ...

  9. WCF实现多个服务

    本篇体验使用WCF实现2个服务.一个使用HTTP通讯,一个使用TCP通讯. 大致思路是: → 创建WCF服务以及接口,写2个接口→ 实现2个接口→ 为WCF创建一个控制台项目的宿主,配置App.con ...

  10. iOS中block简介-作用域

    转:http://www.2cto.com/kf/201401/269467.html 用block可以定义任意的代码片段,将其像对象一样传入另一个方法:它是c级别的语法,和C语言中的函数指针非常相似 ...