简体   繁体   English

您如何按照最小到大多数非零元素的顺序迭代排列?

[英]How do you iterate through permutations in order of least to most non-zero elements?

I am trying to write a C# function that, given an argument like new int[] { 2, 3, 2 } which specifies the upper bound + 1 for each element, would return the following (via IEnumberable<int[]> ): 我正在尝试编写一个C#函数,给定像new int[] { 2, 3, 2 } IEnumberable<int[]> new int[] { 2, 3, 2 }这样的参数,它为每个元素指定上限+ 1,将返回以下内容(通过IEnumberable<int[]> ):

0 0 0
0 0 1
0 1 0
0 2 0
1 0 0
0 1 1
0 2 1
1 0 1
1 1 0
1 2 0
1 1 1
1 2 1

Note that the order is important: all the permutations with 0 non-zero elements, followed by all those with 1 non-zero elements, etc. Within one of those groups the order doesn't matter. 请注意,顺序很重要:所有具有0个非零元素的排列,然后是具有1个非零元素的所有排列,依此类推。在这些组之一内,顺序无关紧要。

I realize that these may not technically be permutations, but it's the closest term that I know of. 我意识到这些从技术上讲可能不是排列,但这是我所知道的最接近的术语。 Also I realize that one way would be to return all the permutations in some order and then sort them according to a function that counts how many non-zero elements there are, but I am hoping for something more elegant and efficient. 我也意识到一种方法是按某种顺序返回所有排列,然后根据一个计算存在多少非零元素的函数对它们进行排序,但是我希望有一种更优雅,更有效的方法。

I apologize if this code has syntax errors (not in a position to test) but hopefully you get the idea. 如果该代码存在语法错误(无法测试),我深表歉意,但希望您能理解。

IEnumerable<int[]> Permutations(int[] upperBounds) {
    int[] c = new int[upperBounds.Length] {};

    while(true) {
        int i = c.Length - 1;

        while(i >= 0 && c[i] == upperBounds[i]) {
            c[i] = 0;
            i--;
        }

        if(i == -1) break;

        c[i]++;

        yield return (int[]) c.Clone();
    }
}

It gets even better if you use a callback and keep the same array reference, but you asked for an IEnumerable . 如果您使用回调并保留相同的数组引用,但要求IEnumerable会更好。 If not using Clone is possible, by all means, please use it - it will be much more efficient. 如果绝对不能使用Clone ,请使用它-它将大大提高效率。

I wanted an answer that doesn't calculate everything first and then sort, while still only going through things the minimal amount of times. 我想要一个不会先计算所有内容然后进行排序的答案,而仍然只经历最短的时间。 Here's what I've got. 这就是我所拥有的。 Note that externally modifying the int[] could screw up the results (alternately, could return a new int[] ). 请注意,在外部修改int[]可能会使结果搞砸(或者,可能返回new int[] )。

The first method tells the helper method how many 0's it wants in the output. 第一种方法告诉辅助方法在输出中需要多少个0。 The helper then calculates the results, stopping if it can't fill in enough 0's or if it runs through all the data. 然后,助手将计算结果,如果无法填写足够的0或遍历所有数据,则停止。

static IEnumerable<int[]> Permutation(int[] bounds)
{
  for(int num0s = bounds.Length; num0s >= 0; --num0s)
  {
    foreach(int[] ret in PermHelper(num0s, 0, bounds, new int[bounds.Length]))
      yield return ret;
  }
}

static IEnumerable<int[]> PermHelper(int num0s, int index, int[] bounds, int[] result)
{
  //Last index.
  if(index == bounds.Length - 1)
  {
    if(num0s > 0)
    {
      result[index] = 0;
      yield return result;
    }
    else
    {
      for(int i = 1; i < bounds[index]; ++i)
      {
        result[index] = i;
        yield return result;
      }
    }
  }
  //Others.
  else
  {
    //still need more 0s.
    if(num0s > 0)
    {
      result[index] = 0;
      foreach(int[] perm in PermHelper(num0s - 1, index + 1, bounds, result))
        yield return perm;
    }
    //Make sure there are enough 0s left if this one isn't a 0.
    if(num0s < bounds.Length - index)
    {
      for(int i = 1; i < bounds[index]; ++i)
      {
        result[index] = i;
        foreach(int[] perm in PermHelper(num0s, index + 1, bounds, result))
          yield return perm;
      }
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM