【问题标题】:Shuffle an array without creating any runs随机排列数组而不创建任何运行
【发布时间】:2013-12-08 07:05:59
【问题描述】:

我有一个重复的字母数组:

AABCCD

我想把它们按伪随机顺序排列。很简单,只需使用 Fisher-Yates => done。但是对输出有限制——我不希望任何运行相同的字母。我希望在同一个角色再次出现之前至少出现两个其他角色。例如:

ACCABD

无效,因为有两个 C 相邻。

ABCACD

也是无效的,因为有两个 C 彼此相邻 (CAC),它们之间只有一个其他字符 (A),我需要至少两个其他字符。

这个简单示例的每个有效序列:

ABCADC ABCDAC ACBACD ACBADC ACBDAC ACBDCA ACDABC ACDACB ACDBAC ACDBCA ADCABC ADCBAC BACDAC BCADCA CABCAD CABCDA CABDAC CABDCA CADBAC CADBCA CADCAB CADCBA CBACDA CBADCA CDABCA CDACBA DACBAC DCABCA

我对这个小数组使用了蛮力方法,但我的实际问题是包含数百个元素的数组。我尝试使用带有一些抑制功能的 Fisher-Yates - 做普通的 Fisher-Yates,然后如果您不喜欢出现的角色,请多尝试 X 次以获得更好的角色。仅在大约 87% 的时间内生成有效序列,而且速度非常慢。想知道是否有更好的方法。显然这对所有数组都是不可能的。仅包含“AAB”的数组没有有效顺序,因此我想针对此类问题尝试使用“ABA”的最佳可用顺序。

【问题讨论】:

  • 你应该标记你使用的语言
  • @peeskillet 按要求标记伪代码
  • 如果您正在使用某种特定的语言,我会标记它以增加曝光度。我之所以这么说,是因为您的帖子已经发布了一段时间,只有 11 次浏览 :)
  • @peeskillet 我使用的实际语言不常见,因此无济于事。我看到 C# 是最流行的标签,meta.stackexchange.com/questions/23554/tag-trends-by-week,所以我将使用那个标签。感谢您的建议。
  • 很难 100% 的时间生成有效数组,因为您可能会在修改后的 Fisher-Yates 例程结束时发现您没有剩余的有效候选人。

标签: c# arrays shuffle


【解决方案1】:

这是一种修改后的 Fisher-Yates 方法。正如我所提到的,很难 100% 地生成一个有效的序列,因为你必须通过在序列的末尾只留下 AAA 来检查你没有陷入困境。

可以创建递归CanBeSorted 方法,它告诉您是否可以根据您的规则对序列进行排序。这将是您获得完整解决方案的基础,但是这个返回一个指示成功或失败的布尔值的函数应该是一个起点。

public static bool Shuffle(char[] array) 
{
    var random = new Random();
    var groups = array.ToDictionary(e => e, e => array.Count(v => v == e));
    char last = '\0';
    char lastButOne = '\0';
    for (int i = array.Length; i > 1; i--)
    {
        var candidates = groups.Keys.Where(c => groups[c] > 0)
            .Except(new[] { last, lastButOne }).ToList();
        if (!candidates.Any())
            return false;

        var @char = candidates[random.Next(candidates.Count)];
        var j = Array.IndexOf(array.Take(i).ToArray(), @char);
        // Swap.
        var tmp = array[j];
        array[j] = array[i - 1];
        array[i - 1] = tmp;
        lastButOne = last;
        last = @char;
        groups[@char] = groups[@char] - 1;
    }
    return true;
}

【讨论】:

    【解决方案2】:

    维护一个链接列表,用于跟踪字母及其在结果中的位置。

    得到随机数后,从输入中选择它对应的字符(与Fisher-Yates相同),但现在在列表中搜索它是否已经出现。

    如果没有,则将字母插入结果中,并在链接列表中插入其在结果中的位置。

    如果是,则检查它在结果中的位置(当您在结果中写入该字母时,您已存储在链接列表中)。现在将此位置与当前插入位置进行比较,如果 mod(currentlocation-previouslocation) 为 3 或更大,则可以在结果中插入该字母,否则不插入,如果不重新选择随机数。

    【讨论】:

    • 我不清楚这比“我尝试使用带有一些抑制的 Fisher-Yates - 做普通的 Fisher-Yates 然后如果你不喜欢出现的角色,多尝试 X 次以获得更好的结果。”这是问题的一部分。
    • 上述算法是确定性算法。您可以使用散列来存储和检索位置(如果您的文本中预定义了字符数,则效率更高)。每当出现错误的模式时,上述算法就不会从头开始。它一次性计算出伪模式。
    猜你喜欢
    • 2020-09-17
    • 2019-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-09
    • 1970-01-01
    相关资源
    最近更新 更多