Randomly select a key from a table in Lua
我想使用可能的项目列表在Lua中随机填充一个网格,定义如下:
1 2 3 4 5 6 7 8 | -- Items items = {} items.glass = {} items.glass.color = colors.blue items.brick = {} items.brick.color = colors.red items.grass = {} items.grass.color = colors.green |
因此,表的键是"玻璃","砖"和"草"。
如果无法通过数字索引寻址,如何随机选择这些键之一?
好吧,我有一个解决方法,但是我愿意接受任何更好的建议。
第一个解决方案包括拥有一个辅助表,该辅助表用作第一个表的索引:
1 | item_index = {"grass","brick","glass"} |
然后,我可以随机存储此表的键(
1 2 | local index = math.random(1,3) board[i][j] = item_index[index] |
之后,我可以获得原始列表的详细信息,如下所示:
1 | items[board[y][x]].color |
我已决定的第二种解决方案是将定义的元素作为数组元素添加到原始表中:
1 2 3 4 5 6 7 8 9 10 11 | -- Items items = {} items.glass = {} items.glass.color = colors.blue table.insert(items, items.glass) --- Add item as array item items.brick = {} items.brick.color = colors.red table.insert(items, items.brick) --- Add item as array item items.grass = {} items.grass.color = colors.green table.insert(items, items.grass) --- Add item as array item |
然后,我可以使用索引直接处理元素:
1 2 | local index = math.random(1,3) board[i][j] = items[index] |
而且可以直接检索它们,而无需其他查找:
1 | board[y][x].color |
上面的答案假设您知道所有键是什么,而这不是我今天早些时候能做的。我的解决方案:
1 2 3 4 5 6 7 8 9 10 11 | function table.randFrom( t ) local choice ="F" local n = 0 for i, o in pairs(t) do n = n + 1 if math.random() < (1/n) then choice = o end end return choice end |
说明:我们无法使用table.getn(t)来获取表的大小,因此我们会一直跟踪它。第一项将有1/1 = 1的机会被选中;第二个1/2 = 0.5,依此类推...
如果您扩展N个项目,则第N个项目将有1 / N的机会被选择。第一项将有1-(1/2)-(1/3)-(1/4)-...-(1 / N)机会不被替换(请记住,始终是第一次选择) 。该级数收敛到1-(N-1)/ N = 1 / N,这等于选择了最后一项的机会。
因此,数组中的每个项目都有相同的可能性被选择;它是均匀随机的。这也需要O(n)的时间运行,虽然不好,但是如果您不知道索引名,这是您可以做的最好的事情。
如果您的桌子不太大,可以随意休息一下。此方法假定您知道表中的条目数(如果表具有非数字键,则该值不等于#table值)。
因此,找到表的长度,然后在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | local items = {} .... items.grass.color = colors.green local numitems = 0 -- find the size of the table for k,v in pairs(items) do numitems = numitems + 1 end local randval = math.random(1, numitems) -- get a random point local randentry local count = 0 for k,v in pairs(items) do count = count + 1 if(count == randentry) then randentry = {key = k, val = v} break end end |
货物:您不必跟踪钥匙。它可以是任何表,您不需要维护它。
坏而丑陋的是:O(n)-两次线性传递,因此,如果您有大桌子,那根本不理想。
尽管您的第二种方法给出了简洁的语法,但我认为第一种方法更易于维护。我无法在这里进行测试,但我认为您可以同时获得两者的最佳效果,因此无法完成这项工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | local items = { glass = { color = colors.blue, }, brick = { color = colors.red, }, grass = { color = colors.green, }, } local item_index = {"grass","brick","glass"} local index = math.random(1,3) board[i][j] = items[item_index[index]] print('color:', board[i][j].color) |