与伪随机相比,我如何产生“令人愉快”的随机?


26

我正在制作一款游戏,该游戏依次展示了许多不同类型的难题。我为每个谜题选择一个伪随机数。对于每个难题,都有很多变化。我选择了另一个伪随机数的变体。等等。

问题是,尽管这会产生接近真实的随机性,但这并不是玩家真正想要的。玩家通常希望自己认为是什么,并且将其标识为随机的,但前提是它不倾向于重复拼图。因此,并不是真正随机的。只是变幻莫测。

考虑一下,我可以想像出一些拙劣的方法。例如,在选择新选项时,暂时从可能性集中消除最近的N个选择。或为每个选择分配相等的概率,将选择时的选择概率降低为零,然后随着每个选择缓慢地增加所有概率。

我认为有一个确定的方法可以执行此操作,但是我只是不知道术语,所以我找不到它。有人知道吗 还是有人以令人愉悦的方式解决了这个问题?


4
《 AI游戏编程智慧2》一书中有一个关于过滤随机性的章节,据我回忆,这正好是您要寻找的内容。不过,目前我还没有,所以我无法真正给您完整的答案。
安东

尝试澄清一下:当您说“不重复拼图”时,您只是不想让两个相同类型的拼图彼此相邻吗?因此,换句话说,如果您只是选择了一个数独,就不要再提供另一个数独难题了,但是如果是数独#19,那么接下来就可以提供Picross#19(换句话说,变体数无关紧要) ?
史蒂文·斯塔德尼基


1
好的,我的AI Game Programming Wisdom 2副本刚到。我阅读了有关过滤随机性的章节,并查看了源代码。这可能是最好的方法。它允许我只使用随机数,然后过滤数字,以免发生意外的模式。似乎比洗牌袋更能防弹。
希尔顿坎贝尔2012年

1
另一个更新...对于我的特定应用程序,过滤随机性并没有完全做到这一点。我真的希望玩家在重复之前先玩完所有类型和子类型,因此我带了一个洗牌袋。
希尔顿坎贝尔

Answers:


25

如果您的拼图数量有限,则可以:

  • 建立一个谜题列表,所有谜题或随机选择;
  • 随机播放此列表(例如,参见Knuth随机播放);
  • 让您的播放器播放此列表;
  • 当列表为空时,从新列表开始。

编辑

我不知道这一点,但是浏览SE使我意识到它实际上被称为“洗牌袋”。一些更多的信息在这里这里那里

编辑2

经典的Knuth Shuffle就是这样:

To shuffle an array a of n elements (indices 0..n-1):
    for i from n  1 down to 1 do
        j  random integer with 0  j  i
        exchange a[j] and a[i]

史蒂文·斯塔德尼基(Steven Stadnicki)在其评论中正确地指出,这种事情并不能阻止改组后的重复。考虑到这一点的一种方法是为最后一项添加特殊情况:

To reshuffle an array a of n elements and prevent repetitions (indices 0..n-1):
    return if n <= 2

    // Classic Knuth Shuffle for all items *except* the last one
    for i from n  2 down to 1 do
        j  random integer with 0  j  i
        exchange a[j] and a[i]

    // Special case for the last item
    // Exchange it with an item which is *not* the first one
    r  random integer with 1  r  n - 1
    exchange a[r] and a[n - 1]

1
这可以奏效,但是如果您在每次播放后都重新洗改而不是从与您结束的相同项目开始,则必须格外小心。
史蒂文·斯塔德尼基

@史蒂文确实。此项目可以从新列表中排除。实际上,最好只构建一个包含一些随机项目的列表,然后仅构建包含剩余项目的下一个列表。因此,如果您有100个项目,则构建一个10的随机列表。完成此列表后,用90个以前未选择的10个项目构建下一个。
洛朗·库维杜

+1。增加了对该技术“更有趣”的支持:例如,这就是俄罗斯方块如何产生“随机”碎片的方式。将每个片段中的一个片段进行随机组合和遍历,避免了真正的随机性不可避免地会产生的重复序列。
KutuluMike 2012年

1
@Hilton我不喜欢这种“ while循环,直到随机给出我想要的东西为止”的方法……这不太可能造成任何麻烦。但是,我始终觉得这是对随机无限循环或性能下降的呼吁-难以调试。从新列表中排除上一个列表的最后一项,您只能随机播放一次,以获得相同的结果。
Laurent

1
您说得对,我也有相同的保留意见。现在,我将其随机洗一次,而不是排除先前的最后一项,然后如果先前的最后一项现在是第一项,那么我将其与其他一些项目随机交换。
希尔顿坎贝尔

2

lorancou方法的一种变体:对于每种拼图类型,都保留一系列(改组后的)拼图数字;然后每当您遇到这种类型的难题时,请从列表中获取下一个数字。例如,假设您有Sudoku,Picross和Kenken拼图,每个拼图都有#1..6拼图。您将创建数字1..6的三个随机排列的数组,每个拼图类型一个:

  • 数独:[5、6、1、3、4、2]
  • Picross:[6、2、4、1、3、5]
  • 肯肯:[3、2、5、6、4、1]

现在,您将像lorancu所建议的那样洗净拼图类型。假设出现[Picross,Sudoku,Kenken]。然后,每次您遇到给定类型的难题时,请在其“随机播放列表”中使用下一个数字;总体来说,您的拼图演示将是[数独#5,Picross#6,肯肯#3,数独#6,Picross#2,肯肯#2,...]

如果您不想在整个循环中每次都将拼图放在相同的整体顺序中,那么我认为您的“随机选择,忽略最后几个选择”选项是最好的。您也可以通过多种方法来提高效率。例如,假设您有20件事,而您想忽略最近选择的5件事。然后,与其随机选择一个数字1..20并“重新滚动”直到最后5个数字之外,不如选择一个数字1..15并逐步完成您的难题类型,只需跳过所有的难题类型即可。被选中(您可以通过保留一个可以容纳最近5个选中拼图的位数组来轻松实现此目的)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.