为了实验,我(很久以前)实施了Conway的Game of Life(我知道this相关的问题!)。
我的实现通过保留2个布尔数组来表示“最后状态”和“正在更新状态”(每次迭代时交换2个数组)。虽然速度相当快,但我常常想知道如何优化它。
例如,一个想法是在迭代N处预先计算可以在迭代(N + 1)处修改的区域(这样如果一个单元不属于这样的区域,它甚至不会被考虑在迭代(N + 1)处进行修改。我知道这很模糊,我从来没有花时间详细介绍......您对如何优化(速度)Game of Life迭代有任何想法(或经验!)吗?
答案 0 :(得分:31)
我将从另一个问题引用我的答案,因为我提到的章节有一些非常有趣和精细的解决方案。一些实现细节是c和/或汇编,是的,但大多数情况下算法可以使用任何语言:
章节17和18 Michael Abrash的Graphics Programmer's Black Book是其中之一 我有史以来最有趣的读物 了。这是思考的一课 箱子外面。整本书是 非常棒,但最终优化了 生命游戏的解决方案是 令人难以置信的编程。
答案 1 :(得分:14)
有一些超快速实现(从内存中)将8个或更多相邻方块的单元格表示为位模式,并将其用作大量预先计算值的索引,以在单个机器指令中确定单元格是否为活着还是死了。
点击这里:
http://dotat.at/prog/life/life.html
还有XLife:
答案 2 :(得分:10)
答案 3 :(得分:2)
算法本身具有固有的可并行性。在未经优化的CUDA内核中使用相同的双缓冲方法,我在4096x4096包裹的世界中每代大约需要25ms。
答案 4 :(得分:2)
什么是最有效的算法主要取决于初始状态。
如果大多数单元格已经死亡,您可以通过跳过空白部分而不是逐个单元地计算内容来节省大量CPU时间。
我认为,首先检查完全死亡的空间是有意义的,当你的初始状态是“随机的,但有可能生命低于5%。”
我只是将矩阵分成两半,然后开始检查较大的矩阵。
所以如果你有10,000 * 10,000的字段,你首先要累积左上角的5,000 * 5,000的状态。
如果第一季度的状态总和为零,您现在可以完全忽略第一季度,然后检查右上角的5,000 * 5,000。
如果其状态总和> 0,您现在将第二季度再分为4个部分 - 并重复检查每个子空间的生命周期。
你现在可以下到8 * 8或10 * 10的子帧(不知道这里最有意义的是什么)。
无论何时找到生命,都要将这些子空间标记为“有生命”。
只有“有生命”的空间需要划分为更小的子空间 - 可以跳过空的空格。
当您完成为所有可能的子空间分配“has life”属性时,您最终得到一个子空间列表,您现在只需向每个方向延伸+1 - 使用空单元格 - 并执行常规(或修改)生命游戏规则给他们。
你可能认为将10,000 * 10,000个spae分成8 * 8的子空间是很多os任务 - 但是积累它们的状态值实际上比执行每个单元格的GoL算法加上它们的计算工作要少得多。 8个邻居加上比较数字和存储净迭代的新状态......
但就像我上面所说的那样,对于一个拥有30%总体的随机初始状态,这没有多大意义,因为找不到很多完全死的8 * 8子空间(留下死256 * 256个子空间)
当然,完美优化的方式将持续但并非最不重要,取决于您的语言。
-110
答案 5 :(得分:1)
正如Arbash的黑皮书中所提到的,获得巨大加速的最简单直接的方法之一是保留更改列表。
不是每次都遍历整个单元网格,而是保留您更改的所有单元格的副本。
这将缩小您在每次迭代时必须完成的工作。
答案 6 :(得分:1)
两个想法:
(1)许多配置大多是空的空间。保持活动单元的链接列表(不一定按顺序,需要更多时间),并且在更新期间,仅更新活动单元(这与您的模糊建议OysterD类似:)
(2)保留一个额外的数组,用于存储3个位置(左中右)每行中的活细胞数。现在,当您计算单元格的新死亡/实时值时,您只需要4个读取操作(顶部/底部行和中心侧位置),以及4个写入操作(更新3个受影响的行汇总值,以及死亡/新细胞的实时价值)。这是从8次读取和1次写入略有改进,假设写入不比读取慢。我猜你可能会对这些配置更加聪明,并在这些方面取得更好的改进。
答案 7 :(得分:0)
不知道如何做到这一点,但我记得我的一些朋友不得不用Quadtree代表这个游戏的网格进行任务。我猜它是优化网格空间的真正好处,因为你基本上只代表被占用的单元格。我不知道执行速度。
答案 8 :(得分:0)
这是一个二维自动机,所以你可以查找优化技术。你的想法似乎是压缩你需要在每一步检查的细胞数量。由于您只需要检查被占用单元占用或相邻的单元格,也许您可以保留所有此类单元格的缓冲区,并在处理每个单元格时在每一步更新它。
如果您的字段最初为空,则速度会快得多。您可能会发现一些平衡点,维护缓冲区比处理所有单元格的成本更高。
答案 9 :(得分:0)
有针对此的表驱动解决方案,可解析每个表查找中的多个单元格。谷歌查询应该给你一些例子。
答案 10 :(得分:0)
我在C#中实现了这个:
所有单元格都有位置,邻居计数,状态和对规则的访问权。
优点:
缺点:
可能的改进:
答案 11 :(得分:-1)
Javascript中最短的实现之一:)
A Game of Life engine Demo in 126 bytes
/* The Game of Life function */
// @param s: current state of the grid
// @param d: size of the grid (d*d)
// @param n: placeholder
// @param k: placeholder
// @param m: placeholder
// @param i: placeholder
// @param j: placeholder
function(s, d, n, k, m, i, j){
for(
n = [], // Initialize next state
m = [d + 1, d, d - 1, 1], // Initialize the upper half of the neighbours indexes
i = d * d; // For each cell
i--;
n[i] = k == 3 || s[i] && k == 2, // Set next state (live if it has 3 neighbours or lives and has 2 neighbours)
k = 0 // Reset the count of living neighbours
)
for(j in m) // for each neighbour position
k += s[i + m[j]] + s[i - m[j]] // count the living neighbours
return(n) // return the next state
}