10 lua
在Lua中,通常会使用math.random&生成随机值和/或字符串math.randomseed,其中os.time包含math.randomseed.
然而,这种方法有一个主要的缺点; 返回的数量永远只是作为当前时间是随机的,和每一个随机数的间隔是1秒,这是如果一个人在很短的时间需要随机值的时间太长了.
:这个问题甚至被Lua的用户维基指出http://lua-users.org/wiki/MathLibraryTutorial,以及相应的RandomStringS receipe:http://lua-users.org/wiki/RandomStrings.
所以我坐下来写了一个不同的算法(如果它甚至可以称之为),它使用表的内存地址(错误)生成随机数:
math.randomseed(os.time())
function realrandom(maxlen)
local tbl = {}
local num = tonumber(string.sub(tostring(tbl), 8))
if maxlen ~= nil then
num = num % maxlen
end
return num
end
function string.random(length,pattern)
local length = length or 11
local pattern = pattern or '%a%d'
local rand = ""
local allchars = ""
for loop=0, 255 do
allchars = allchars .. string.char(loop)
end
local str=string.gsub(allchars, '[^'..pattern..']','')
while string.len(rand) ~= length do
local randidx = realrandom(string.len(str))
local randbyte = string.byte(str, randidx)
rand = rand .. string.char(randbyte)
end
return rand
end
Run Code Online (Sandbox Code Playgroud)
起初,一切似乎都是完全随机的,我确信它们......至少对于当前的程序而言.
所以我的问题是,这些数字是如何随机返回的realrandom?
或者是有一个更好的方式中超过一秒(这种意味着一个较短的时间间隔来产生随机数os.time,不应使用,如上面所explaind),而不依赖于外部库,和,如果可能,在一个完全跨平台方式?
编辑:
关于RNG播种的方式似乎存在重大误区; 在生产代码中,调用math.randomseed()仅发生一次,这只是一个错误选择的例子.
我所说的随机值只是每秒一次随机,很容易通过这个粘贴来证明:http://codepad.org/4cDsTpcD
不管我的编辑如何,这个问题都会被取消,我也取消了之前接受的答案 - 希望有一个更好的答案,即使只是更好的意见.我理解有关随机值/数字的问题之前已经讨论了很多次,但我没有找到可能与Lua相关的问题 - 请记住这一点!
每次调用随机时都不应该调用种子,你应该在程序初始化时调用它一次(除非你从某个地方获取种子,例如,复制一些以前的"随机"行为).
标准Lua随机生成器在统计意义上质量很差(事实上,它是标准C随机生成器),如果您关心它,请不要使用它.例如,使用lrandom模块(在LuaRocks中可用).
如果您需要更安全的随机,请/dev/random在Linux上阅读.(我认为Windows应该有相同的东西 - 但你可能需要用C编写代码才能使用它.)
依赖表指针值是个坏主意.例如,考虑使用Java中的备用Lua实现 - 没有人知道它们会返回什么.(此外,指针值可能是可预测的,并且在每次调用程序时,它们在某些情况下可能是相同的.)
如果你想要更精确的种子(并且只有当你每秒启动程序的频率超过一次时才会想要这个),你应该使用更好分辨率的计时器.例如,socket.gettime()来自LuaSocket.将它乘以某个值,因为math.randomseed它仅使用整数部分,并socket.gettime()以(浮点)秒返回时间.
require 'socket'
math.randomseed(socket.gettime() * 1e6)
for i = 1, 1e3 do
print(math.random())
end
Run Code Online (Sandbox Code Playgroud)关于你的问题第一部分的一些想法:
所以我的问题是,这些数字返回的随机性有多大
realrandom?
您的函数试图通过使用其默认实现的怪癖来发现表的地址tostring()。我不相信 返回的字符串tostring{}具有指定的格式,或者该字符串中包含的值具有任何记录的含义。实际上,它是从与特定表相关的内容的地址派生的,因此不同的表会转换为不同的字符串。然而,Lua 的下一个版本可以随意将其更改为任何方便的内容。更糟糕的是,它采用的格式将高度依赖于平台,因为它似乎使用%p格式说明符,sprintf()而该格式说明符仅被指定为指针的合理表示。
还有一个更大的问题。虽然进程中创建的第 n 个表的地址在您的平台上可能看起来是随机的,但 tt 可能根本不是随机的。或者它可能只有几位的变化。例如,在我的 win7 机器上只有少数位有所不同,而且不是很随机:
C:...>for /L %i in (1,1,20) do @ lua -e "print{}"
表:0042E5D8
表:0061E5D8
表:0024E5D8
表:0049E5D8
表:0042E5D8
表:0042E5D8
表:0042E5D8
表:0064E5D8
表:0042E5D8
表:002FE5D8
表:0042E5D8
表:0049E5D8
表:0042E5D8
表:0042E5D8
表:0042E5D8
表:0024E5D8
表:0042E5D8
表:0042E5D8
表:0061E5D8
表:0042E5D8
当然,其他平台会有所不同。我什至期望在某些平台上,第一个分配的表的地址是完全确定的,因此在程序的每次运行中都是相同的。
简而言之,过程映像中任意对象的地址并不是很好的随机性来源。
编辑:为了完整起见,我想添加一些昨晚想到的其他想法。
stocktostring()函数由基础库提供并由 function 实现luaB_tostring()。相关位是这个片段:
switch (lua_type(L, 1)) {
...
default:
lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1));
break;
Run Code Online (Sandbox Code Playgroud)
如果你真的调用这个函数,那么字符串的末尾将是一个地址,以标准Csprintf()格式表示%p,与特定的表密切相关。一项观察是,我见过几种不同的%p. Windows MSVCR80.DLL(当前版本的 Lua for Windows 使用的 C 库版本)使其等同于%08X. 我的 Ubuntu Karmic Koala 盒子似乎使其相当于显着%#x删除了前导零。如果您要解析字符串的该部分,那么您应该以一种更灵活的方式来应对 含义的变化%p。
另请注意,在库代码中执行此类操作可能会让您遇到一些意外。
首先,如果传递给的表tostring()有一个提供函数的元表__tostring(),那么该函数将被调用,并且上面引用的片段将永远不会被执行。在您的情况下,不会出现该问题,因为表具有单独的元表,并且您没有意外地将元表应用到本地表。
其次,当您的模块加载时,其他一些模块或用户提供的代码可能已经tostring()用其他东西替换了库存。如果替换是良性的(例如记忆包装器),那么它可能对编写的代码并不重要。然而,这将成为攻击源,并且完全超出您的模块的控制范围。如果目标是提高随机种子材料的某种安全性,那么我认为这不是一个好主意。
第三,您可能根本没有加载到现有的 Lua 解释器中,并且较大的应用程序(Lightroom、WoW、Wireshark 等)可能会选择用自己的实现替换基本库函数。对于 来说,这是一个不太可能出现的问题tostring(),但请注意,基础库print()是替代实现中替换或删除的频繁目标,并且有些模块(例如Lua Lanesprint )如果不是基础库中的实现,则会中断。