升序列表排列

时间:2012-04-04 20:10:55

标签: c# algorithm list recursion permutation

我正在尝试获取列表的所有预定义长度排列,仅按升序排列。

For example, take the set:  "ABCDE"
I'd like the returning result to be:
ABC, ABD, ABE, ACD, ACE, ADE, BCD, BCE, BDE, CDE

换句话说,“B”永远不会出现在“A”(升序)之前,但我想要满足这一要求中的每一个变化。

我不想使用LINQ,我想找出实现这个的最快方法(速度是这个应用程序中的一个因素)。

到目前为止,我有一系列字符列表:

List<List<char>> Combinations;

其中内部“List”将是“ABC”(每个字母为char)的组合,外部列表将是所有组合的列表。

每个结果集的长度(上例中为3)需要是动态的,所以我认为我需要某种递归...我只是无法弄清楚如何实现它。 / p>

非常感谢任何帮助!

修改

到目前为止,这就是我所拥有的(我觉得我已经越来越近了......我真的无法让它真正构建最终列表(联盟不起作用 - 我是否正确使用它?) :

    private List<List<char>> AscendingPermute(List<char> InMovements, int Combinations)
    {
        List<List<char>> Ret = new List<List<char>>();

        for(int i = 0; i <= InMovements.Count - Combinations; i++)
        {
            if(Combinations <= 1){
                Ret.Add(new List<char>() {InMovements[i] });
                return Ret;
            }
            else
            {
                Ret.Union(AscendingPermute(InMovements.GetRange(1, InMovements.Count - 1), Combinations - 1));
            }
        }

        return Ret;
    }

我是否在正确的轨道上?我错过了什么?

谢谢!

5 个答案:

答案 0 :(得分:5)

所以你想要一组n个元素中的所有可能的k元素,并且你想要按升序排列每个k元素列表?

看看这里:Algorithm to return all combinations of k elements from n

答案 1 :(得分:3)

认为这就是你要找的东西,虽然我对速度并不乐观:

public static IEnumerable<string> GetPermutations(string letters, int max = 3, int curr = 0)
{
  if (curr < max - 1)
  {
    for (int a = 0; a < letters.Length; a++)
    {
      string firstHalf = letters.Substring(a,1);
      string subset = letters.Substring(a+1);
      foreach (string secondHalf in GetPermutations(subset, max, curr + 1))
      {
        //Console.Write("1st: {0}, 2nd: {1}; set: {2}", firstHalf, secondHalf, subset);
        yield return firstHalf + secondHalf;
      }
    }
  }
  else
    yield return String.Empty;
}

示例电话:

foreach (var result in GetPermutations('ABCDE', 3)){
  Console.WriteLine(result);
}

结果:

ABC
ABD
ABE
ACD
ACE
ADE
BCD
BCE
BDE
CDE
Press any key to continue...

答案 2 :(得分:2)

无需递归。

List<string> sortedResult = Perm("ABCDE",3);

static int BitCount(int n)
{
    int test = n,count = 0;

    while (test != 0)
    {
        if ((test & 1) == 1) count++;
        test >>= 1;
    }
    return count;
}


static List<string> Perm(string input,int M)
{
    var chars = input.ToCharArray();
    int N = chars.Length;
    List<List<char>> result = new List<List<char>>();

    for (int i = 0; i < Math.Pow(2, N); i++)
    {
        if (BitCount(i) == M)
        {
            List<char> line = new List<char>();
            for (int j = 0; j < N; j++)
            {
                if (((i >> j) & 1) == 1)
                {
                    line.Add(chars[j]);
                }
            }
            result.Add(line);
        }
    }

    return result.Select(l => String.Join("", l)).OrderBy(s => s).ToList();
}

答案 3 :(得分:0)

您正在寻找一个递归函数,它将计算:给定字母表中的第一个字母(按升序排序)与升序排列连接,其中字母的其余部分少一个字母,加上剩余部分的上升排列相同的字母数。

为了澄清,您的例子是

asc_perm("ABCDE",3):="A"+asc_perm("BCDE",2) | asc_perm("BCDE",3)

要迭代编码,您可以使用nn>m => idx_{n} > idx_{m}的约束将0 < n,m <= count(alphabet)索引放入字母表中,并枚举所有可能的索引。它有点像一些额外的条件。要使用这些索引进行计数,请从1, 2, 3, 4, ...n开始。从递增最后一个计数器开始,直到达到字母长度。如果是,请找到上一个索引,将其递增1,并将其后面的每个索引设置为1+idx_prev,只要索引不会超过您的计数。如果是,则使用上一个索引重复该过程,直到用完有效位置。

您的示例的简单说明就是:

  • 初始条件:{1,2,3}
  • 为所有有效职位运行最后一个索引:{1,2,4}{1,2,5}
  • 将之前的索引(2)增加到下一个并重置其余部分:{1,3,4}
  • 运行所有有效职位的最后一个索引:{1,3,5}
  • 将之前的索引(2)增加到下一个并重置其余部分:{1,4,5}
  • 为所有有效仓位运行最后一个索引:无可能的移动
  • 将之前的索引(2)增加到下一个并重置其余部分:没有可能的移动
  • 将之前的索引(1)增加到下一个并重置其余部分:{2,3,4}
  • 运行所有有效职位的最后一个索引:{2,3,5}
  • 将之前的索引(2)增加到下一个并重置其余部分:{2,4,5}
  • 为所有有效仓位运行最后一个索引:无可能的移动
  • 将之前的索引(2)增加到下一个并重置其余部分:没有可能的移动
  • 将之前的索引(1)增加到下一个并重置其余部分:{3,4,5}
  • 为所有有效仓位运行最后一个索引:无可能的移动
  • 将之前的索引(2)增加到下一个并重置其余部分:没有可能的移动
  • 将之前的索引(1)增加到下一个并重置其余部分:无可能的移动
  • 终止

答案 4 :(得分:0)

这是一个符合你想要的递归方法:

    static IEnumerable<List<byte>> AscPerm(List<byte> inBytes, int combinations)
    {
        if (combinations == 1)
        {
            foreach (var b in inBytes)
            {
                yield return new List<byte> { b };
            }
        }
        else
        {
            for (int i = 0; i <= inBytes.Count - combinations; i++)
            {
                // Recurse down, passing last items of inBytes.
                var subPerms = AscPerm(inBytes.Skip(i +1).ToList(), combinations - 1);
                foreach (var subPerm in subPerms)
                {
                    List<byte> retBytes = new List<byte>{ inBytes[i] };
                    yield return retBytes.Concat(subPerm).ToList();
                }
            }
        }
    }

    static void Main(string[] args)
    {
        var retList = AscPerm(new List<byte> { 1, 2, 3, 4, 5, 6, 7 }, 3);
        foreach (var ret in retList)
        {
            foreach (var r in ret)
            {
                Console.Write(r);
            }
            Console.WriteLine();
        }
        Console.ReadLine();
    }