在不创建任何运行的情况下随机播放阵列

时间:2013-12-08 07:05:59

标签: c# arrays shuffle

我有一系列重复的字母:

  

AABCCD

我想把它们变成伪随机顺序。简单吧,只使用Fisher-Yates =>完成。但是输出有限制 - 我不希望任何相同字母的运行。我希望在同一个角色重新出现之前至少出现两个其他角色。例如:

  

ACCABD

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

  

ABCACD

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

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

  

ABCADC ABCDAC ACBACD ACBADC ACBDAC ACBDCA ACDACC 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”的最佳订单。

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:0)

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

获取随机数后,从输入中选择相应的字符(与Fisher-Yates相同),但现在在列表中搜索是否已经发生。

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

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