Core Java

Permutation – Heap’s Algorithm

 This is a little bit of experimentation that I did recently to figure out a reasonable code to get all possible permutations of a set of characters. 

So say given a set of characters “ABC”, my objective is to come up code which can spit out “ABC”, “ACB”, “BAC”, “BCA”, “CBA”, “CAB”. 

The approach I took is to go with the definition of permutation itself, so with “ABCD” as the set of characters a 4 slot that needs to be filled.

The first slot can be filled by any of A, B, C, D, in 4 ways:

The second slot by any of the remaining 3 characters, so with “A” in the first slot – 

The third slot by the remaining 2 characters, so with “A”, “B” in the first two slots:

And finally, the fourth slot by the remaining 1 character, with say “A”, “B”, “C” in the first 3 slots:

In total, there would be 4 for the first slot * 3 for the 2nd slot * 2 for the 3rd slot * 1 for the 4th slot – 24 permutations altogether. 

I can do this in place, using an algorithm that looks like this:

package org.bk.algo.general;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class Permutations {

    public List permutations(String str) {
        char[] chars = str.toCharArray();
        List result = new ArrayList<>();
        permutations(0, chars, result);
        return result;
    }

    private void permutations(int idx, char[] arr, List result) {
        if (idx == arr.length - 1) {
            result.add(new String(arr));
        }

        for (int i = idx; i <= arr.length - 1; i++) {
            swap(arr, i, idx);
            permutations(idx + 1, arr, result);
            swap(arr, i, idx);
        }
    }


    private void swap(char[] arr, int p1, int p2) {
        if (p1 == p2) return;
        char temp = arr[p1];
        arr[p1] = arr[p2];
        arr[p2] = temp;
    }

    @Test
    void testPerms() {
        List abcdPerms = permutations("ABCD");
        assertThat(abcdPerms).hasSize(24);
        assertThat(abcdPerms)
                .containsExactlyInAnyOrder(
                        "ABCD", "ABDC", "ACBD", "ACDB", "ADCB", "ADBC", "BACD", "BADC", "BCAD", "BCDA", "BDCA", "BDAC",
                        "CBAD", "CBDA", "CABD", "CADB", "CDAB", "CDBA", "DBCA", "DBAC", "DCBA", "DCAB", "DACB", "DABC");
    }
}

A trace of flow and the swaps is here:

The only trick here is that the code does all the holding of characters and getting it into the right place in place by swapping the right characters to the right place and restoring it at the end of it. 

This works well for a reasonably sized set of characters – reasonable because for just 10 characters, there would be 3,628,800 permutations. 

An algorithm that works even better, though a complete mystery to me how it actually functions(well explained
here if anybody is interested), is the Heap’s Algorithm. Here is a java implementation of it:

package org.bk.algo.general;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class PermutationsHeap {

    public List permutations(String str) {
        char[] chars = str.toCharArray();
        List result = new ArrayList<>();
        permutations(chars.length, chars, result);
        return result;
    }

    private void permutations(int k, char[] arr, List result) {
        if (k == 1) {
            result.add(new String(arr));
            return;
        }
        permutations(k - 1, arr, result);
        for (int i = 0; i < k - 1; i++) {
            if (k % 2 == 0) {
                swap(arr, i, k - 1);
            } else {
                swap(arr, 0, k - 1);
            }
            permutations(k - 1, arr, result);
        }
    }
    private void swap(char[] arr, int p1, int p2) {
        if (p1 == p2) return;
        char temp = arr[p1];
        arr[p1] = arr[p2];
        arr[p2] = temp;
    }

    @Test
    void testPerms() {
        List abcdPerms = permutations("ABCD");
        assertThat(abcdPerms).hasSize(24);
        assertThat(abcdPerms)
                .containsExactlyInAnyOrder(
                        "ABCD", "ABDC", "ACBD", "ACDB", "ADCB", "ADBC", "BACD", "BADC", "BCAD", "BCDA", "BDCA", "BDAC",
                        "CBAD", "CBDA", "CABD", "CADB", "CDAB", "CDBA", "DBCA", "DBAC", "DCBA", "DCAB", "DACB", "DABC");
    }
}

It very efficiently does one swap per permutation, which is still high but better than the approach that I have described before. 

In a sample perumutation of 8 characters, which generates 40320 permutations, the home cooked version swaps 80638 times, and the Heap’s algorithm swaps 40319 times! thus proving its efficacy.

Published on Java Code Geeks with permission by Biju Kunjummen, partner at our JCG program. See the original article here: Permutation – Heap’s Algorithm

Opinions expressed by Java Code Geeks contributors are their own.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
St1mpy
St1mpy
3 years ago

Great article!

Back to top button