我最近一直在努力写一种快速约会风格"算法。基本目标是让一个小组中的每个成员(男性)在他们的桌子上与其他小组(女性)的每个成员会面一次。
条件是:
当男性组的成员多于女性组时会出现困难,反之亦然。
示例:
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 │
└───────────────────────────┘
因此比赛是:
var results = [
'm1' = [
'w1', 'w3', null
],
'm2' = [
'w2', null, 'w1'
],
'm3' = [
'w3', null, 'w2'
],
'm4' = [
null, 'w1', 'w3'
],
'm5' = [
null, 'w2', null
],
];
到目前为止,尽管在本网站及其他网站上查看了类似的问题,但我在解决此问题方面的尝试仍然没有成功(见下面的截图)。在这次尝试中,我进行了15/15轮比赛,最后还是暂停了两轮或更多轮的人等等。
*截图中的名称是假的;由Faker生成
我基本上有四个数组
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);
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="start_round_btn">Start Round</button>
&#13;
答案 0 :(得分:4)
您可以暂停填充数组,并首先保持数组的长度相同。然后你可以使用数组的叉积,在第一个数组上移位,并保留第二个数组的值。
function getSeating(a1, a2) {
function fill(a, l) {
var p = l - a.length;
a = a.slice();
while (p) {
a.splice(p, 0, 'pause' + p);
p--;
}
return a;
}
var max = Math.max(a2.length, a1.length);
a1 = fill(a1, max);
a2 = fill(a2, max);
return a1.map(function (_, i) {
return a2.map(function (b, j) {
return [a1[(i + j) % max], b];
});
});
}
console.log(getSeating(['m1', 'm2', 'm3', 'm4', 'm5'], ['w1', 'w2', 'w3']));
console.log(getSeating(['m1', 'm2', 'm3'], ['w1', 'w2', 'w3', 'w4', 'w5']));
console.log(getSeating(['m1', 'm2', 'm3', 'm4', 'm5'], ['w1', 'w2', 'w3', 'w4', 'w5']));
console.log(getSeating(['m1', 'm2', 'm3', 'm4'], ['w1', 'w2']));
console.log(getSeating(['m1', 'm2'], ['w1', 'w2', 'w3', 'w4']));
console.log(getSeating(['m1', 'm2', 'm3', 'm4'], ['w1', 'w2', 'w3', 'w4']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
答案 1 :(得分:4)
这里的主要问题是确保男性和女性都不会连续两次停顿。
为了确保不会发生这种情况,请考虑放置&#34;暂停表&#34;在与女性的桌子之间,直到你有足够的桌子来安排男人。这意味着在基于0的表编号中,这些&#34;暂停表&#34;得到一个奇怪的位置指数。
当你把男人从一张桌子循环到下一张桌子时,永远不会有男人穿上&#34;暂停&#34;连续两次,但他们会遇到每个女人,然后再回到他们第一次被分配到的桌子上。
使用这种方法,当男性比女性多两倍时,问题也很明显。在这种情况下,一个人按顺序暂停两次是不可避免的。
当男性较少时,可以使用类似的技巧:女性可能永远不会单独按顺序进行一轮以上,因此在这种情况下,使用相同的方法引入假人(&#34;暂停座位&#34;) :把这些男人排成一排,并在这一行中注入这些假人&#34;所以他们最终得到奇怪的指数。我将这个可能延长的行称为“#34;座位”。
如果你这样做,你将拥有与桌子一样多的座位,并且为每一轮产生分配变得微不足道:只需将座位在桌子上循环:每一轮座位移动到下一轮按顺序排列表,循环回到最后一个表后的第一个表。
以下是代码:
function getRounds(men, women) {
// Exclude the cases where a solution is not possible
if (men.length > women.length * 2) throw "not possible: too many men";
if (women.length > men.length * 2) throw "not possible: too many women";
// Create the tables:
// Women get a fixed place, so the table name is the woman's name
var tables = women.slice();
// Create the seaters: they are the men
var seaters = men.slice();
// Which do we have fewer of? tables or seaters?
var maxLen = Math.max(tables.length, seaters.length);
var least = tables.length < maxLen ? tables : seaters;
// The smallest of both arrays should get some dummies added to it.
// We insert the dummies at odd indices: This is the main point of this algorithm
for (var i = 0; least.length < maxLen; i++) least.splice(i*2+1, 0, 'pause');
// Now everything is ready to produce the rounds. There are just as many
// rounds as there are tables (including the "pause tables"):
return tables.map( (_, round) =>
// Assign the men: each round shifted one place to the left (cycling):
tables.map( (table, tid) => [seaters[(round+tid)%maxLen], table] )
);
}
var result1 = getRounds(["John", "Tim", "Bob", "Alan", "Fred"],
["Lucy", "Sarah", "Helen"]);
console.log(result1);
var result2 = getRounds(["John", "Tim"],
["Lucy", "Sarah", "Helen", "Joy"]);
console.log(result2);
&#13;
答案 2 :(得分:3)
你可以像这样创建一个数组表:
var tables = men.map((element, index) => index < women.length ? [element, women[index]] : [element, "pause"]);
获取:
[ [ 'm1', 'w1' ],
[ 'm2', 'w2' ],
[ 'm3', 'w3' ],
[ 'm4', 'pause' ],
[ 'm5', 'pause' ]
]
因此,你只需重组夫妻“新生儿”即可。
答案 3 :(得分:1)
好的我喜欢这个问题。我的方法将处理,直到一个组的大小是另一组的两倍。没有人会连续两次等待。我也很高兴有机会使用我的Array.prototype.rotate()
方法。
好的代码是;
Array.prototype.rotate = function(n){
var len = this.length,
res = new Array(this.length);
if (n % len === 0) return this.slice(); // no need to rotate just return a copy
else for (var i = 0; i < len; i++) res[i] = this[(i + (len + n % len)) % len];
return res;
};
function arrangeTables(matches){
var meetings = [],
tableCount = matches[0].length;
for (var i = 0, len = matches.length; i < len; i++){
meetings.push(matches.slice(0,tableCount).map((t,ix) => t[ix]));
matches = matches.concat(matches.splice(0,tableCount).rotate(1));
}
return meetings;
}
var men = ['m1', 'm2', 'm3', 'm4', 'm5'],
women = ['w1', 'w2', 'w3'],
few = [],
much = men.length > women.length ? (few = women,men) : (few = men,women), // this is needed for the nesting order of maps to get matches.
matches = much.map(m => few.map(f => [f,m]));
result = arrangeTables(matches);
console.log(result);
我们每轮都有很多性别表,我们必须做出与性别数量相当多的轮次。
首先我们找到所有可能的匹配
matches = much.map(m => few.map(f => [f,m]))
返回我们
[ [ [ 'w1', 'm1' ], [ 'w2', 'm1' ], [ 'w3', 'm1' ] ],
[ [ 'w1', 'm2' ], [ 'w2', 'm2' ], [ 'w3', 'm2' ] ],
[ [ 'w1', 'm3' ], [ 'w2', 'm3' ], [ 'w3', 'm3' ] ],
[ [ 'w1', 'm4' ], [ 'w2', 'm4' ], [ 'w3', 'm4' ] ],
[ [ 'w1', 'm5' ], [ 'w2', 'm5' ], [ 'w3', 'm5' ] ] ]
然后我们转到我们的arrangeTables()
函数。它像这样工作
将第一张表格排成多行(本例中为3行)并选择3个会议 根据行索引并将它们放在一个数组中。
meetings.push(matches.slice(0,tableCount).map((t,ix) => t[ix]));
因此在第一个中选择了[["w1","m1"],["w2","m2"],["w3","m3"]]
轮。
删除matches
的前三行。旋转删除的行
一次并将它们连接回matches
。
matches = matches.concat(matches.splice(0,tableCount).rotate(1));
所以第二轮比赛将如下所示。
[ [ [ 'w1', 'm4' ], [ 'w2', 'm4' ], [ 'w3', 'm4' ] ],
[ [ 'w1', 'm5' ], [ 'w2', 'm5' ], [ 'w3', 'm5' ] ],
[ [ 'w1', 'm2' ], [ 'w2', 'm2' ], [ 'w3', 'm2' ] ],
[ [ 'w1', 'm3' ], [ 'w2', 'm3' ], [ 'w3', 'm3' ] ],
[ [ 'w1', 'm1' ], [ 'w2', 'm1' ], [ 'w3', 'm1' ] ] ]
多次重复此例行程序“超出性别数”。 (在这种情况下为5)