I've been struggling recently writing a kind of "speed dating style" algorithm. The basic goal is for each member of one group (Men) to meet each member of the other group (Women) at their table, once.
The conditions are:
- The number of tables are equal to the number of women.
- Each man is assigned to a table where a woman is sitting (1v1 dialogue).
- In the next round, each man is switched to another table he has not been to before.
- If the groups are of different size, no member (man or woman) must be in pause (lack a partner) for two rounds in a row.
Difficulty arises when the Men group has more members than the Women group, or vice-versa.
Example:
var men = [
'm1', 'm2', 'm3', 'm4', 'm5',
],
women = [
'w1', 'w2', 'w3'
];
┃ ROUND 1 ┃ ROUND 2
┌─────────────┬─────────────┐ ┌─────────────┬─────────────┐
│ men │ women │ │ men │ women │
├─────────────┴─────────────┤ ├─────────────┴─────────────┤
│ m1 >>> w1 │ │ m4 >>> w1 │
│ m2 >>> w2 │ │ m5 >>> w2 │
│ m3 >>> w3 │ │ m1 >>> w3 │
│ m4 pause │ │ m2 pause │
│ m5 pause │ │ m3 pause │
└───────────────────────────┘ └───────────────────────────┘
┃ ROUND 3
┌─────────────┬─────────────┐
│ men │ women │
├─────────────┴─────────────┤
│ m2 >>> w1 │
│ m3 >>> w2 │
│ m4 >>> w3 │
│ m5 pause │
│ m1 pause │
└───────────────────────────┘
Matchups therefore are:
var results = [
'm1' = [
'w1', 'w3', null
],
'm2' = [
'w2', null, 'w1'
],
'm3' = [
'w3', null, 'w2'
],
'm4' = [
null, 'w1', 'w3'
],
'm5' = [
null, 'w2', null
],
];
So far my attempts at tackling this problem have been unsuccessful, despite looking at similar questions in this site and others (see screenshot below). In this attempt I ran 15/15 rounds and still ended up with persons in pause for 2 or more rounds, etc.
* Names in the screenshot are fake; generated by Faker
My current (non-working) attempt in Javascript
I basically have four arrays
- Array of men
- Array of women
- Array of available tables for the round
- Array of unmet women per man
var men_array = [
'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9', 'm10'
];
var women_array = [
'w1', 'w2', 'w3', 'w4', 'w5'
];
var available_tables_this_round = [];
var unmet_women = [];
// Run round
function start_round(){
console.log('START OF ROUND ----------------');
// Set available tables this round
// One table per woman
available_tables_this_round = women_array;
// Selected table
var selected_table;
// Finding table partner for each man
men_array.forEach(function(item){
var current_man = item;
// Checking if this user has unmet women on record
if(typeof unmet_women[current_man] == 'undefined'){
// Unmet women array not set. Setting...
unmet_women[current_man] = women_array;
}
// Loop through available tables to see which
// of those the man can join (has not visited yet).
for(var i = 0; i < available_tables_this_round.length; i++){
var current_woman = available_tables_this_round[i];
// If woman among the available has not been met
if($.inArray(current_woman, available_tables_this_round) !== -1){
selected_table = current_woman;
// Removing table from those available this round
available_tables_this_round = $.grep(available_tables_this_round, function(value) {
return value != selected_table;
});
// Removing woman from unmet by this man
unmet_women[current_man] = $.grep(unmet_women[current_man], function(value) {
return value != selected_table;
});
console.log(current_man, '>>>', selected_table);
// Exiting loop since we've found a match for the man (for this round!)
break;
}
}
});
console.log('END OF ROUND ----------------');
}
// Button handling
$(document).on('click', 'button#start_round_btn', start_round);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="start_round_btn">Start Round</button>
The main problem here is making sure neither men nor women are put on pause twice in a row.
To ensure this does not happen, think of placing "pause tables" in between the tables with the women, until you have enough tables to seat the men. This means in a 0-based table numbering, that these "pause tables" get an odd position index.
When you then cycle the men from one table to the next, there will never be a man that is put on "pause" twice in a row, yet they will meet every woman before they get back to the table they were first assigned to.
With this method it also becomes apparent that there is no solution to the problem when there are more than twice as many men as women. In that case it is unavoidable that a man is put on pause twice in sequence.
A similar trick can be used when there are fewer men: women may never be alone for more than one round in sequence, so in this case introduce dummy men ("pause seaters") with the same methodology: put the men in a row, and inject in this row these "dummy men" so they end up at odd indices. I call this potentially extended row the "seaters".
If you do the above, you'll have just as many seaters as tables, and it becomes trivial to produce the assignments for each round: just cycle the seaters over the tables: each round a seater moves to the next table in sequence, cycling back to the first table after the last one.
Here is the code:
OK I loved this question. My approach will handle up until a group is twice the size of the other. Nobody will wait twice in a row. I am also happy to take the chance to use my
Array.prototype.rotate()
method as well.Well ok the code is;
Well we have gender in short many tables for each round and we have to make as many rounds as the number of gender in excess.
First we find all possible matches
which return us
then we move to our
arrangeTables()
function. It works like thisTake first table many rows (3 in this case) and pick 3 meetings according to the row index and put them in an array.
meetings.push(matches.slice(0,tableCount).map((t,ix) => t[ix]));
So
[["w1","m1"],["w2","m2"],["w3","m3"]]
are picked in the first round.Remove the first three rows of
matches
. Rotate the removed rows once and concatenate them back to thematches
.matches = matches.concat(matches.splice(0,tableCount).rotate(1));
So the matches would become as follows in the second round.
And repeat this routine "number of gender in excess" many times. (5 in this case)
You could just create an array tables like this :
To obtain :
Hence you would just have to recombinate couples for "nouvelles rencontres"
You could fill the arrays with pause and maintain the same length of the arrays, first. Then you could use the cross product of the arrays with a shift on the first array and by keeping the value of the second array.