Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating permutations of a set - efficiently and with distinction

I'm building on code from here. I'd like to generate all permutations of a set, for example (taken from the thread):

Collection: 1, 2, 3
Permutations: {1, 2, 3}
              {1, 3, 2}
              {2, 1, 3}
              {2, 3, 1}
              {3, 1, 2}
              {3, 2, 1}

There are enter image description here possible permutations for every set, but this is not what I'd like to achieve. Take, for consideration, following set:

enter image description here

This would yield enter image description here permutations, an extreme amout of enter image description here. This would take an extraordinary long time to compute, as each zero is being considered unique.

Rather than that, I'd like to only generate distinct permutations. If we do that, there are only

enter image description here

permutations remaining, as 18 items are identical (k).

Now, I could run the code from the mentioned thread and store the results in a HashSet, eliminating the duplicate permutations. However, that would be extremely inefficient. I'm looking for an algorithm to generate permutations with distinction directly.

like image 255
jacobz Avatar asked Oct 25 '15 11:10

jacobz


People also ask

What is a permutation algorithm?

This algorithm is based on swapping elements to generate the permutations. It produces every possible permutation of these elements exactly once. This method is a systematic algorithm, which at each step chooses a pair of elements to switch in order to generate new permutations.


1 Answers

With Swap algorithm to find permutations you can directly exclude the parts that produce duplicate permutations. This algorithm is available on internet and you can find more information about it.

private static void Main(string[] args)
{
    List<int> set = new List<int>
    {
        20, 4, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    var permutes = Permute(set);

    Console.WriteLine(permutes.Count); // outputs 58140
}

private static List<int[]> Permute(List<int> set)
{
    List<int[]> result = new List<int[]>(); 

    Action<int> permute = null;
    permute = start =>
    {
        if (start == set.Count)
        {
            result.Add(set.ToArray());
        }
        else
        {
            List<int> swaps = new List<int>();
            for (int i = start; i < set.Count; i++)
            {
                if (swaps.Contains(set[i])) continue; // skip if we already done swap with this item
                swaps.Add(set[i]);

                Swap(set, start, i);
                permute(start + 1); 
                Swap(set, start, i);
            }
        }
    };

    permute(0);

    return result;
}

private static void Swap(List<int> set, int index1, int index2)
{
    int temp = set[index1];
    set[index1] = set[index2];
    set[index2] = temp;
}

Here is the image that shows how swap algorithm works.

enter image description here

So you have {A,B,C}, {A,C,B}, {B,A,C}, {B,C,A}, {C,B,A}, {C,A,B}

Now Consider A and B are equal. Ive edited the image with photoshop (sorry if im terrible at it!) And replaced B with A. As you can see in the image

enter image description here

Ive identified duplicates in image. If you skip them you will have {A,A,C}, {A,C,A}, {C,A,A}

You have to store items that are swapped so if the items are equal and we already had that swap we just skip to prevent dupes

if (swaps.Contains(set[i])) continue; // skip if we already done swap with this item
swaps.Add(set[i]); // else add it to the list of swaps.

For test if you delete this part then this algorithm will produce duplicate permutations and console will output n!.

like image 87
M.kazem Akhgary Avatar answered Nov 02 '22 19:11

M.kazem Akhgary