【发布时间】:2016-05-18 21:15:17
【问题描述】:
我已经编程多年了,我现在提出的问题可能是我遇到的最奇怪的问题之一。
我的应用中有一段代码随机生成一个令牌序列,有三种可能的类型,比如 A、B 或 C。
所以 10 个令牌可能是 ABCCAAABAC。
在代码块的开头,随机数生成器种子被初始化如下:
math.randomseed(seed)
math.random()
现在,不出所料,当种子值保持不变时,我总是得到相同的标记序列,因为随机生成代码以确定性方式执行。嗯,几乎总是。
实际上,在极少数情况下,在给定相同种子的情况下,我会出乎意料地得到不同的随机序列。然后在不知不觉中恢复正常。你可能在想 - 啊,副作用,这可能是一个与状态相关的问题,其中生成令牌随机序列的代码块使用了一个变量,该变量改变了它调用 random() 的次数(例如)。但是,我 99% 确信我已经控制了所有明显的副作用。代码块中只有少数地方可以访问外部状态,并且它们都保持不变。
情节更加复杂 - 这个问题只有在我一直在构建的应用的 Android 部署中对我来说很明显。诚然,这是一个罕见的错误,我似乎无法可靠地重复它。所以它也可能出现在 iOS 部署中。但我还没有在其他平台上遇到过。我不妨提一下,我正在通过 Corona SDK 使用 lua 脚本来开发应用程序。
我对这个问题进行了深思熟虑,并将其范围缩小为几种可能性:
- 与另一个使用相同随机数生成器的线程交互,我不知道
- (这在 lua 中是否可能?)某种堆损坏导致奇怪的副作用
- 我搞砸了,有一些该死的明显的对外部状态的引用,我在许多小时的调试过程中都错过了
所有这一切中最痛苦的方面是错误的不可重复性。大多数情况下,代码块在给定重复种子的情况下完全确定性地运行。然后就好像有一个不确定性的阶段,然后在一段未知的时间后再次消散。我很想在这里挑选专家的大脑。
这里会发生什么?另外 - 由于我只在 Android 部署中看到过这个问题,是否可能存在特定于平台的任何问题?
作为参考,这里是完整的代码块。它实际上生成具有两种随机属性(三种颜色中的一种,以及三种形状中的一种)的标记,但这对于问题的本质而言意义不大。
math.randomseed(currentRandomSeed)
math.random()
local tokenListPlan = {}
-- randomly assign weighting distribution
local thresh1, thresh2
while (true) do
local s0 = math.random(1, 99)
local s1 = math.random(1, 99)
local c0 = s0
local c1 = s1 - c0
local c2 = 100 - c1 - c0
if (c0 >= eng.DEVIATION_THRESHOLD and c1 >= eng.DEVIATION_THRESHOLD and c2 >= eng.DEVIATION_THRESHOLD) then
thresh1 = c0
thresh2 = c0 + c1
break
end
end
-- generate tokens (deterministic based on seed)
for i = 1, sortedCountTarget do
local token
local c = 1
local rnd = math.random(1, 100)
if (rnd < thresh1) then -- skewed dist
c = 1
elseif (rnd < thresh2) then
c = 2
else
c = 3
end
if (paramGameMode == eng.GAME_MODE_COLOR) then
local rnd46 = math.random(4, 6)
token = {color = c, shape = rnd46}
elseif (paramGameMode == eng.GAME_MODE_SHAPE) then
local rnd13 = math.random(1, 3)
token = {color = rnd13, shape = c + 3}
else
local rnd13 = math.random(1, 3)
local rnd46 = math.random(4, 6)
token = {color = rnd13, shape = rnd46}
end
tokenListPlan[#tokenListPlan + 1] = token
end
【问题讨论】:
-
你在哪里设置随机种子?可能是,在 android 中,无论出于何种原因,randomseed 设置行不止一次运行。
-
就在代码块之前(中间没有任何内容),我将在这里编辑帖子以减少歧义。不幸的是,我看不到它可以多次执行的任何方式,尤其是不是偶尔...
-
在某些情况下,在此代码运行期间,math.random 触发的次数是否超过预期数量?尝试将它包装到一个函数中,该函数使用闭包来计算它被调用的数量。当你得到不同的结果时,看看它是否发生了变化。
-
您可以简单地创建自己的 RNG,例如,as in this SO Q&A。
-
@DougCurrie 是的,我一直在考虑这样做。事实上,我可以通过将整个生成的序列存储在内存中并在需要时重用它来解决整个问题,而不是使用相同的种子重新生成。但我认为这个问题既具有实际意义,又具有学术意义 - 并且还排除了我的程序中可能导致此副作用的任何潜在严重问题。
标签: android algorithm random lua coronasdk