稳定匹配(解决婚姻问题)

15 篇文章 0 订阅

婚姻问题

现在有N位男生和N位女生,每个男生都对N个女生的喜欢程度做了排序,每个女生都对N个男生的喜欢程度做了排序,现在需要确定一个稳定的约会状态。

稳定的定义:如果男生i和女生a牵手,但男生i对女生b更喜欢,而女生b发现,相比自己的男朋友j,她更喜欢男生i,则没有力量阻碍男生i和女生b的私奔,这即是不稳定的。


解决思路

  • 首先选择一个单身男生,他会按照他的喜欢程度对一个还没有表白过的女生表白。
  • 如果女生此时处于单身状态,则恭喜,他们两人将进入约会状态。
  • 如果女生已经有男朋友,则女生会比较当前男朋友与表白的男生,如果更喜欢表白的男生,则恭喜,男生成功上位,女生之间的男朋友则进入单身状态;若女生还是更喜欢自己的男朋友,则不好意思,男生表白失败。
  • 当所有的男生都脱离单身状态时,此时的约会状态应是稳定的,证明如下。

若存在之前描述的不稳定因素,即虽然男生i和女生a牵手,但男生i对女生b更喜欢,而女生b发现,相比自己的男朋友j,她更喜欢男生i。既然男生i更喜欢女生b,那么男生i肯定在对女生b表白失败之后才会对女生a表白,而若男生i对女生b的表白失败,则女生b至少有比对男生i更喜欢的男朋友,所以女生b相比自己的男朋友j更喜欢男生i并不成立,所以此时所有的约会状态都是稳定的。


算法分析

首先构造People类,由其继承出来的有Man类和Woman类,它们都有的是setPrefer方法,设置自己的心爱对象排序列表。

Man类独有的是next方法,可以返回其心爱列表中最靠前的一个还没有表白过的心仪对象。

Woman类独有的是prefer方法,可以比较两个男生她更喜欢哪一个。

在存储结构上,可以分别选用两个数组存储所有的男生和女生,一个链表来存储所有单身的男生,一个数组来存储已有的配对情况。

主要算法步骤如下:

一. People类中有一个静态属性ID,会为每一个男生或女生赋予一个自增的ID,在创建男生或女生实例之前需要对其进行初始化。
二. 创建所有的男生和女生并设置其心爱顺序列表,初始化单身男生链表为所有男生,女生的男朋友数组每个元素都为空。
三. 当单身链表不为空则不终止循环。
四. 循环过程为:每次取一个单身的男生,将其从链表中删除,并给与其对还没有表白失败过的心仪女孩表白的权利。若其表白成功,则设置相应女生的男朋友为当前男生的ID,若相应女生之前的男朋友被绿,则将其男朋友插入到单身链表中;若其表白失败,则再将其插入到单身链表中。


源代码

测试数据为:

  • 男生表:
男生\偏爱排序
AdamBethAmyDianeEllenCara
BillDianeBethAmyCaraEllen
CarlBethEllenCaraDianeAmy
DanAmyDianeCaraBethEllen
EricBethDianeAmyEllenCara
  • 女生表:
女生\偏爱排序
AmyEricAdamBillDanCarl
BethCarlBillDanAdamEric
CaraBillCarlDanEricAdam
DianeAdamEricDanCarlBill
EllenDanBillEricCarlAdam

示例输出为:

Amy-Adam
Beth-Carl
Cara-Bill
Diane-Eric
Ellen-Dan
import java.util.ArrayList;
import java.util.LinkedList;

