【问题标题】:Implementation of heaps algorithm堆算法的实现
【发布时间】:2015-10-24 01:55:45
【问题描述】:

我似乎不知道我的堆置换算法的 C# 实现有什么问题。它没有给出输入数组的正确排列。有人可以帮帮我吗?

这是伪代码

procedure generate(n : integer, A : array of any):
    if n = 1 then
          output(A)
else
    for i := 0; i < n - 1; i += 1 do
        generate(n - 1, A)
        if n is even then
            swap(A[i], A[n-1])
        else
            swap(A[0], A[n-1])
        end if
    end for
    generate(n - 1, A)
end if

这是我的 c# 实现

static void Permute(int[] A, int n) {
    if (n == 1) {
        printArray(A);
    } else {
        for (int i = 0; i < n - 1; i++) {
            Permute(A, n - 1);

            if (n % 2 == 0) {
                A = Swap(A, A[i], A[n - 1]);
                printArray(A);
            } else {
                A = Swap(A, A[0], A[n - 1]);
                printArray(A);
            }

        }

        Permute(A, n - 1);
    }
}


static int[] Swap(int[] A, int x, int y) {
    int temp;
    temp = A[x];
    A[x] = A[y];
    A[y] = temp;

    return A;
}

static void printArray(int[] A) {
    foreach(var x in A) {
        Console.Write(x);

    }
    Console.WriteLine();
}

