“间隔是空的”,Lua math.random不适用于大数?

时间:2013-11-24 04:47:50

标签: random lua

我不知道这是Lua本身的错误还是我做错了。我无论如何都找不到任何关于它的东西。我正在使用Lua for Windows(Lua 5.1.4):

>return math.random(0, 1000000000)
1251258

这将返回0到10000000000之间的随机整数,如预期的那样。这似乎适用于所有其他值。但是如果我添加一个0:

>return math.random(0, 10000000000)
stdin:1: bad argument #2 to 'random' (interval is empty)

任何高于此数字的数字都是相同的。

我试图弄清楚一个数字究竟要多高才能导致这种情况发现甚至更奇怪的事情:

>return math.random(0, 2147483647)
-75617745

如果值为2147483647则会给出负数。任何高于此值,它会抛出一个错误。任何低于它,它工作正常。

这是二进制的0b1111111111111111111111111111111,完全是31位二进制数字。我不确定这意味着什么。

2 个答案:

答案 0 :(得分:8)

这种意外行为(错误?)是由于math.random如何处理Lua 5.1中传递的输入参数。来自lmathlib.c

case 2: {  /* lower and upper limits */
  int l = luaL_checkint(L, 1);
  int u = luaL_checkint(L, 2);
  luaL_argcheck(L, l<=u, 2, "interval is empty");
  lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */
  break;
}

正如您在C中所知,标准int可以代表-2,147,483,6482,147,483,647的值。将+1添加到2,147,483,647,就像在用例中一样,会溢出并回绕赋予-2,147,483,648的值。最终结果是否定的,因为您将正数与负数相乘。

此外,由于溢出环绕,2,147,483,647以上的任何内容都会因luaL_argcheck而失败。

有几种方法可以解决这个问题:

  • 升级到Lua 5.2。那个人已经通过将输入参数视为lua_Number而修复了这个问题。
  • 切换到没有此整数溢出问题的LuaJIT。
  • 使用修复程序自行修补Lua 5.1源代码并重新编译。
  • 修改您的随机范围,使其不会溢出。

答案 1 :(得分:4)

如果你需要的范围大于随机函数支持的范围(32位有符号整数或2 ^ 31由于符号位,因为math.random是C级),但小于Lua“数字的范围“类型(基于What is the maximum value of a number in Lua?,2 ^ 52,甚至可能是2 ^ 53),您可以尝试生成两个随机数:将第一个缩放到所需的范围;添加第二个“填补空白”。例如,假设您想要0到2 ^ 36的范围。 math.random中最大的是2 ^ 31。所以你可以这样做:

-- 2^36 = 2^31 * 2^5 so
scale = 2^5
baseRand = scale * math.random(0, 2^31)
-- baseRand is now between 0 and 2^36 but there are gaps of 2^5 in the set 
-- of possible values; fill the gaps with second random number: 
fillGap = math.random(0, 2^5)
randNum = baseRand + fillGap

只要期望的范围小于Lua解释器的Lua数的最大值,这是可配置的编译时间参数,但是如果使用库存构建它是2 ^ 52,这是一个非常大的数字(尽管不是大到最大的长整数,2 ^ 63)。

另请注意,最大正N位整数是2 ^ N-1(不是2 ^ N),但上述技术可以应用于任何范围,例如scale = 10 ^ 6则randNum = 10 ^ 6 * math.random(0,10 ^ 8)+ math.random(0,10 ^ 6)。