### Overview

Networking events are becoming more and more popular. People are attending them expecting to find someone capable of solving their concerns.

The problem of telling *"who should talk to who"* is relatively easy when the number of attendees is low. In this case, conveyors can do the matching manually. However when the number of people (or a variety of skills) increases things beginning to complicate.

### Desired scenario

Let's consider the following plot:

Before an event, each person fills-in the form describing his

skillsandneeds

For example:

1 2 3 4 5 6 |
Person A Skills: - web development, - mobile development Needs: - investor |

1 2 3 4 5 6 7 |
Person B Skills: - investor, - entrepreneur Needs: - graphic designer, - internet marketer |

Later on, each person receives the information telling which people are somehow especially valuable and should talk to.

1 2 3 4 |
Person A should talk to: - Person X - Person Y - Person Z |

### Problem

The main **question** is: *"Who should each person talk to?"*

To estimate the difficulty of the problem let's assume that there are (n) people and each one receives only

three recommendations().

This gives a total number of combinations expressed with:

That is estimated like follows:

1 2 3 4 5 6 7 8 9 10 11 |
4 people = 4 combinations 5 people = 20 combinations 6 people = 60 combinations 7 people = 140 combinations ... 20 people = 19 380 combinations 21 people = 23 940 combinations ... 50 people = 921 200 combinations ... 100 people = 15 684 900 combinations |

It is obvious that the number of possible combinations is growing faster for the bigger amount of participants.

In *naive approach* (i.e. brute-force) each combination should be evaluated and compared with the rest (lots of computation).

Additionally, to compare the solutions (to know whether they are good or bad) they have to be somehow measured - this leads to a concept of a **fitness function**.

### Fitness function

A fitness function (f) is generally a function taking a possible combination as an argument and returning a numeric value.

For example a fitness function for a very bad combination (lot's of mismatches) will have a very low score.

In our case the function can return values in the range from 0 (worst) to 1 (best):

In this case, a fitness function will check two things:

- do the matched candidates have skills matching our needs?
- how often is the analyzing person being recommended to others?

The last conditions deal with a problem of *rare competencies*. There is a possible scenario with a person with rare skills, and lot's of others who are needing it.

Finally, each condition has a **weight** assigned to it. A fact of fulfilling needs will be more important than the popularity.

Having a way of evaluating the solution we can proceed with the **algorithm**.

### Algorithm

To approach this problem a variation of an **evolutionary algorithm** (EA) will be used. EA are sort of meta-heuristics based on Darwinian principles of evolution.

Intuitively their workflow looks like follows:

Description of process:

- Randomly assign 3 people to each participant
- Randomly pick-up two participant
- Make the connection if the second one is interesting for the first one (
cross-over)- Randomly pick-up two participant and connect them (
mutation, happens very rarely)- Go to step 2

Each course from 2-5 is called an **epoch** or **generation**. An epoch is represented by the possible solution (list of all participants with their matchings) that can be also represented using a fitness function (average of all individuals).

1 2 3 4 5 6 7 8 |
final def POPULATION_SIZE = 50 final def GENERATIONS = 3000 Population population = new Population(POPULATION_SIZE) for (generation in 2..GENERATIONS) { population = Algorithm.evolvePopulation(population) } |

In step 3, we are randomly selecting two people (**cross-over**) and trying to match the second one to the first one. A possible individual is being put either as the first, second or third match. In all cases, the overall fitness function is calculated. If a better solution than the current one if found - the matching is performed.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private static def crossover(Population pop) { def indiv1 = (Participant) pop.getRandomIndividual() def indiv2 = (Participant) pop.getRandomIndividual() if (indiv1 != indiv2) { if (indiv1.isUseful(indiv2)) { def alternatives = [] indiv1.matches.eachWithIndex { match, index -> def altPop = (Population) pop.clone() altPop.match(indiv1.id, indiv2.id, index) alternatives << ([index: index, fitness: altPop.fitness()]) } alternatives.sort { -it.fitness } def bestAlt = alternatives.first() if (bestAlt.fitness > pop.fitness()) { pop.match(indiv1.id, indiv2.id, bestAlt.index) } } } } |

Step 4 represent a **mutation** - a very rare possibility of accepting a worse solution. The main idea is to introduce some diversity into the solution.

1 2 3 4 5 6 7 8 9 10 11 12 |
private static def mutate(Population pop) { if (Math.random() < 0.0003) { def indiv1 = (Participant) pop.getRandomIndividual() def indiv2 = (Participant) pop.getRandomIndividual() def randomIndex = (int) (Math.random() * indiv1.matches.size()) if (indiv1 != indiv2) { pop.match(indiv1.id, indiv2.id, randomIndex) } } } |

Each generation is evolved with a cross-over followed by a mutation:

1 2 3 4 5 6 7 8 |
static def evolvePopulation(Population pop) { def newPopulation = (Population) pop.clone() crossover(newPopulation) mutate(newPopulation) return newPopulation } |

The whole source code (written in Groovy) is available here.

### Results

The testing was performed in 3 cases:

- 20 people,
- 50 people,
- 100 people

In each one, the initial population (skills, needs, and matches) was randomly generated. In real case scenario, you will have to load this data from other sources (like Excel, DB, or CSV file). Also, there was an upper limit of 5000 generations (all took about 5 seconds to complete).

After looking at the plot you can see some interesting facts:

- All cases are starting random solution, which is generally bad (the worst one is for 50 participants (),
- All cases are getting very close the perfect matching after 3000 generations,
- There was one case of mutation (red line drop, near 5000-th generation),
- Learning is slower when the greater number of participants

### Conclusion

This experiment concludes that evolutionary algorithm provides a very efficient way of solving match-making problems. They are easy to implement and very extendible to custom restrictions and limitations.

The usage of EA can also provide an extra value for generating online recommendations during the event. It's common that some of the participants are absent which is disturbing others expectations.