【问题讨论】:

    标签: c# algorithm permutation heaps-algorithm


    【解决方案1】:

    我看,你定义的交换函数的意思是希望得到交换值的数组和索引。

    所以而不是:

    Swap(A, A[i], A[n - 1]);
    Swap(A, A[0], A[n - 1]);
    

    应该是:

    Swap(A, i, n - 1);
    Swap(A, 0, n - 1);
    

    顺便说一句,如果你有这个算法用于除int[] 之外的任何其他类型的数组,你可能会在交换调用中得到编译错误。由于您的数组元素与数组索引类型的类型相同,因此您没有编译错误:int

    还有一件事,即使它不是问题,也没有必要从 Swap 函数返回数组。 Swap 的第一个参数,数组是通过引用传递的,因此 Swap 函数在与调用函数相同的数组实例上工作,而不是在其副本上工作。因此,从 Swap 中删除不必要的返回后, printArray(A);在交换之后立即调用将打印与您现在相同的内容。

    【讨论】:

    • 谢谢,它起作用了...关于交换函数,我故意为整数做这件事,因为我要解决的问题只需要整数。下次会使用'var'关键字
    • 对您有帮助太好了!我认为您误解了“var”关键字的含义。它实际上是一种定义具体类型的懒惰方式,编译器可以从变量的初始化中推断出来。看这里:msdn.microsoft.com/en-us/library/bb383973.aspx
    • 嗯....所以这行不通? 'code' 静态 var[] Swap(var[] A, var x, var y) { var temp;温度 = A[x]; A[x] = A[y]; A[y] = 温度;返回一个; }
    • 它不会编译。看起来您的下一步是使用“泛型”进行实施。请检查下面@Backs的答案
    【解决方案2】:

    试试this:

    public static IEnumerable<IEnumerable<T>> Permute<T>(this IList<T> v)
    {
        ICollection<IList<T>> result = new List<IList<T>>();
    
        Permute(v, v.Count, result);
    
        return result;
    }
    
    private static void Permute<T>(IList<T> v, int n, ICollection<IList<T>> result)
    {
        if (n == 1)
        {
            result.Add(new List<T>(v));
        }
        else
        {
            for (var i = 0; i < n; i++)
            {
                Permute(v, n - 1, result);
                Swap(v, n % 2 == 1 ? 0 : i, n - 1);
            }
        }
    }
    
    private static void Swap<T>(IList<T> v, int i, int j)
    {
        var t = v[i];
        v[i] = v[j];
        v[j] = t;
    }
    

    【讨论】:

      【解决方案3】:

      作为任何人的信息......

      您可以通过一些调整来获得更好的性能:

      • 将递归转换为迭代(内存更少,效率更高)
      • 您的交换函数可以内联,您只能通过引用接收 2 个参数
      • 模数的计算成本可能很高,您可以改为比较位 1
      • 您可以使其通用,这不会影响性能,但会变得更加灵活
      • 为了提高灵活性,您可以将 func 作为参数传递给您的方法。

      作为参考,我包括了我的实现。

      using System;
      using System.Collections.Generic;
      using System.Diagnostics;
      using System.Linq;
      using System.Runtime.CompilerServices;
      
      namespace WpfPermutations
      {
          /// <summary>
          /// EO: 2016-04-14
          /// Generator of all permutations of an array of anything.
          /// Base on Heap's Algorithm. See: https://en.wikipedia.org/wiki/Heap%27s_algorithm#cite_note-3
          /// </summary>
          public static class Permutations
          {
              /// <summary>
              /// Heap's algorithm to find all pmermutations. Non recursive, more efficient.
              /// </summary>
              /// <param name="items">Items to permute in each possible ways</param>
              /// <param name="funcExecuteAndTellIfShouldStop"></param>
              /// <returns>Return true if cancelled</returns> 
              public static bool ForAllPermutation<T>(T[] items, Func<T[], bool> funcExecuteAndTellIfShouldStop)
              {
                  int countOfItem = items.Length;
      
                  if (countOfItem <= 1)
                  {
                      return funcExecuteAndTellIfShouldStop(items);
                  }
      
                  var indexes = new int[countOfItem];
                  for (int i = 0; i < countOfItem; i++)
                  {
                      indexes[i] = 0;
                  }
      
                  if (funcExecuteAndTellIfShouldStop(items))
                  {
                      return true;
                  }
      
                  for (int i = 1; i < countOfItem;)
                  {
                      if (indexes[i] < i)
                      { // On the web there is an implementation with a multiplication which should be less efficient.
                          if ((i & 1) == 1) // if (i % 2 == 1)  ... more efficient ??? At least the same.
                          {
                              Swap(ref items[i], ref items[indexes[i]]);
                          }
                          else
                          {
                              Swap(ref items[i], ref items[0]);
                          }
      
                          if (funcExecuteAndTellIfShouldStop(items))
                          {
                              return true;
                          }
      
                          indexes[i]++;
                          i = 1;
                      }
                      else
                      {
                          indexes[i++] = 0;
                      }
                  }
      
                  return false;
              }
      
              /// <summary>
              /// This function is to show a linq way but is far less efficient
              /// From: StackOverflow user: Pengyang : http://stackoverflow.com/questions/756055/listing-all-permutations-of-a-string-integer
              /// </summary>
              /// <typeparam name="T"></typeparam>
              /// <param name="list"></param>
              /// <param name="length"></param>
              /// <returns></returns>
              static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
              {
                  if (length == 1) return list.Select(t => new T[] { t });
      
                  return GetPermutations(list, length - 1)
                      .SelectMany(t => list.Where(e => !t.Contains(e)),
                          (t1, t2) => t1.Concat(new T[] { t2 }));
              }
      
              /// <summary>
              /// Swap 2 elements of same type
              /// </summary>
              /// <typeparam name="T"></typeparam>
              /// <param name="a"></param>
              /// <param name="b"></param>
              [MethodImpl(MethodImplOptions.AggressiveInlining)]
              static void Swap<T>(ref T a, ref T b)
              {
                  T temp = a;
                  a = b;
                  b = temp;
              }
      
              /// <summary>
              /// Func to show how to call. It does a little test for an array of 4 items.
              /// </summary>
              public static void Test()
              {
                  ForAllPermutation("123".ToCharArray(), (vals) =>
                  {
                      Console.WriteLine(String.Join("", vals));
                      return false;
                  });
      
                  int[] values = new int[] { 0, 1, 2, 4 };
      
                  Console.WriteLine("Ouellet heap's algorithm implementation");
                  ForAllPermutation(values, (vals) =>
                  {
                      Console.WriteLine(String.Join("", vals));
                      return false;
                  });
      
                  Console.WriteLine("Linq algorithm");
                  foreach (var v in GetPermutations(values, values.Length))
                  {
                      Console.WriteLine(String.Join("", v));
                  }
      
                  // Performance Heap's against Linq version : huge differences
                  int count = 0;
      
                  values = new int[10];
                  for (int n = 0; n < values.Length; n++)
                  {
                      values[n] = n;
                  }
      
                  Stopwatch stopWatch = new Stopwatch();
      
                  ForAllPermutation(values, (vals) =>
                  {
                      foreach (var v in vals)
                      {
                          count++;
                      }
                      return false;
                  });
      
                  stopWatch.Stop();
                  Console.WriteLine($"Ouellet heap's algorithm implementation {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
      
                  count = 0;
                  stopWatch.Reset();
                  stopWatch.Start();
      
                  foreach (var vals in GetPermutations(values, values.Length))
                  {
                      foreach (var v in vals)
                      {
                          count++;
                      }
                  }
      
                  stopWatch.Stop();
                  Console.WriteLine($"Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-05-03
        • 2015-03-17
        • 2019-07-31
        • 2020-07-13
        • 2016-04-16
        • 2018-02-25
        • 2015-09-28
        • 2012-11-25
        • 2018-05-16
        相关资源
        最近更新 更多