Technical challenge for the interview at Credimi S.p.A. Java program to print all possibile permutations of a list of numbers.
Go to file
2021-08-19 18:53:19 +02:00
src Add sources. 2021-08-19 18:53:19 +02:00
.gitignore Add sources. 2021-08-19 18:53:19 +02:00
Dockerfile Add sources. 2021-08-19 18:53:19 +02:00
pom.xml Add sources. 2021-08-19 18:53:19 +02:00
README.md Add sources. 2021-08-19 18:53:19 +02:00

Challenge

Technical challenge for an interview at Credimi S.p.A.

Suppose you're given a file with a single string of comma separated numbers like the following

$ cat input  
123,456,789

Your program must print to STDOUT all possible permutations with repetitions, like follows (actual order is not relevant)

$ cat input | <your program> > output  
$ cat output  
123,456,789  
123,789,456  
456,789,123  
456,123,789  
789,123,456  
789,456,123  

Your solution must print to STDOUT and any extraneous output will be considered an error (but you may print to STDERR at your convenience). Your program will be tested against inputs of growing size, the more they can take, the better!

The solution must contain documentation explaining time and space complexity of your implementation.

You should use only the standard library of your language of choice, any additional requirement should be documented and motivated.

Java, Python, Scala or Rust are preferred.

Solution

Typo

The challenge description contains an error.
It asks to print all possible permutations WITH repetitions, but the example shows the permutations without repetition.
If we consider permutations with repetitions, we would also need an additional parameter that is the desired length of the permutation. The typo is probably "with repetition" instead of "without repetition".

Algorithm

The most efficient algorithm to compute all permutations is Heap's Algorithm.
It minimizes elements swapping: every permutation is generated interchanging a single pair of elements from the previous one.

Heap algorithm cannot be parallelized, we could use a less efficient algorithm with better average time assuming thread count > 1 (see for example Ouellet Indexed).
However, the bottleneck of our program is the writing to stdout, in some tests I saw that the multi-threaded version is actually slower, because threads need to synchronize to write to the output.

Choices

Element types

The challenge description does not specify the type of the input elements, they are just "numbers".
I decided to represent them with the Java primitive type long.
There is no noticeable performance improvement representing them with an int instead.

Repeated elements

It's not specified what is the desired output if a number is repeated.
I've decided to not check for duplicate elements, since they affect the algorithm time and space complexity.
If one or more numbers is repeated, some permutations will be duplicated:

$ echo "1,1" | <my program>
1,1
1,1

Implementation

I wrote the project in Java because it's the language where I have more experience.
It's a maven project that targets JDK11+.

Time and space complexity

Given n elements, there are n! permutations. Heap's algorithm compute a new permutation in O(1) time. Each permutation of n elements must also be printed to the stdout (O(n) time).
Time complexity is O(n*n!).

The program uses two n sized arrays and a fixed buffer. Space complexity is O(n).

Classes

I divided the program into three classes:

  • ElementsInputReader: to read the input elements.
  • PermutationsGenerator: to compute all the elements permutations.
  • PermutationsPrinter: to print permutations to the output.

Generics

I didn't use generics to compute permutations of an array T[] because the program performance would be a lot worse than using primitive types.

How to run the application

Compilation

If you have JDK11+ and Maven installed, you can simply run

mvn package

The jar file will be in target/permutations.jar.

Otherwise, you can use the provided Dockerfile

docker build -t permutations .

(Maven compilation fails if you had built the project locally, be sure to delete target directory).

Run

If you have built the project using Docker, you can simply run it

echo 1,2,3 | docker run --rm -i permutations

However running it with Docker has a big effect on performances.
To extract the jar file, run

docker create --name permutations permutations
docker cp permutations:/app/permutations.jar .

Then run it with locally installed JRE (11+)

echo 1,2,3 | java -jar permutations.jar