public class Match {
    public static void main(String[] args) {
        ArrayList<Man> men;
        ArrayList<Woman> women;
        LinkedList<Man> menTmp;
        Integer[] current;

        People.init();
        Man Adam = new Man("Adam");
        Man Bill = new Man("Bill");
        Man Carl = new Man("Carl");
        Man Dan = new Man("Dan");
        Man Eric = new Man("Eric");
        People.init();
        Woman Amy = new Woman("Amy");
        Woman Beth = new Woman("Beth");
        Woman Cara = new Woman("Cara");
        Woman Diane = new Woman("Diane");
        Woman Ellen = new Woman("Ellen");
        men = new ArrayList<>();
        men.add(Adam.setPrefers(Beth, Amy, Diane, Ellen, Cara));
        men.add(Bill.setPrefers(Diane, Beth, Amy, Cara, Ellen));
        men.add(Carl.setPrefers(Beth, Ellen, Cara, Diane, Amy));
        men.add(Dan.setPrefers(Amy, Diane, Cara, Beth, Ellen));
        men.add(Eric.setPrefers(Beth, Diane, Amy, Ellen, Cara));
        women = new ArrayList<>();
        women.add(Amy.setPrefers(Eric, Adam, Bill, Dan, Carl));
        women.add(Beth.setPrefers(Carl, Bill, Dan, Adam, Eric));
        women.add(Cara.setPrefers(Bill, Carl, Dan, Eric, Adam));
        women.add(Diane.setPrefers(Adam, Eric, Dan, Carl, Bill));
        women.add(Ellen.setPrefers(Dan, Bill, Eric, Carl, Adam));
        menTmp = new LinkedList<>();
        for (Man m : men) {
            menTmp.push(m);
        }
        current = new Integer[men.size()];
        for (int i = 0; i < men.size(); i++) {
            current[i] = null;
        }
        while (!menTmp.isEmpty()) {
            Man man = menTmp.pop();
            int prefer = man.next();
            if (prefer != -1) {
                if (current[prefer] == null) {
                    current[prefer] = man.getID();
                } else {
                    if (women.get(prefer).prefer(current[prefer], man.getID())) {
                        menTmp.push(men.get(current[prefer]));
                        current[prefer] = man.getID();
                    } else {
                        menTmp.push(man);
                    }
                }
            }
        }
        for (int i = 0; i < current.length; i++) {
            System.out.print(women.get(i));
            System.out.print("-");
            System.out.println(men.get(current[i]));
        }
    }
}

class People {
    private String name;
    protected int ID;
    protected static int curID = 0;

    public static void init() {
        curID = 0;
    }

    public People(String name) {
        this.name = name;
    }

    public int getID() {
        return ID;
    }

    @Override
    public String toString() {
        return name;
    }
}

class Man extends People {
    private int[] prefers;
    private int index;

    public Man(String name) {
        super(name);
        this.ID = curID;
        curID++;
    }

    public Man setPrefers(Woman... women) {
        prefers = new int[women.length];
        for (int i = 0; i < women.length; i++) {
            prefers[i] = women[i].getID();
        }
        index = 0;
        return this;
    }

    public int next() {
        if (index < prefers.length) {
            return prefers[index++];
        } else {
            return -1;
        }
    }
}

class Woman extends People {
    private int[] prefers;

    public Woman(String name) {
        super(name);
        this.ID = curID;
        curID++;
    }

    public Woman setPrefers(Man... men) {
        prefers = new int[men.length];
        for (int i = 0; i < men.length; i++) {
            prefers[men[i].getID()] = i;
        }
        return this;
    }

    public boolean prefer(int cur, int next) {
        return prefers[next] < prefers[cur];
    }
}

深入分析

首先这是一个男生向女生表白的思路,也即男生占主动位,女生占被动位。

虽然男生享有选择向不同女孩表白的权利,但是最终决定权在女方手中,即由女方决定是否接受男生的表白。而且,即使男生表白成功,也有被绿的可能,即当有女生更喜欢的男生向女生表白的时候,女生会毫不犹豫的抛弃他。

从大局来看,从一开始的不稳定约会,到最后的稳定婚姻的过程中,男生的伴侣总是从自己最喜欢的女生,慢慢地变成自己不怎么喜欢的女生,而女生的伴侣,却在一步步地变好,一步步地接近自己心目中的那个他。

所以这看起来好像是一个对女生有益的匹配策略。

但是更深入的分析之后,你会发现,虽然男生再一次次地被绿,但是他每次选择的女生都是有可能和他走到最后的女生中最好的一个,正是因为更好的女生会拒绝他的表白,所以最终和男生在一起的女生是可能和他在一起的女生中最好的一个。而对于女生来说,虽然她的男朋友在一步一步地变好,但是可能和她在一起的最好的那个男生,可能还没有轮到向她表白,就和他更喜欢的女生在一起了,所以并不能保证最终的伴侣是可能和她在一起的最好的那个男生。

所以这实际是一个对男生有益的匹配策略。同时也告诉了我们,努力争取总会比静心等待得来更好的结果。

以另一种思路思考哈,在N个可匹配对象中,如果你对最好的伴侣的期望值是N,对最差的期望值是1,以此类推,假设最终的匹配是你期望值为 N 2 \frac{N}{2} 2N的伴侣,令其中约会的持续时间平均为t,则男生所享受到的是: ∑ i = N 2 N i × t + N 2 × ∞ \sum_{i=\frac{N}{2}}^Ni×t+\frac{N}{2}×∞ i=2NNi×t+2N×;而女生享受到的是: ∑ 1 N 2 i × t + N 2 × ∞ \sum_{1}^{\frac{N}{2}}i×t+\frac{N}{2}×∞ 12Ni×t+2N×

哈?懂不懂由你。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值