7

我知道关于排列列表的 SO 上有几个措辞相似的问题,但它们似乎并没有真正解决我正在寻找的问题。我知道有办法做到这一点,但我正在画一个空白。我有一个类似于这种格式的平面文件:

Col1|Col2|Col3|Col4|Col5|Col6
a|b,c,d|e|f|g,h|i
. . .

现在这是诀窍:我想创建这些行的所有可能排列的列表,其中行中以逗号分隔的列表表示可能的值。例如,我应该能够将IEnumerable<string>上述表示为这样的行:

IEnumerable<string> row = new string[] { "a", "b,c,d", "e", "f", "g,h", "i" };
IEnumerable<string> permutations = GetPermutations(row, delimiter: "/");

这应该生成以下字符串数据集合:

a/b/e/f/g/i
a/b/e/f/h/i
a/c/e/f/g/i
a/c/e/f/h/i
a/d/e/f/g/i
a/d/e/f/h/i

对我来说,这似乎可以优雅地融入递归方法,但显然我在星期一遇到了一个糟糕的情况,我无法完全思考如何处理它。一些帮助将不胜感激。应该是什么GetPermutations(IEnumerable<string>, string)样子?

4

4 回答 4

1

我不确定这是否是最优雅的方法,但它可能会让你开始。

private static IEnumerable<string> GetPermutations(IEnumerable<string> row, 
                                                    string delimiter = "|")
{
    var separator = new[] { ',' };
    var permutations = new List<string>();
    foreach (var cell in row)
    {
        var parts = cell.Split(separator);
        var perms = permutations.ToArray();
        permutations.Clear();
        foreach (var part in parts)
        {
            if (perms.Length == 0)
            {
                permutations.Add(part);
                continue;
            }
            foreach (var perm in perms)
            {
                permutations.Add(string.Concat(perm, delimiter, part));
            }
        }
    }
    return permutations;
}

当然,如果排列的顺序很重要,您可以.OrderBy()在末尾添加一个。

编辑:添加了一个替代品

您还可以通过在确定排列之前计算一些数字来构建字符串数组的列表。

private static IEnumerable<string> GetPermutations(IEnumerable<string> row, 
                                                    string delimiter = "|")
{
    var permutationGroups = row.Select(o => o.Split(new[] { ',' })).ToArray();
    var numberOfGroups = permutationGroups.Length;
    var numberOfPermutations = 
           permutationGroups.Aggregate(1, (current, pg) => current * pg.Length);
    var permutations = new List<string[]>(numberOfPermutations);

    for (var n = 0; n < numberOfPermutations; n++)
    {
        permutations.Add(new string[numberOfGroups]);
    }

    for (var position = 0; position < numberOfGroups; position++)
    {
        var permutationGroup = permutationGroups[position];
        var numberOfCharacters = permutationGroup.Length;
        var numberOfIterations = numberOfPermutations / numberOfCharacters;
        for (var c = 0; c < numberOfCharacters; c++)
        {
            var character = permutationGroup[c];
            for (var i = 0; i < numberOfIterations; i++)
            {
                var index = c + (i * numberOfCharacters);
                permutations[index][position] = character;
            }
        }
    }

    return permutations.Select(p => string.Join(delimiter, p));
} 
于 2013-02-18T22:20:19.450 回答
1

您可以使用的一种算法基本上就像计数:

  • 从每个列表中的第 0 项 (00000) 开始
  • 增加最后一个值(00001、00002 等)
  • 当您无法增加一个值时,将其重置并增加下一个值(00009、00010、00011 等)
  • 当你不能增加任何价值时,你就完了。

功能:

static IEnumerable<string> Permutations(
    string input,
    char separator1, char separator2,
    string delimiter)
{
    var enumerators = input.Split(separator1)
        .Select(s => s.Split(separator2).GetEnumerator()).ToArray();
    if (!enumerators.All(e => e.MoveNext())) yield break;

    while (true)
    {
        yield return String.Join(delimiter, enumerators.Select(e => e.Current));
        if (enumerators.Reverse().All(e => {
                bool finished = !e.MoveNext();
                if (finished)
                {
                    e.Reset();
                    e.MoveNext();
                }
                return finished;
            }))
            yield break;
    }
}

用法:

foreach (var perm in Permutations("a|b,c,d|e|f|g,h|i", '|', ',', "/"))
{
    Console.WriteLine(perm);
}
于 2013-02-19T09:42:42.943 回答
1

你让我“递归”。这是另一个建议:

private IEnumerable<string> GetPermutations(string[] row, string delimiter,
                                            int colIndex = 0, string[] currentPerm = null)
{
    //First-time initialization:
    if (currentPerm == null) { currentPerm = new string[row.Length]; }

    var values = row[colIndex].Split(',');
    foreach (var val in values)
    {
        //Update the current permutation with this column's next possible value..
        currentPerm[colIndex] = val;

        //..and find values for the remaining columns..
        if (colIndex < (row.Length - 1))
        {
            foreach (var perm in GetPermutations(row, delimiter, colIndex + 1, currentPerm))
            {
                yield return perm;
            }
        }
        //..unless we've reached the last column, in which case we create a complete string:
        else
        {
            yield return string.Join(delimiter, currentPerm);
        }
    }
}
于 2013-02-18T23:41:41.160 回答
0

我真的认为这将是一个很棒的递归函数,但我最终没有这样写。最终,这是我创建的代码:

public IEnumerable<string> GetPermutations(IEnumerable<string> possibleCombos, string delimiter)
{
    var permutations = new Dictionary<int, List<string>>();
    var comboArray = possibleCombos.ToArray();
    var splitCharArr = new char[] { ',' };

    permutations[0] = new List<string>();

    permutations[0].AddRange(
        possibleCombos
        .First()
        .Split(splitCharArr)
        .Where(x => !string.IsNullOrEmpty(x.Trim()))
        .Select(x => x.Trim()));

    for (int i = 1; i < comboArray.Length; i++)
    {
        permutations[i] = new List<string>();
        foreach (var permutation in permutations[i - 1])
        {
            permutations[i].AddRange(
                comboArray[i].Split(splitCharArr)
                .Where(x => !string.IsNullOrEmpty(x.Trim()))
                .Select(x => string.Format("{0}{1}{2}", permutation, delimiter, x.Trim()))
                );
        }
    }

    return permutations[permutations.Keys.Max()];
}

...我的测试条件为我提供了我期望的输出:

IEnumerable<string> row = new string[] { "a", "b,c,d", "e", "f", "g,h", "i" };
IEnumerable<string> permutations = GetPermutations(row, delimiter: "/");
foreach(var permutation in permutations)
{
    Debug.Print(permutation);
}

这产生了以下输出:

a/b/e/f/g/i
a/b/e/f/h/i
a/c/e/f/g/i
a/c/e/f/h/i
a/d/e/f/g/i
a/d/e/f/h/i

多亏了大家的建议,他们对整理我脑海中需要做的事情确实很有帮助。我已经赞成你所有的答案。

于 2013-02-19T15:09:43.123 回答