简体   繁体   中英

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[]> ):

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.

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 . If not using Clone is possible, by all means, please use it - it will be much more efficient.

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[] ).

The first method tells the helper method how many 0's it wants in the output. The helper then calculates the results, stopping if it can't fill in enough 0's or if it runs through all the data.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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