堆算法的实现

时间:2015-08-01 13:11:55

标签: c# algorithm permutation

我似乎不知道这个堆排列算法的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();
}

3 个答案:

答案 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)

正如任何人的信息......

通过做一些调整,你可以获得更好的表现:

  • 将递归转换为迭代(内存更少,效率更高)
  • 您的交换功能可以内联,您只能通过参考
  • 接收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");
        }
    }
}