【问题标题】:Generating random fixed length permutations of a string生成字符串的随机固定长度排列
【发布时间】:2008-12-14 19:07:18
【问题描述】:

假设我的字母表包含 X 个字母,而我的语言只支持 Y 个字母单词(当然是 Y

例如 字母=a,b,c,d,e,f,g Y=3

所以这些话是: 啊啊啊 aab aac 阿坝 .. bbb ccc .. (以上要随机生成)

最简单的方法是生成单词,然后随机化列表。我不想那样做。我想以随机顺序生成单词。

rondom(n)=letter[x].random(n-1) 将不起作用,因为这样您将拥有一个以 letter[x].. 开头的单词列表,这将使列表不那么随机。

感谢任何代码/伪代码。

【问题讨论】:

  • 您能告诉我们空间要求是什么吗?你有理由相信问题可以使用小于 X 的 Y 次方空间来解决(并且仍然保证终止)?
  • 您需要更具体地说明您需要它的“随机程度”,以及您是否真的需要整个列表(所有 X^Y 条目)的排列。

标签: algorithm random


【解决方案1】:

正如其他答案所暗示的那样,有两种主要方法:1) 跟踪您已经生成的内容(此类别中的建议解决方案可能永远不会终止),或 2) 跟踪尚未生成的排列(其中意味着必须预先生成排列,这在要求中是明确不允许的)。这是另一种保证终止且不需要预生成的解决方案,但可能无法满足您的随机化要求(此时还很模糊)。

一般概述:生成一棵树以跟踪生成的内容或剩余的内容。通过遍历树中的随机链接来“选择”新的排列,在生成该排列后在叶子处修剪树以防止再次生成它。

如果没有白板来绘制图表,我希望这个描述足以描述我的意思:创建一个“节点”,它与字母表中的每个字母的其他节点都有链接。这可以使用字母到节点的通用映射来实现,或者如果您的字母是固定的,您可以创建特定的引用。该节点表示字母表中的可用字母,接下来可以“生成”以生成排列。通过访问根节点开始生成排列,从该节点的可用字母中选择一个随机字母,然后遍历该引用到下一个节点。每次遍历都会为排列生成一个字母。当到达叶子时(即完全构造了一个排列),您将回溯树以查看父节点是否还有任何可用的排列;如果没有,则可以修剪父节点。

作为一个实现细节,节点可以存储在该点不能生产的字母集或在该点仍然可以生产的字母集。为了可能减少存储需求,您还可以允许节点存储 either 并带有一个标志,指示它正在执行的操作,以便当节点允许超过一半的字母表时,它存储到目前为止产生的字母,并且当可用的字母不到一半时,切换到使用剩余的字母。

使用这样的树结构限制了无需预先生成所有组合就可以生成的内容,因为您不需要预先构建整个树(可以在生成排列时构建它)并且可以保证由于清除了节点而完成(即,当这是未产生的排列的允许组合时,您只遍历到节点的链接)。

然而,我认为这种技术的随机化有点奇怪,而且我认为在任何给定时间生成每种组合的可能性并不相同,尽管我还没有真正考虑过这一点。还可能值得注意的是,即使不一定要预先生成完整的树,所涉及的开销也可能足够,因此您最好预先生成所有排列。

【讨论】:

  • 我喜欢这个答案,但我认为你是对的,它不会给你一个均匀的随机分布。为了获得统一的概率,我认为您需要跟踪节点的每个分支上有多少剩余叶子可用。然后使用这些计数来衡量您从该节点中随机选择的分支。
【解决方案2】:

我认为您可以通过根据您拥有的字母表(在 c# 中)生成一个随机字符数组来做一些非常简单的事情:

        char[] alphabet = {'a', 'b', 'c', 'd'};
        int wordLength = 3;

        Random rand = new Random();

        for (int i = 0; i < 5; i++)
        {
            char[] word = new char[wordLength];
            for (int j = 0; j < wordLength; j++)
            {
                word[j] = alphabet[rand.Next(alphabet.Length)];
            }
            Console.WriteLine(new string(word));
        }

显然这可能会产生重复,但如果需要,您可以将结果存储在 hashmap 或其他东西中以检查重复。

【讨论】:

  • 我想生成所有 XY 条目。如果我使用 Zach 的答案,则不能保证它会终止。如果我想使用 hashmap.. 我也可以将所有内容转储到 hashmap 并打印出来,因为 hashmap 无论如何都会为我随机化条目。
  • OK 误解了对不起 - 看起来你必须采用蛮力方法并生成所有可能的组合并随机化我想。也许一个中间的解决方案是将它全部放入一个文件并从那里读取行!
【解决方案3】:

所以我认为你想要的是使用尽可能少的内存产生集合的排列。

首先,它不能在没有内存的情况下完成。对于您的第一个字符串,您需要一个可以产生任何具有相同可能性的字符串的函数。假设该函数称为 nextString()。如果您再次调用 nextString() 而不更改状态中的任何内容,当然它将再次能够生成任何字符串。

所以你需要存储一些东西。问题是,您需要存储什么,需要多少空间?

字符串可以看作是数字 0 - X^Y。 (aaa=0, aab=1,aac=2...aba=X...) 因此,为了尽可能高效地存储单个字符串,您需要 lg(X^Y) 位。假设 X = 16 且 Y = 2。然后你需要 1 个字节的存储空间来唯一地指定一个字符串。

当然,最简单的算法是在生成每个字符串时对其进行标记,这需要 X^Y 位,在我的示例中是 256 位(32 字节)。这就是你说你不想做的事。您可以使用这个问题中讨论的随机算法:Creating a random ordered list from an ordered list(在通过随机算法生成字符串时,您不需要存储字符串,但您仍然需要标记它们)。

好的,现在的问题是,我们能做得更好吗?我们总共需要存储多少?

好吧,在第一次调用时,我们不需要任何存储空间。在第二次调用时,我们需要知道之前生产的是哪一个。在最后一个调用中,我们只需要知道最后一个是哪一个。所以最坏的情况是当我们进行到一半时。当我们进行到一半时,已经产生了 128 个字符串,还有 128 个要处理。我们需要知道还有哪些需要生产。假设这个过程是真正随机的,任何分裂都是可能的。有(256 种选择 128)种可能性。为了能够存储这些中的任何一个,我们需要 lg(256 选择 128) 位,根据谷歌计算器是 251.67。因此,如果您真的很聪明,您可以将信息压缩到比简单算法少 4 位。可能不值得。

如果您只是希望它看起来随机且存储空间很小,请参阅以下问题:Looking for an algorithm to spit out a sequence of numbers in a (pseudo) random order

【讨论】:

    猜你喜欢
    • 2015-09-13
    • 2021-03-15
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    • 2019-01-05
    • 2018-01-25
    • 2016-04-01
    • 2020-03-11
    相关资源
    最近更新 更多