简体   繁体   English

堆算法的实现

[英]Implementation of heaps algorithm

Please I don't seem to know what is wrong with this my C# implementation of the heaps permutation algorithm. 请我似乎不知道我的C#实现堆置换算法的问题是什么。 It does not give the correct permutation of an input array. 它不能给出输入数组的正确排列。 Can somebody help me out? 有人可以帮我吗?

Here is the pseudo-code 这是伪代码

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

this is my c# Implementation 这是我的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();
}

Looks for me, that your meaning about the swap function, as you defined, expects to get the array and indexes of the swapped values. 在我看来,您所定义的关于交换函数的含义希望获得交换值的数组和索引。

So instead of: 所以代替:

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

Should be: 应该:

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

By the way, if you had this algorithm for array of any other type than int[] you whould get compilation error in the swap call. 顺便说一句,如果您有此算法用于除int[]以外的任何其他类型的数组,则在swap调用中将出现编译错误。 You haven't have compilation error because of the coincidence that your array elements are of the same type as array index type: int 您还没有编译错误,因为巧合的是,你的数组元素都是同一类型的数组索引类型的: int

And another thing, even though its not an issue, it is not necessary to return the array, from Swap function. 另外,即使这不是问题,也不必从Swap函数返回数组。 The first argument of the Swap, the array is passed by reference, so the Swap function works on the same array instance as in the caller function and not on its copy. 交换的第一个参数是数组,是通过引用传递的,因此交换函数与调用方函数在相同的数组实例上工作,而不是在其副本上工作。 So after you remove the unnecessary return from Swap, the printArray(A); 因此,从Swap中删除了不必要的收益之后,printArray(A); called right after the Swap will print the same as you have now. 在“交换”之后立即调用,将打印与您现在的相同的内容。

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

Just as information for anybody... 就像任何人的信息...

You could achieve a really better performance by doing some adjustments: 通过进行一些调整,您可以实现更好的性能:

  • Convert recursion to iteration (less memory, better efficiency) 将递归转换为迭代(更少的内存,更高的效率)
  • Your swap function can be inlined and you could receive 2 parameters only by reference 可以内联交换函数,并且只能通过引用接收2个参数
  • Modulo could be expensive to calculate, you could compare bit 1 instead 模数的计算可能很昂贵,您可以改为比较第1位
  • You can make it generic which would not effect performance but will become more flexible 您可以使其通用,这不会影响性能,但会变得更加灵活
  • To also improve flexibility, you can pass a func as a parameter to your method. 为了提高灵活性,可以将func作为参数传递给方法。

Just as a reference, I included my implementation of it. 仅供参考,我包括了它的实现。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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