你能在C#中优雅地编写排列函数吗?

时间:2009-04-16 13:53:50

标签: c# python algorithm

我非常喜欢这个6行解决方案,并试图在C#中复制它。基本上,它会置换数组的元素:

def permute(xs, pre=[]):
  if len(xs) == 0:
     yield pre
  for i, x in enumerate(xs):
     for y in permute(xs[:i] + xs[i+1:], pre + [x]):
        yield y

4 个答案:

答案 0 :(得分:12)

嗯,可能不是我写的方式,但是:

static IEnumerable<T[]> Permute<T>(this T[] xs, params T[] pre) {
    if (xs.Length == 0) yield return pre;
    for (int i = 0; i < xs.Length; i++) {
        foreach (T[] y in Permute(xs.Take(i).Union(xs.Skip(i+1)).ToArray(), pre.Union(new[] { xs[i] }).ToArray())) {
            yield return y;
        }
    }
}

重新评论;我对这个问题并不完全清楚。如果你的意思是“为什么这有用?” - 除此之外,还有一系列蛮力场景,您可能希望尝试不同的排列 - 例如,对于小型订购问题,例如旅行销售人员(不足以保证更复杂的解决方案),您可能想检查是否最好去{base,A,B,C,base},{base,A,C,B,base},{base,B,A,C,base}等。

如果您的意思是“我将如何使用此方法?” - 未经测试,但有点像:

int[] values = {1,2,3};
foreach(int[] perm in values.Permute()) {
   WriteArray(perm);
}

void WriteArray<T>(T[] values) {
    StringBuilder sb = new StringBuilder();
    foreach(T value in values) {
        sb.Append(value).Append(", ");
    }
    Console.WriteLine(sb);
}

如果你的意思是“它是如何运作的?” - 迭代器块(yield return)本身就是一个复杂的主题 - 但是Jon有一个免费的章节(6)in his book。其余代码非常类似于您原来的问题 - 只使用LINQ来提供+(对于数组)的道德等价物。

答案 1 :(得分:1)

C#有一个yield关键字,我认为它与你的python代码的工作方式非常相似,所以不应该太难以获得大多数直接翻译。

然而,这是一个递归解决方案,所以尽管它简洁,但它是次优的。我个人并不理解所涉及的所有数学,但是为了获得有效的数学排列,你想要使用factoradics。本文应该有所帮助:
http://msdn.microsoft.com/en-us/library/aa302371.aspx

[更新]:另一个答案提出了一个好点:如果你只是使用排列来进行洗牌,那么仍有更好的选择。具体来说,Knuth/Fisher-Yates shuffle

答案 2 :(得分:0)

虽然你不能在保持简洁的同时移植它,但你可以非常接近。

public static class IEnumerableExtensions
{
  public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> source)
  {
    if (source == null)
      throw new ArgumentNullException("source");

    return PermutationsImpl(source, new T[0]);
  }

  private static IEnumerable<IEnumerable<T>> PermutationsImpl<T>(IEnumerable<T> source, IEnumerable<T> prefix)
  {
    if (source.Count() == 0)
      yield return prefix;

    foreach (var x in source)
      foreach (var permutation in PermutationsImpl(source.Except(new T[] { x }),
                                                   prefix.Union(new T[] { x }))))
        yield return permutation;
  }
}

答案 3 :(得分:-6)

在一些评论之后我不得不承认这一点,但下面的代码可用于生成有限序列的随机排列。它是Fisher-Yates shuffle algorithm的变体。该示例使用int的序列,但您当然可以使用任何Enumerable<T>

var ints = Enumerable.Range(0, 51);
var shuffledInts = ints.OrderBy(a => Guid.NewGuid());

您按照随机值(在这种情况下为Guid)进行排序,这基本上会排列您的列表。 NewGuid是否是一个很好的随机性来源是值得商榷的,但它是一个优雅而紧凑的解决方案(虽然问题实际上是另一个问题)。

取自Jeff Atwood (Coding Horror)