stable-marriage
Version:
A javascript implementation of stable marriage problem
702 lines (684 loc) • 15.9 kB
JavaScript
const {
Person,
stableMarriage,
clearNotReferedPerson,
} = require('../stable-marriage-problem')
const commonCases = [
// 先写 6 * 6 的
[
// boys.length === 1
{ boys: { A: ['a'] }, girls: { a: ['A'] }, pairings: { A: 'a' } },
{
boys: { A: 'b,a'.split(',') },
girls: { a: ['A'], b: ['A'] },
pairings: { A: 'b' },
},
{
boys: { A: 'c,a,b'.split(',') },
girls: { a: ['A'], b: ['A'], c: ['A'] },
pairings: { A: 'c' },
},
{
boys: { A: 'a,c,d,b'.split(',') },
girls: { a: ['A'], b: ['A'], c: ['A'], d: ['A'] },
pairings: { A: 'a' },
},
{
boys: { A: 'e,c,d,a,b'.split(',') },
girls: { a: ['A'], b: ['A'], c: ['A'], d: ['A'], e: ['A'] },
pairings: { A: 'e' },
},
{
boys: { A: 'e,c,d,f,a,b'.split(',') },
girls: { a: ['A'], b: ['A'], c: ['A'], d: ['A'], e: ['A'], f: ['A'] },
pairings: { A: 'e' },
},
],
[
// boys.length === 2
{
boys: { A: ['a'], B: ['a'] },
girls: { a: 'A,B'.split(',') },
pairings: { A: 'a' },
},
{
boys: { A: 'b,a'.split(','), B: 'a,b'.split(',') },
girls: { a: 'B,A'.split(','), b: 'B,A'.split(',') },
pairings: { A: 'b', B: 'a' },
},
{
boys: { A: 'a,b,c'.split(','), B: 'c,a,b'.split(',') },
girls: { a: 'A,B'.split(','), b: 'A,B'.split(','), c: 'A,B'.split(',') },
pairings: { A: 'a', B: 'c' },
},
{
boys: { A: 'c,d,a,b'.split(','), B: 'a,c,b,d'.split(',') },
girls: {
a: 'B,A'.split(','),
b: 'A,B'.split(','),
c: 'A,B'.split(','),
d: 'B,A'.split(','),
},
pairings: { A: 'c', B: 'a' },
},
{
boys: { A: 'c,a,d,b,e'.split(','), B: 'e,d,a,c,b'.split(',') },
girls: {
a: 'B,A'.split(','),
b: 'B,A'.split(','),
c: 'B,A'.split(','),
d: 'B,A'.split(','),
e: 'B,A'.split(','),
},
pairings: { A: 'c', B: 'e' },
},
{
boys: { A: 'c,f,b,d,e,a'.split(','), B: 'c,a,e,b,d,f'.split(',') },
girls: {
a: 'B,A'.split(','),
b: 'A,B'.split(','),
c: 'B,A'.split(','),
d: 'B,A'.split(','),
e: 'B,A'.split(','),
f: 'B,A'.split(','),
},
pairings: { A: 'f', B: 'c' },
},
],
[
// boys.length === 3
{
boys: { A: ['a'], B: ['a'], C: ['a'] },
girls: { a: 'C,A,B'.split(',') },
pairings: { C: 'a' },
},
{
boys: { A: 'a,b'.split(','), B: 'b,a'.split(','), C: 'a,b'.split(',') },
girls: { a: 'B,A,C'.split(','), b: 'C,B,A'.split(',') },
pairings: { B: 'a', C: 'b' },
},
{
boys: {
A: 'b,a,c'.split(','),
B: 'a,b,c'.split(','),
C: 'c,b,a'.split(','),
},
girls: {
a: 'B,A,C'.split(','),
b: 'B,A,C'.split(','),
c: 'B,C,A'.split(','),
},
pairings: { A: 'b', B: 'a', C: 'c' },
},
{
boys: {
A: 'd,b,a,c'.split(','),
B: 'c,b,d,a'.split(','),
C: 'd,a,b,c'.split(','),
},
girls: {
a: 'A,B,C'.split(','),
b: 'C,B,A'.split(','),
c: 'A,B,C'.split(','),
d: 'C,B,A'.split(','),
},
pairings: { A: 'b', B: 'c', C: 'd' },
},
{
boys: {
A: 'd,b,a,c,e'.split(','),
B: 'a,b,e,c,d'.split(','),
C: 'b,a,d,c,e'.split(','),
},
girls: {
a: 'A,C,B'.split(','),
b: 'A,C,B'.split(','),
c: 'A,C,B'.split(','),
d: 'C,B,A'.split(','),
e: 'C,B,A'.split(','),
},
pairings: { A: 'd', B: 'a', C: 'b' },
},
{
boys: {
A: 'a,f,d,e,b,c'.split(','),
B: 'b,f,d,e,c,a'.split(','),
C: 'a,e,f,b,c,d'.split(','),
},
girls: {
a: 'C,B,A'.split(','),
b: 'A,C,B'.split(','),
c: 'C,A,B'.split(','),
d: 'B,A,C'.split(','),
e: 'B,C,A'.split(','),
f: 'C,A,B'.split(','),
},
pairings: { A: 'f', B: 'b', C: 'a' },
},
],
[
// boys.length === 4
{
boys: { A: ['a'], B: ['a'], C: ['a'], D: ['a'] },
girls: { a: 'A,D,B,C'.split(',') },
pairings: { A: 'a' },
},
{
boys: {
A: 'b,a'.split(','),
B: 'b,a'.split(','),
C: 'a,b'.split(','),
D: 'a,b'.split(','),
},
girls: { a: 'C,D,B,A'.split(','), b: 'C,A,D,B'.split(',') },
pairings: { A: 'b', C: 'a' },
},
{
boys: {
A: 'c,b,a'.split(','),
B: 'b,c,a'.split(','),
C: 'a,b,c'.split(','),
D: 'c,b,a'.split(','),
},
girls: {
a: 'D,B,C,A'.split(','),
b: 'A,B,D,C'.split(','),
c: 'D,A,C,B'.split(','),
},
pairings: { A: 'b', B: 'a', D: 'c' },
},
{
boys: {
A: 'c,b,a,d'.split(','),
B: 'b,c,a,d'.split(','),
C: 'b,a,c,d'.split(','),
D: 'd,c,b,a'.split(','),
},
girls: {
a: 'C,B,A,D'.split(','),
b: 'A,D,C,B'.split(','),
c: 'B,D,C,A'.split(','),
d: 'A,B,C,D'.split(','),
},
pairings: { A: 'b', B: 'c', C: 'a', D: 'd' },
},
{
boys: {
A: 'c,a,b,d,e'.split(','),
B: 'd,c,a,b,e'.split(','),
C: 'c,d,b,e,a'.split(','),
D: 'c,d,e,b,a'.split(','),
},
girls: {
a: 'C,D,A,B'.split(','),
b: 'C,A,D,B'.split(','),
c: 'D,C,B,A'.split(','),
d: 'B,A,D,C'.split(','),
e: 'D,B,C,A'.split(','),
},
pairings: { A: 'a', B: 'd', C: 'b', D: 'c' },
},
{
boys: {
A: 'd,b,e,a,f,c'.split(','),
B: 'f,b,d,e,a,c'.split(','),
C: 'a,d,c,b,f,e'.split(','),
D: 'e,f,d,b,a,c'.split(','),
},
girls: {
a: 'C,A,D,B'.split(','),
b: 'A,D,C,B'.split(','),
c: 'A,B,C,D'.split(','),
d: 'B,C,A,D'.split(','),
e: 'C,D,A,B'.split(','),
f: 'B,D,C,A'.split(','),
},
pairings: { A: 'd', B: 'f', C: 'a', D: 'e' },
},
],
[
// boys.length === 5
{
boys: { A: ['a'], B: ['a'], C: ['a'], D: ['a'], E: ['a'] },
girls: { a: 'B,C,A,D,E'.split(',') },
pairings: { B: 'a' },
},
{
boys: {
A: 'a,b'.split(','),
B: 'b,a'.split(','),
C: 'a,b'.split(','),
D: 'b,a'.split(','),
E: 'a,b'.split(','),
},
girls: { a: 'E,C,B,D,A'.split(','), b: 'C,E,B,A,D'.split(',') },
pairings: { C: 'b', E: 'a' },
},
{
boys: {
A: 'c,b,a'.split(','),
B: 'b,a,c'.split(','),
C: 'a,c,b'.split(','),
D: 'b,a,c'.split(','),
E: 'c,b,a'.split(','),
},
girls: {
a: 'A,D,E,C,B'.split(','),
b: 'D,A,E,B,C'.split(','),
c: 'B,D,E,A,C'.split(','),
},
pairings: { A: 'a', B: 'c', D: 'b' },
},
{
boys: {
A: 'd,b,a,c'.split(','),
B: 'b,d,a,c'.split(','),
C: 'b,c,d,a'.split(','),
D: 'a,d,b,c'.split(','),
E: 'a,d,c,b'.split(','),
},
girls: {
a: 'A,D,E,B,C'.split(','),
b: 'E,B,C,D,A'.split(','),
c: 'D,E,C,A,B'.split(','),
d: 'B,D,A,E,C'.split(','),
},
pairings: { A: 'd', B: 'b', D: 'a', E: 'c' },
},
{
boys: {
A: 'e,a,d,c,b'.split(','),
B: 'd,a,b,c,e'.split(','),
C: 'd,a,e,b,c'.split(','),
D: 'a,c,b,d,e'.split(','),
E: 'c,b,e,d,a'.split(','),
},
girls: {
a: 'B,D,E,A,C'.split(','),
b: 'D,A,C,B,E'.split(','),
c: 'C,B,D,E,A'.split(','),
d: 'E,C,B,D,A'.split(','),
e: 'C,B,D,E,A'.split(','),
},
pairings: { A: 'e', B: 'a', C: 'd', D: 'c', E: 'b' },
},
{
boys: {
A: 'b,a,e,c,d,f'.split(','),
B: 'a,e,f,d,c,b'.split(','),
C: 'f,e,a,b,d,c'.split(','),
D: 'f,a,d,b,c,e'.split(','),
E: 'b,a,f,d,c,e'.split(','),
},
girls: {
a: 'D,A,B,C,E'.split(','),
b: 'B,D,C,E,A'.split(','),
c: 'C,A,E,B,D'.split(','),
d: 'D,E,B,C,A'.split(','),
e: 'D,B,C,E,A'.split(','),
f: 'A,E,D,C,B'.split(','),
},
pairings: { A: 'c', B: 'e', C: 'b', D: 'a', E: 'f' },
},
],
[
// boys.length === 6
{
boys: { A: ['a'], B: ['a'], C: ['a'], D: ['a'], E: ['a'], F: ['a'] },
girls: { a: 'C,A,F,E,B,D'.split(',') },
pairings: { C: 'a' },
},
{
boys: {
A: 'a,b'.split(','),
B: 'a,b'.split(','),
C: 'b,a'.split(','),
D: 'b,a'.split(','),
E: 'a,b'.split(','),
F: 'a,b'.split(','),
},
girls: { a: 'B,F,D,C,E,A'.split(','), b: 'A,E,C,F,B,D'.split(',') },
pairings: { A: 'b', B: 'a' },
},
{
boys: {
A: 'a,b,c'.split(','),
B: 'a,b,c'.split(','),
C: 'c,a,b'.split(','),
D: 'b,a,c'.split(','),
E: 'a,b,c'.split(','),
F: 'a,b,c'.split(','),
},
girls: {
a: 'D,C,F,A,E,B'.split(','),
b: 'C,F,B,E,A,D'.split(','),
c: 'D,B,F,C,E,A'.split(','),
},
pairings: { B: 'c', C: 'b', D: 'a' },
},
{
boys: {
A: 'b,a,c,d'.split(','),
B: 'd,b,c,a'.split(','),
C: 'a,c,b,d'.split(','),
D: 'b,c,d,a'.split(','),
E: 'd,b,a,c'.split(','),
F: 'c,b,d,a'.split(','),
},
girls: {
a: 'C,B,E,D,A,F'.split(','),
b: 'A,D,E,C,F,B'.split(','),
c: 'D,E,F,C,A,B'.split(','),
d: 'C,F,E,B,A,D'.split(','),
},
pairings: { A: 'b', C: 'a', D: 'c', F: 'd' },
},
{
boys: {
A: 'a,e,d,b,c'.split(','),
B: 'e,c,b,a,d'.split(','),
C: 'd,a,b,c,e'.split(','),
D: 'd,c,e,b,a'.split(','),
E: 'b,e,a,c,d'.split(','),
F: 'e,d,a,b,c'.split(','),
},
girls: {
a: 'B,D,C,F,A,E'.split(','),
b: 'C,F,B,D,E,A'.split(','),
c: 'A,D,F,C,E,B'.split(','),
d: 'F,A,B,C,D,E'.split(','),
e: 'D,A,F,C,B,E'.split(','),
},
pairings: { A: 'a', B: 'b', C: 'd', D: 'c', F: 'e' },
},
{
boys: {
A: 'b,a,d,e,f,c'.split(','),
B: 'd,c,a,b,e,f'.split(','),
C: 'c,e,b,a,d,f'.split(','),
D: 'c,e,d,b,f,a'.split(','),
E: 'f,a,d,c,b,e'.split(','),
F: 'c,f,b,d,a,e'.split(','),
},
girls: {
a: 'F,A,C,B,E,D'.split(','),
b: 'F,B,D,C,A,E'.split(','),
c: 'D,F,E,B,C,A'.split(','),
d: 'E,C,D,A,F,B'.split(','),
e: 'C,D,A,F,E,B'.split(','),
f: 'E,F,A,D,B,C'.split(','),
},
pairings: { A: 'a', B: 'd', C: 'e', D: 'c', E: 'f', F: 'b' },
},
],
]
/******************************************************************************/
const asymmetricCases = [
// 非对称 preferences 5 * 5
[
// boys.length === 1
{
boys: { A: ['b'] },
girls: { a: [], b: ['A'] },
pairings: { A: 'b' },
},
{
boys: { A: ['a'] },
girls: { a: ['A'], b: ['A'], c: [] },
pairings: { A: 'a' },
},
{
boys: { A: ['c'] },
girls: { a: ['A'], b: [], c: ['A'], d: ['A'] },
pairings: { A: 'c' },
},
{
boys: { A: ['e'] },
girls: { a: [], b: ['A'], c: ['A'], d: ['A'], e: ['A'] },
pairings: { A: 'e' },
},
{
boys: { A: ['a'] },
girls: { a: ['A'], b: ['A'], c: [], d: ['A'], e: ['A'], f: ['A'] },
pairings: { A: 'a' },
},
],
[
// boys.length === 2
{
boys: { A: ['a'], B: ['a'] },
girls: {
a: [],
},
pairings: {},
},
{
boys: { A: 'b,c'.split(','), B: ['b'] },
girls: {
a: ['B'],
b: ['B'],
c: ['A'],
},
pairings: { A: 'c', B: 'b' },
},
{
boys: { A: 'a,c,d'.split(','), B: 'b,d'.split(',') },
girls: {
a: ['A'],
b: ['A'],
c: ['B'],
d: ['B'],
},
pairings: { A: 'a', B: 'd' },
},
{
boys: { A: 'a,b,d'.split(','), B: 'a,c,e'.split(',') },
girls: {
a: ['B'],
b: ['A'],
c: 'A,B'.split(','),
d: 'A,B'.split(','),
e: ['B'],
},
pairings: { A: 'b', B: 'a' },
},
{
boys: { A: 'b,d,e,f'.split(','), B: 'a,b,d,e'.split(',') },
girls: {
a: 'A,B'.split(','),
b: ['B'],
c: ['A'],
d: 'A,B'.split(','),
e: 'A,B'.split(','),
f: 'A,B'.split(','),
},
pairings: { A: 'd', B: 'a' },
},
],
[
// boys.length === 3
{
boys: { A: ['a'], B: ['a'], C: [] },
girls: {
a: ['B'],
},
pairings: { B: 'a' },
},
{
boys: { A: 'b,c'.split(','), B: ['b'], C: ['A'] },
girls: {
a: ['a'],
b: ['c'],
},
pairings: {},
},
{
boys: { A: 'a,c,d'.split(','), B: 'b,d'.split(','), C: 'b,d'.split(',') },
girls: {
a: 'B,C'.split(','),
b: ['A'],
c: 'A,C,B'.split(','),
d: ['C'],
},
pairings: { A: 'c', C: 'd' },
},
{
boys: { A: 'a,b,d'.split(','), B: [], C: ['b'] },
girls: {
a: [],
b: ['C'],
c: 'A,C,B'.split(','),
d: 'A,B,C'.split(','),
e: ['B'],
},
pairings: { A: 'd', C: 'b' },
},
{
boys: { A: 'b,d,e,f'.split(','), B: 'a,b,d,e'.split(','), C: ['e'] },
girls: {
a: 'A,B'.split(','),
b: ['C'],
c: ['A'],
d: 'C,B'.split(','),
e: [],
f: 'B,C'.split(','),
},
pairings: { B: 'a' },
},
],
]
/******************************************************************************/
/**
* 对每个测试用例转换为 Person 实例,进行稳定配对后,核对结果是否与期望一致
*
* @param {Object} testCase
* @returns {Boolean}
*/
const stableMarriageTest = ({ boys, girls, pairings }) => {
// 这俩是 Person 实例的数组
const newBoys = Object.keys(boys).map((name) => new Person(name))
const newGirls = Object.keys(girls).map((name) => new Person(name))
for (let boy of newBoys) {
// 测试用例中的优先级数组,元素为 name
const preferences = boys[boy.name]
for (let preference of preferences) {
for (let girl of newGirls) {
// 根据 name 字符串匹配 Person 实例
if (girl.name === preference) {
boy.preferences.push(girl)
// 标记引用
girl.refered()
}
}
}
}
for (let girl of newGirls) {
// 测试用例中的优先级数组,元素为 name
const preferences = girls[girl.name]
for (let preference of preferences) {
for (let boy of newBoys) {
// 根据 name 字符串匹配 Person 实例
if (boy.name === preference) {
girl.preferences.push(boy)
// 标记引用
boy.refered()
}
}
}
}
stableMarriage(newBoys)
const judgeList = []
const entriesPairings = Object.entries(pairings)
for (let entry of entriesPairings) {
for (let boy of newBoys) {
// 对比配偶是否与答案一致
if (boy.name === entry[0]) {
judgeList.push(boy.fiance.name === entry[1])
}
}
}
// 当所有配对都正确时返回 true
return judgeList.reduce((acc, cur) => acc && cur, true)
}
/******************************************************************************/
const referedCase = [
[
{
boys: { A: [], B: [] },
girls: { a: [], b: [] },
expectation: { boys: [], girls: [] },
},
{
boys: { A: [], B: [] },
girls: { a: [], b: ['A'] },
expectation: { boys: ['A'], girls: ['b'] },
},
{
boys: { A: [], B: ['a'] },
girls: { a: [], b: ['B'] },
expectation: { boys: ['B'], girls: ['a', 'b'] },
},
{
boys: { A: ['b'], B: ['b'] },
girls: { a: [], b: ['A'] },
expectation: { boys: ['A', 'B'], girls: ['b'] },
},
{
boys: { A: ['b'], B: ['b'] },
girls: { a: ['B'], b: ['A'] },
expectation: { boys: ['A', 'B'], girls: ['a', 'b'] },
},
],
]
/******************************************************************************/
const referedTest = ({ boys, girls, expectation }) => {
// 这俩是 Person 实例的数组
const newBoys = Object.keys(boys).map((name) => new Person(name))
const newGirls = Object.keys(girls).map((name) => new Person(name))
for (let boy of newBoys) {
// 测试用例中的优先级数组,元素为 name
const preferences = boys[boy.name]
for (let preference of preferences) {
for (let girl of newGirls) {
// 根据 name 字符串匹配 Person 实例
if (girl.name === preference) {
boy.preferences.push(girl)
// 标记引用
girl.refered()
}
}
}
}
for (let girl of newGirls) {
// 测试用例中的优先级数组,元素为 name
const preferences = girls[girl.name]
for (let preference of preferences) {
for (let boy of newBoys) {
// 根据 name 字符串匹配 Person 实例
if (boy.name === preference) {
girl.preferences.push(boy)
// 标记引用
boy.refered()
}
}
}
}
clearNotReferedPerson(newBoys)
clearNotReferedPerson(newGirls)
const judgeList = []
judgeList.push(
expectation.boys.join('') === `${newBoys.map((p) => p.name).join('')}`,
)
judgeList.push(
expectation.girls.join('') === `${newGirls.map((p) => p.name).join('')}`,
)
// 当所有配对都正确时返回 true
return judgeList.reduce((acc, cur) => acc && cur, true)
}
module.exports = {
commonCases,
asymmetricCases,
stableMarriageTest,
referedCase,
referedTest,
}