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

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

(1)万有引力定律

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

以数学表示为:

其中:

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

依照国际单位制,F的单位为牛顿(N),m1和m2的单位为千克(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. 【python学习-3】python数据类型

    1.数字 在python 2.x中,数字类型有4种,int.long.float 和 complex(复数):而python 3 中,只有 int.float 和 complex 3种,python ...

  2. mysql创建索引笔记

    1.添加PRIMARY KEY(主键索引.就是 唯一 且 不能为空.): ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 2.添加UNIQU ...

  3. OSNIT信息收集分析框架OSRFramework

     OSNIT信息收集分析框架OSRFramework OSNIT是一种从公开的信息资源搜集信息的有效方式.Kali Linux集成了一款专用分析工具集OSRFramework.该工具集包含多个常用工具 ...

  4. 24.python中类的方法

    类中的方法,其实就是类中的函数,可以分为:实例方法,类方法,静态方法.方法和字段一样,也是属于类的属性,所以也具有运行中修改的特效, 但一般不推荐这样做. 我在类的基本语法中,介绍了构造器方法:__i ...

  5. react的非DOM操作

    非dom属性?dangerouslySetInnerHTML,ref,key非dom标准属性,也就是说dom标准里面没有规定的属性,react引入了三个非dom属性,如上. dangerouslySe ...

  6. BZOJ2330 SCOI2011糖果

    复习了一波差分约束. http://blog.csdn.net/my_sunshine26/article/details/72849441 构图方式记住就好. 本题要倒序插入否则会被卡. #incl ...

  7. Android中,利用Intent传递对象值

    在很多情况下,调用startActivity(Intent) 方法,跳转到另外一个Activity或其他component,需要传递一个对象给它. 可以让这个要传递的对象所属类实现Serializab ...

  8. Codeforces Round #371 (Div. 2) C. Sonya and Queries 水题

    C. Sonya and Queries 题目连接: http://codeforces.com/contest/714/problem/C Description Today Sonya learn ...

  9. Sed&awk笔记之awk篇(转)

    Awk是什么 Awk.sed与grep,俗称Linux下的三剑客,它们之间有很多相似点,但是同样也各有各的特色,相似的地方是它们都可以匹配文本,其中sed和awk还可以用于文本编辑,而grep则不具备 ...

  10. ubuntu修改时区和时间的方法

    改时区参考 http://blog.sina.com.cn/s/blog_6c9d65a1010145st.html 1.首先查看时区: swfsadmin@swfsubuntu:~$ date -R ...