news 2026/8/15 23:53:49

算法(24):event-driven simulation

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法(24):event-driven simulation

Page 42-43(目标与物理模型)

内容:目标是用弹性碰撞定律模拟N个运动粒子的行为。物理模型是“硬球模型”:

  • 粒子是圆盘,有位置、速度、质量和半径。

  • 只存在弹性碰撞(粒子之间、粒子与墙壁)。

  • 没有其他外力(重力、摩擦力忽略)。
    科学意义:这个模型可以推导出宏观性质(温度、压力)与微观运动的关系(麦克斯韦-玻尔兹曼分布、布朗运动)。
    你的关注点:这是一个动态系统,你需要在时间轴上处理粒子状态的变化。


Page 44-47(时间驱动模拟——为什么它不行)

第44-45页:展示了一个简单的Ball类,每次移动固定时间步长dt,遇到墙壁就反弹。但缺少粒子间的碰撞检测。
第46页:时间驱动模拟的流程:离散时间步长dt,每一步都检查所有粒子对是否重叠,如果重叠则回退到碰撞时刻,更新速度,继续。
第47页(关键缺陷)

  • 问题1:每一步需要检查~N²/2对粒子,如果dt很小,计算量爆炸。

  • 问题2:如果dt太大,两个粒子可能在两次检查之间穿过彼此而没有重叠,导致完全错过碰撞
    结论:时间驱动模拟在粒子数量大或需要高精度时不可行。


Page 48(事件驱动模拟——核心思想)

物理策略

  • 只在“有事情发生”时前进时间(两次碰撞之间,所有粒子做匀速直线运动)。

  • 维护一个按时间排序的优先队列,里面存放所有未来的潜在碰撞事件。

  • 每次从队列中取出时间最小的事件(即下一次碰撞),处理它,然后预测这次碰撞可能引发的新事件,重新插入队列。

你的关注点:这正是在 DFT/逻辑仿真中处理“信号翻转事件”的经典模式。优先队列在这里扮演“时间调度器”的角色。


Page 49(粒子-墙壁碰撞预测)

物理公式:计算粒子到墙壁的距离,除以速度在垂直方向的分量,得到碰撞时间。
PPT上给出了公式,但物理细节不重要,你只需要知道:timeToHitWall()返回一个double值,表示“从现在开始,多少时间后会撞墙”。


Page 50-52(粒子-粒子碰撞预测与解析)

PPT 给出了完整的向量公式(相对速度、相对位置、判别式d),用于计算两个圆盘何时相撞,以及碰撞后的速度变化。
但是,PPT 在第51页用大字标注了:

"Important note: This is high-school physics, so we won't be testing you on it!"(这是高中物理,不会考你。)

所以这些公式你完全不需要记忆。你只需要知道:timeToHit(Particle that)返回两个粒子碰撞的时间(如果它们正在远离,则返回无穷大);bounceOff(Particle that)更新两个粒子的速度向量。


Page 53-54(Particle 数据类型的骨架)

展示了timeToHitbounceOff的实现代码。代码较长,但核心逻辑已经被上面的公式覆盖。物理细节同样不需要深究。


Page 55(事件驱动模拟的初始化和主循环)——这一页是算法的核心

初始化

  • 把所有粒子与墙壁的潜在碰撞加入优先队列。

  • 把所有粒子与粒子的潜在碰撞加入优先队列。

  • 这些事件都是“潜在”的——因为如果某个碰撞先发生了,后续的事件可能就无效了。

主循环

  1. 从优先队列中取出最小时间的事件(t)。

  2. 检查事件是否已失效:如果该事件涉及的粒子自事件创建以来已经发生过其他碰撞,则这个事件就是无效的,直接丢弃。

  3. 将所有粒子按直线轨迹移动到时间t

  4. 更新碰撞粒子的速度(调用bounceOff)。

  5. 只针对刚碰撞的粒子,重新预测它们未来可能的新碰撞(粒子-墙壁、粒子-粒子),并插入优先队列。

关键物理事实:为什么只预测“刚碰撞的粒子”?因为其他粒子的轨迹没有改变,它们未来的碰撞时间依然有效。只有速度发生了变化的粒子,才会改变未来事件的时钟。这正是“事件驱动”高效的原因——你只更新受影响的那一小部分。


Page 56(Event 数据类型)

约定

  • 两个粒子都不为空 → 粒子-粒子碰撞。

  • 一个粒子不为空 → 粒子-墙壁碰撞。

  • 两个都为空 →重绘事件(用来刷新屏幕动画)。
    Event类还存储了事件创建时,两个粒子的碰撞计数器(countAcountB)。用来检测事件是否失效(如果当前粒子计数器与存储的值不同,说明粒子在事件被处理前已经撞过了)。


Page 57-58(CollisionSystem 框架代码)

predict(a)方法:为给定粒子a,计算它与所有其他粒子的碰撞时间,以及与墙壁的碰撞时间,将它们作为新事件插入优先队列。
simulate()方法:就是 Page 55 描述的主循环。


Page 59-62(模拟可视化示例)

