2012-04-04 72 views
3

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

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; 

其中内“列表”将会像“ABC”(每个字母是一个字符的组合),以及外列表会所有组合的列表。

每个结果集(在上面的例子中是3)的长度需要是动态的,所以我想我需要某种递归......我只是不知道如何实现它。

任何帮助将不胜感激!

编辑

到目前为止,这是我有什么(我觉得我越来越近......我只是无法得到它的实际构建的最终名单(工会不工作 - 我使用它错误地):

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; 
    } 

我在正确的轨道上我缺少什么

感谢

+1

有可能性('2^N'确切)指数数量,你不会如果你真的想要所有这些,速度会变得太快。 – amit 2012-04-04 20:13:01

+3

你到目前为止有什么? – 48klocs 2012-04-04 20:14:02

+1

我认为有nCk的可能性(n是第一个列表的长度,k是结果中每个列表的长度),那不是2^n。 – zmbq 2012-04-04 20:14:44

回答

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... 
+0

想象一下,我会使用'String'而不是'List '来给你一些事情做。 ;-) – 2012-04-04 20:50:37

+0

你的例子要求3个字母的子集,但结果显示4个字母子集... – zmbq 2012-04-04 20:57:18

+1

@zmbq:是的,在支票上忘记了'-1'。接得好。 ;-) – 2012-04-04 20:58:55

5

所以,你希望所有的POSS???!在一组n个元素中包含了k个元素,并且您希望每个k元素列表按升序排列?

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

+0

我看到,在我的研究,它看起来像http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n/1898744#1898744会做什么我正在寻找 - 这只是关于使用大量内存的较大套件的评论,这让我很担心。因此,我正在寻找更多的定制(原始和快速希望)解决方案。 – Harry 2012-04-04 20:20:44

+1

你实际上应该在你的代码中用你的数据试试看看它是否使用太多。枚举工作的方式通常可以流式传输,这意味着尽可能少的内存在任何时间点。只要你不要。列出结果或类似的东西。 – Servy 2012-04-04 20:22:13

+0

+1好找。甚至附有源代码。 – vhallac 2012-04-04 20:48:36

0

你正在寻找一个递归函数,将计算:第一个字母在给定的字母用的剩余部分少了一个字母升序排列级联(按升序排序)字母表,加上相同字母数的余数的升序排列。

为了澄清,你的例子是

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

要反复代码时,你可以有n索引到你的字母与约束n>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)到下一个和重置其余:没有可能的行动
  • 终止
+0

或者,为了简化,它只是*组合*而不是*组合排序(内部)的*排列*。 – Servy 2012-04-04 20:33:56

+0

@Servy Yup。但正确命名并不总是让你编码。 :) – vhallac 2012-04-04 20:44:11

+0

确实如此,但是与编码解决方案的链接完全相同,如果您使用适当的术语,它在搜索中会更容易。 – Servy 2012-04-04 20:48:34

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(); 
} 
+0

好的角度。内存效率非常好,但您不想尝试使用大字母。 ;) – vhallac 2012-04-04 20:53:43

+0

@vhallac如果字母足够大,任何算法都会失败:) – 2012-04-04 20:55:00

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(); 
    }