我似乎不知道这个堆排列算法的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#Implementation
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();
}
答案 0 :(得分:3)
找我,你对swap函数的意义,如你所定义的,期望得到交换值的数组和索引。
所以而不是:
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);在Swap之后调用就像你现在一样打印出来。
答案 1 :(得分: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;
}
答案 2 :(得分:2)
正如任何人的信息......
通过做一些调整,你可以获得更好的表现:
作为参考,我包括了我的实现。
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");
}
}
}