展示了几种不同的粒子碰撞模拟动画截图:

  • 随机粒子

  • 台球布局(billiards.txt

  • 大质量粒子撞击小粒子群
    这些只是运行结果展示,没有新的算法内容。


这一节你需要记住的唯一算法机制:

“当事件发生时,只重新计算受影响的粒子的未来事件,其他事件保持不变。用碰撞计数器标记事件的有效性,避免在事件触发前从堆中删除它们。”

一、运动公式推导

Q:我想看看这页的“高中物理”都是怎么来的

A:

1. 物理建模:把两个粒子简化为“一个粒子”的运动

你把粒子ij的速度、位置相减,得到相对速度Δv相对位置Δr

  • 物理含义:你可以想象粒子i静止不动,粒子jΔv运动。它们能否相撞,取决于Δv是否指向Δr(即二者是否在接近)。


2. 碰撞条件:它们中心距离等于σ


3. 展开成标准二次方程


4. PPT 中的判别式d就是二次方程的判别式b² - 4ac


5. 为什么条件Δv · Δr >= 0直接返回

如果Δv · Δr >= 0

  • Δv · Δr是相对速度在相对位置方向上的投影。

  • 如果>= 0,说明两个粒子正在远离或相对静止。它们当前的距离不会减小,因此无论判据d如何,t只能是负值(过去的时间)或无解。所以直接返回无限大(未来不会发生碰撞)。


6. 最终取根公式

二、关于PQ的用法,初始化

Q:好吧,让我们来探讨一下这里的PQ是怎么使用的。首先我具有如此之多的粒子,并且他们中有一些是静止的,有一些是具有初始速度的,被放置在一个单位面积的空间中。那么这个时候pq里面会有什么呢?你之前说pq里装的是可能的碰撞,但是在这个初始化场景下,我们并不知道哪些是可能的碰撞呀?简要讲一讲。

A:在初始化时,PQ 里会被填入“所有理论上可能发生的碰撞”,即使我们不知道它们是否真的会发生。这个“强行计算并插入,后续通过失效检测过滤掉无效事件”的设计,是这个算法能够避免复杂事件管理的关键。

1. 初始化阶段:强制计算所有潜在的“第一次碰撞”

在模拟开始的那一刻(t=0),所有粒子的位置和速度都是已知的。虽然我们不知道谁会先撞上谁,但我们可以假设没有其他粒子干扰,计算每个粒子与所有其他粒子的“理论首次碰撞时间”。

具体操作

  • 粒子-粒子:对于每一对粒子(i, j),调用timeToHit(particles[j]),计算出它们在假设直线运动下首次碰撞的时间dt。如果dt是有限值(不是无穷大),就将这个事件(t=dt, a=i, b=j)插入优先队列。

  • 粒子-墙壁:对于每个粒子i,调用timeToHitVerticalWall()timeToHitHorizontalWall(),分别计算它撞到四面墙的时间,如果有限,也插入优先队列。

物理后果初始化阶段的计算量是O(N²)(因为要检查所有粒子对)。对于粒子数量庞大的模拟,这确实是一次性开销,但也正是这个模型能正确运行的代价。


2. 为什么可以“盲目地”把所有可能的碰撞都塞进去?

因为你不知道哪些碰撞会被“截胡”。比如粒子 A 和 B 预计在t=5时相撞,但粒子 A 在t=2时先撞了墙,速度改变了,那么 A 和 B 在t=5的碰撞就永远不会发生。

这个算法的处理方式是:它不会在t=2时去堆里删除那个失效的(A,B, t=5)事件。它只是在t=2处理完墙碰撞后,重新计算 A 和 B 的新碰撞时间,并把新事件插入堆中。当时间推进到t=5时,从堆顶弹出(A,B, t=5),然后检查它是否失效:

  • 检查 A 的碰撞计数器(countA)是否自该事件创建以来增加过。

  • 如果增加了(意味着 A 在t=2撞过墙),则(A,B)事件失效,直接丢弃,不处理。

  • 如果没有增加,则事件有效,处理真正的碰撞。

结论:PQ 在初始化阶段装载的是“在没有干扰情况下,所有可能发生的碰撞候选事件”。这种“预测所有可能,忽略失效事件”的策略,是事件驱动模拟能够用堆高效处理的核心原因——你永远不需要在堆中删除任意元素,只需要靠计数器自然淘汰它们。这正是你之前看到的“惰性删除(Lazy Deletion)”或“幽灵事件”模式在粒子碰撞模拟中的具体体现。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/15 23:49:45

Hermes Agent 与 AutoGPT 有什么区别?

核心结论(一句话) AutoGPT 是无状态的「一次性任务执行器」——单次运行、做完即忘;Hermes Agent 是有状态的「持久化学习系统」——跨会话记忆 自动沉淀技能 越用越强。 两者本质差异在于 时间维度:AutoGPT 解决"当前任务…

作者头像 李华
网站建设 2026/8/15 23:48:15

Linux 中文本处理:cut 和 awk命令

cut 和 awk 都是 Linux 中用于文本处理的命令行工具,常用于从文件或数据流中提取、处理和分析文本。cut 偏向简单、快速的列提取,而 awk 是一个完整的文本处理编程语言,功能强大得多。一、cut 命令:简单列提取cut 用于按列&#x…

作者头像 李华
网站建设 2026/8/15 23:40:08

GLM ZCode AI编程助手实战:从安装配置到项目开发全指南

最近,很多开发者朋友在群里讨论一个事儿:智谱AI的GLM大模型和ZCode编程助手,突然宣布对百万用户“重置用量”,并且伴随着新一轮的“价格战”。消息一出,有人欢呼“羊毛来了”,也有人疑惑“这是不是套路&…

作者头像 李华
网站建设 2026/8/15 23:35:37

第二十五章 个体理解工程

第二十五章 个体 第二十五章 个体理解工程 📅 2026年08月14日👤 东塬一老翁📂 第一卷 个体人工智能理论基础 第二十五章 个体理解工程 ——Individual Understanding Model 25.1 个体理解的本质 在个体人工智能中,理解不是对…

作者头像 李华