Instructions

To run follow run sections. Algorithm explained in the Algorithm section.

Screenshot from 2024-06-23 14-35-39

Run

The fastest way to run it. Is use Dockerfile.

 docker build --build-arg dir="Training Problems" --build-arg pyScript="evaluateShared.py" -t vpr:latest .
 docker run vpr:latest

Or run run.sh

The directory must contain the problems directory and evaluateShared.py. You can set them in build arguments dir and pyScript accordingly. Default values

ARG dir="Training Problems"
ARG pyScript="evaluateShared.py"

Alternatively, you can compile the Go binary for your operating system using the command:

go build .

Algorithm

The original intent was to implement the A* or smt similar. However, I started with the Nearest Neighbor algorithm.

This allowed achieving a mean cost of 52041.86997702847 and an average runtime of 4.130744934082031ms. In the next iteration, I decided to tune it with some versions N recursive brute force Salesman algorithm.

This resulted in a slight improvement in the mean cost, down to 49508.511593292424, but a significant increase in runtime to 8068.529975414276ms. I figured out that some test cases contained a multitude of small vectors close to the depot (especially problems 5 and 6). What caused very deep recursions and consequently, longer runtimes.

So I decided to apply following KNOWHOW : If the algorithm's execution exceeded 1 second for N=3 (branching), the best result from N=1,2 would be selected instead.

Later I applied different evaluators functions.

Evaluators

After we have a list of possible routes we start evaluate them.

  1. GetTheBestByLengthAndCostMin - we filter the longest routes and choose route with minimal cost
  2. GetTheBestByLengthAndCostMax - we filter the longest routes and choose route with maximum cost
  3. GetTheBestByLengthAndRandom - we filter the longest routes and then choose random one from it. To ensure determinacy, I seed the random generator with content of source file. For every iteration we recreate a random generator so it always give the same random sequence.. I use CHACHA8 fast random generator which was added in rand/v2 go22.

See performance for details.

// 1 52041.86997702847 <-- NEAREST NEIGHBOR
// 2 49628.99589848467
// 3 49324.77775315616
...

// 48341.15786902501  <-- run N = 1, 2, 3 with Min, Max, Random evaluators 

Code

I am trying to demonstrate proficiency in GoLang and in general programming.

Most golish part is this clumsy code. As I explained in the Algorithm section, I noted that some test cases take less than 1ms with N=1 and up to 150 seconds with N=3. We start all tasks at the same time in different goroutines. I chose not to add a process limiter as the current implementation only uses up to 91 goroutines at most, with 3 of them finishing almost instantly. The important thing is to catch test cases that may take a lot of time and stop them right away. The mentioned logic handle it.

Footnotes

  1. Actually 7. I fixed it here