【问题标题】:Even when using the same randomseed in Lua, get different results?即使在 Lua 中使用相同的随机种子,得到不同的结果?
【发布时间】:2016-06-03 07:38:01
【问题描述】:

我有一个大型的、相当复杂的程序内容生成 lua 项目。出于调试目的,我想做的一件事是使用随机种子,以便我可以重新运行系统并获得相同的结果。

最后,我在运行开始时打印出种子。问题是,每次运行它时,我仍然得到完全不同的结果。假设种子在其他任何地方都没有改变,这应该是不可能的,对吧?

我的问题是,还有什么其他方法可以影响 lua 的 math.random() 的输出?我已经搜索了项目中的所有代码,并且只有一个地方调用了 math.randomseed(),而且我在做其他任何事情之前都会这样做。任何事情。我不使用时间或日期进行任何计算,因此不会影响结果...我还能遗漏什么?


2/22/16 更新猴子补丁 math.random 和 math.randomseed 经常(但不总是)输出相同的随机数序列。但结果仍然不一样——所以我想现在的真正问题是:lua 中的哪些行为是不确定的,并且当相同的代码按顺序运行时可能会导致不同的输出?注意它在哪里发散,什么时候发散,这有助于我缩小范围,但我仍然没有找到它。 (这段代码使用协程,所以我不认为这是线程/竞争条件问题)

【问题讨论】:

  • 请创建一个minimal reproducible example
  • 我觉得可能是外部程序在影响它,但我没有足够的技术知识来支持这一点。
  • math.random 在内部使用tutorialspoint.com/c_standard_library/c_function_rand.htm,也许这个函数在某些方面被弄乱了,除了你的程序之外还有另一种方式。
  • 我很想提供一个最小、完整且可验证的示例,但它是一个非常复杂的系统。到目前为止,我以更小的形式复制它的尝试被证明是徒劳的,这就是为什么我正在寻找关于寻找什么的想法。

标签: random lua procedural-generation


【解决方案1】:

randomseed 正在使用 srandom/srand 函数,该函数“将其参数设置为由 random() 返回的新伪随机整数序列的种子”。

我可以提供几种可能的解释:

  1. 您认为您调用了randomseed,但实际上没有(在这种情况下random 将为您初始化序列)。
  2. 您认为您调用了一次randomseed,但您调用了多次(或者代码的其他部分也调用了randomseed,可能在您的序列中的不同时间)。
  3. 代码的其他部分调用random(若干次),这会为您的代码部分生成不同的结果。
  4. 生成的序列没有任何问题,但您误解了结果。
  5. 您的 Lua 版本在 srandom/random 处理中存在错误。
  6. 您系统中的srandomrandom 函数有问题。

了解有关您的 Lua 版本和系统的一些信息(除了演示问题的小示例)将有助于找出导致此问题的原因。

2016/2/22 更新:应该很容易检查; monkeypatch math.randomseedmath.random 并记录所有调用和函数返回的值,以进行两次后续运行。比较结果。如果结果不同,您应该能够找出它们不同的原因并在较小的示例中重现。您还可以使用debug.traceback 查看函数的调用位置。

【讨论】:

  • 绝对不是#1,而且#2似乎不太可能。如果我每次运行的第一件事是设置种子,我不确定#3 会如何产生不同的结果。它产生文本输出,这很明显不同,所以#4 似乎也是不可能的。我已经尝试过使用 luajit 2.1.0 lua 5.1.5,所以#5 需要 2 个不同的版本才能有相同的错误。而#6似乎太不可能了。我想这一定是做错了什么。不过,我将继续尝试将其范围缩小为可重现的示例。
  • @sinisterandroid,用建议更新了帖子。
  • 谢谢,这非常有用!我还没有找到解决方案,但你的回答让我更接近了。
  • @sinisterandroid,它不可能是线程,因为 Lua 不使用线程(你说你不使用协程)。如果你真的使用线程,那么你有多个 Lua 状态/VM,这意味着每个 VM 有不同的随机序列(它们可能以不可预知的方式重叠)。
【解决方案2】:

正确,正如documentation 中所述,“相同的种子产生相同的数字序列。”

在将种子设置为已知的常量值后,立即输出对 rand 的调用 - 如果这在运行期间有所不同,您就知道出现了严重错误(损坏的库下载、重击安装、伽马射线击中您的驱动器等)。

假设第一个值在运行中匹配,则在代码中途添加另一个输出。从那里,您可以使用二进制搜索将出错的地方归零(即相关代码块的前半部分或后半部分)。

虽然您可以并且应该使用一些直觉来查找错误,但请记住,如果仅凭直觉就足够了,那么您已经找到了它,因此有必要进行一些系统的消除。

修订以涵盖有关数组顺序的注释:

如果可能,请使用调试工具。 detecting when the value of a Lua variable changes 上的这篇 SO 帖子可能会有所帮助。

在没有工具的情况下,这是解决这个问题的一种方法:

任何大型数组的完整调试转储很快就会变得一团糟,很难发现更改。相反,我会使用一些额外的变量和一个测试函数来保持简洁。

制作数组的两个深层副本。让我们称它们为debug01 & debug02 & 称原始数组为original。接下来,特意调换debug02中两个元素的顺序。

接下来,构建一个函数来比较两个数组并测试它们的元素是否匹配,如果不匹配则返回/打印第一个不匹配的索引。初始化数组后,立即测试它们以确保:

  1. original & debug01 匹配
  2. original & debug02 不匹配
  3. original & debug02 与您更改它们的位置不匹配

对于使用未经验证(因此可能存在错误)的测试功能来追踪错误,我再怎么强调也不为过。

一旦您确认该功能有效,您就可以再次使用二分搜索将事情偏离轨道的位置归零。和以前一样,平衡使用系统搜索与您的直觉。

【讨论】:

  • 谢谢!我现在正在这样做。虽然进展缓慢,但我认为这种方法(最终)会帮助我实现目标。
  • 这种方法最终让我找到了问题的根源——具体来说,我不知道如何这是发生的,但是在随后的运行中,相同的数组会发现自己不同的顺序。因此,即使随机 #s 相同,它们指的是该数组的不同元素,这会导致运行发散。也就是说,我仍然无法在较小的测试用例中重现它。
猜你喜欢
  • 1970-01-01
  • 2019-05-31
  • 1970-01-01
  • 1970-01-01
  • 2016-09-16
  • 2019-04-10
  • 2018-11-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多