假如你是一个媒人,有若干个单身男子登门求助,还有同样多的单身女子也前来征婚。如果你已经知道这些女孩在每个男人心目中的排名,以及男孩们在每个女孩心中的排名(1),你应该怎样为他们牵线配对呢?
最好的配对方案当然是,每个人的另一半正好都是自己的“第一选择”。这虽然很完美,但绝大多数情况下都不可能实现。比方说,男 1 号的最爱是女 1 号,而女 1 号的最爱不是男 1 号,这两个人的最佳选择就不可能被同时满足。如果出现了好几个男人的最爱都是同一个女孩儿的情况,这几个男人的首选也不会同时得到满足。当这种最为理想的配对方案无法实现时,怎样的配对方案才能令人满意呢?
其实,找的对象太完美不见得是个好事儿,和谐才是婚姻的关键。如果男 1 号和女 1 号各自有各自的对象,但男 1 号觉得,比起自己现在的对象,女 1 号更好一些;女 1 号也发现,在自己心目中,男 1 号的排名比现男友更靠前一些。这样一来,这两人就可能会发生外遇,最后扔下各自现在的对象,一起私奔了——因为这个结果对他们两人都更好一些。在一种男女配对的方案中,如果出现了这种情况,我们就说婚姻搭配是不稳定的。作为一个红娘,你深深地知道,对象介绍得不好没有关系,就怕婚姻关系不稳定。给客户牵线配对时,虽然不能让每个人都得到最合适的,但婚姻搭配必须得是稳定的。换句话说,对于每一个人,在他心目中比他当前的伴侣更好的异性,都不会认为他也是一个更好的选择。现在,我们的问题就是:稳定的婚姻搭配总是存在吗?应该怎样寻找出一个稳定的婚姻搭配?
为了便于分析,我们下面做一些约定。我们用字母 A 、 B 、 C 对男性进行编号,用数字 1 、 2 、 3 对女性进行编号。我们把所有男性从上到下列在左侧,括号里的数字表示每个人心目中对所有女性的排名;再把所有女性列在右侧,用括号里的字母表示她们对男性的偏好。图 0-1 所示的就是有 2 男 2 女的一种情形,每个男的都更喜欢女 1 号,但女 1 号更喜欢男 B ,女 2 号更喜欢男 A 。若按 A-1 、 B-2 进行搭配,则男 B 和女 1 都更喜欢对方一些,这样的婚姻搭配就是不稳定的。但若换一种搭配方案(如图 0-2 ),这样的搭配就是稳定的了。
图 0-1 一个不稳定的婚姻搭配 男 B 和女 1 都不满意现任伴侣
图 0-2 一个稳定的婚姻搭配
可能很多人会立即想到一种寻找稳定婚姻搭配的策略:不断修补当前搭配方案。如果两个人互相之间都觉得对方比自己当前的伴侣更好,就让这两个人成为一对,剩下被甩的那两个人组成一对。如果还有想要私奔的男女对,就继续按照他们的愿望对换情侣,直到最终消除所有的不稳定组合。容易看出,应用这种“修补策略”所得到的最终结果一定满足婚姻的稳定性,但这种策略的问题就在于,它不一定有一个“最终结果”。事实上,按照上述方法反复调整搭配方案,最终有可能会陷入一个死循环,因此该策略甚至不能保证得出一个确定的方案来。
图 0-3 应用“修补策略”可能会产生死循环
1962 年,美国数学家 David Gale 和 Lloyd Shapley 发明了一种寻找稳定婚姻的策略。不管男女各有多少人,不管他们各自的偏好如何,应用这种策略后总能得到一个稳定的婚姻搭配。换句话说,他们证明了稳定的婚姻搭配总是存在的。有趣的是,这种策略反映了现实生活中的很多真实情况。
在这种策略中,男人将一轮一轮地去追求他中意的女子,女子可以选择接受或者拒绝她的追求者。第一轮,每个男人都选择自己名单上排在首位的女人,并向她表白。此时,一个女孩儿可能面对的情况有三种:没有人跟她表白,只有一个人跟她表白,有不止一个人跟她表白。在第一种情况下,这个女孩儿什么都不用做,只需要继续等待;在第二种情况下,接受那个人的表白,答应暂时和他做男女朋友;在第三种情况下,从所有追求者中选择自己最中意的那一位,答应和他暂时做男女朋友,并拒绝其他所有的追求者。
第一轮结束后,有些男人已经有女朋友了,有些男人仍然是单身。在第二轮追女行动中,每个单身男都从所有还没拒绝过他的女孩中选出自己最中意的那一个,并向她表白,不管她现在是否是单身。和第一轮一样,女孩儿们需要从表白者中选择最中意的一位,拒绝其他追求者。注意,如果这个女孩儿已经有男朋友了,当她遇到了更好的追求者时,她必须拒绝掉现在的男友,投向新的追求者的怀抱。这样,一些单身男人将会得到女友,那些已经有了女友的人也可能会被甩掉,重新变成光棍。在以后的每一轮中,单身的男人继续追求列表中的下一个女孩儿,女孩儿则从包括现男友在内的所有追求者中选择最好的一个,并对其他人说不。这样一轮一轮地进行下去,直到某个时候所有人都不再单身,下一轮将不会有任何新的表白发生,整个过程自动结束。此时的婚姻搭配就一定是稳定的了。
图 0-4 应用上述策略,三轮之后将得出稳定的婚姻搭配
//代码实现 使用堆栈区记录单身的男士 知道栈中为空 匹配结束
import java.util.Stack;
public class 婚姻搭配 {
/**
* @param args
*/
static int NUM=4;
public static void main(String[] args) {
// TODO Auto-generated method stub
int manArray[][] ={{2,3,1,0},{2,1,3,0},{0,2,3,1},{1,3,2,0}};
int ladayArray[][] = {{0,3,2,1},{0,1,2,3},{0,2,3,1},{1,0,3,2}};
int manPerfer[]=new int[NUM];//每位男生选中的女生;
int manStartPos[]=new int[NUM];//记录每位男生选中的是心目中第几位女生
int ladayNow[]=new int[NUM];//女生对应的男生;
//吧ladayNow 标记为-1 表示还没有人对她们表白
for(int i=0;i<NUM;i++){
ladayNow[i]=-1;
}
Stack<Integer> stack=new Stack<Integer>();//还处于单身的男士
//进行第一轮迭代 每个男士都选择自己名单上排在首位的女生
for(int i=0;i<NUM;i++){
ChoosePartener(stack,i,manArray,ladayArray,manPerfer,manStartPos,ladayNow);
}
//继续迭代 当堆栈为空时 结束
while(stack.size()!=0){
int manPos = stack.pop();
ChoosePartener(stack,manPos,manArray,ladayArray,manPerfer,manStartPos,ladayNow);
}
for(int i=0;i<NUM;i++){
System.out.println(i+"-->"+manPerfer[i]);
}
}
private static void ChoosePartener(Stack<Integer> stack, int manPos,
int[][] manArray, int[][] ladayArray, int[] manPerfer,
int[] manStartPos, int[] ladayNow) {
// TODO Auto-generated method stub
//选中自己名单上首个女生 并表白
int perferLaday =manArray[manPos][manStartPos[manPos]];
//如果该女孩没有被表白
if(ladayNow[perferLaday]==-1){
//接受男士的表白
ladayNow[perferLaday]=manPos;
manPerfer[manPos]=perferLaday;
}
//如果已经有人表白了 继续表白 判断女生觉得你是否比他现任还符合自己的心意
else{
//获取女方现任在心中的排名
int oldPos =GetPositionFromLaday(ladayArray,perferLaday,ladayNow[perferLaday]);
//获取当前表白男生在女生心中的排名
int newPos =GetPositionFromLaday(ladayArray,perferLaday,manPos);
if(oldPos<newPos){//表明女生对现任男生更满意
manStartPos[manPos]++;//说明该女生更喜欢现在拥有的,只有选取心目中下一位了,选心目中第二位
//加入单身行列
stack.push(manPos);
}else{
//换男朋友
//被甩男友恢复自由身 被甩男士只有选取心目中下一位女生
manStartPos[ladayNow[perferLaday]]++;
//加入单身行列
stack.push(ladayNow[perferLaday]);
//将表白男生改为现任
ladayNow[perferLaday]=manPos;
manPerfer[manPos]=perferLaday;
}
}
}
private static int GetPositionFromLaday(int[][] ladayArray,
int perferLaday, int man) {
// TODO Auto-generated method stub
for(int i=0;i<NUM;i++){
if(ladayArray[perferLaday][i]==man){
return i;
}
}
return -1;
}
}