匹配算法——相亲男女匹配

时间:20210928

背景:有个相亲活动,需要暗地里给男女进行匹配,毕竟明面上直接说不喜欢哪个异性总是尴尬的。匹配的话,方法众多,并不能让每个人都满意,根据各自的意向,总能计算整体意向都不错的。


太长了不看,直接操作:

线下让N对男女:

  1. 写个小纸条,各自给N个异性排序,更喜欢的排在前面
  2. 得到:
    1. 女生的选择:womanChoices
      1. 女1:男2,男5,男1,....
      2. 女2:...
      3. ...
      4. 女N:...
    2. 男生的选择:manChoices
      1. 同理

操作:

  1. 进入网站:代码在线运行 - 在线工具
  2. 选择python
    1. 不要用python3
  3. 代码复制进去
  4. 修改代码里的两个变量:womanChoices、manChoices
    1. 将之前线下统计得到的,放在对应位置

  5. 点击“执行Run”
  6. 右侧出结果即可


 

算法:GS算法(盖尔-沙普利算法(Gale-Shapley algorithm))

算法思想:略

算法伪代码:

0ada99de85afd017f4e27d298fba153b.png


样例解释:

1、1对男女,不考虑

2、两对男女,分别优秀男1、一般男2;优秀女1、一般女2(“优秀”、“普通”方便理解,换成“青菜”、“萝卜”一个意思)

1.0、优秀女、男互相看中,普通女、男互相看中

  • 女1喜欢:男1>男2
  • 女2喜欢:男2>男1
  • 男1喜欢:女1>女2
  • 男2喜欢:女2>女1
  • 结果:男1——女1、男2——女2分别匹配
  • 解释:萝卜青菜各有所爱,分别各自找到自己最中意的

2.1.1、两女争优秀男,两男争优秀女

  • 女1喜欢:男1>男2
  • 女2喜欢:男1>男2
  • 男1喜欢:女1>女2
  • 男2喜欢:女1>女2
  • 结果:优秀男1——优秀女1、一般男2——一般女2
  • 解释:优者则优

2.2.1、两女争优秀男,两男争普通女

  • 女1喜欢:男1>男2
  • 女2喜欢:男1>男2
  • 男1喜欢:女2>女1
  • 男2喜欢:女2>女1
  • 结果:优秀男1——普通女2、普通男2——优秀女1
  • 解释:
    • 两男争普通女,该普通女不普通,优先级高,是个“优秀女”,优秀男1同理,故两个被相争的在一起
    • 该情况和上面一种“两女争优秀男,两男争优秀女”是类似

2.1.2、两女争普通男,两男争普通女:换身份,转换为“两女争优秀男,两男争优秀女”情况一样,

2.2.2、两女争普通男,两男争优质女:换身份,转换为“两女争优秀男,两男争普通女”情况一样

3.1.1、两女争优秀男,优秀男争优秀女、普通男争普通女

  • 女1喜欢:男1>男2
  • 女2喜欢:男1>男2
  • 男1喜欢:女1>女2
  • 男2喜欢:女2>女1
  • 结果:优秀男1——优秀女1、一般男2——一般女2
  • 解释:普通男1没被优秀女1优先选。

3.2.1、两女争优秀男,优秀男争普通女、普通男争优秀女

  • 结果:优秀男1——普通女2、普通男2——优秀女1
  • 解释:和3.1.1情况类似,优秀男权重更高(被两女相争)

3.1.2、两女争普通男,普通男争优秀女、优秀男争普通女

  • 解释:和3.1.1情况类似,普通男、优秀男身份互换即可

3.2.2、两女争普通男,普通男争普通女、优秀男争优秀女

  • 解释:和3.2.1情况类似,普通男、优秀男身份互换;

4、四角恋:女1->男1->女2->男2->女1

  • 女1喜欢:男1>男2
  • 女2喜欢:男2>男1
  • 男1喜欢:女2>女1
  • 男2喜欢:女1>女2
  • 结果:
    • 女士优先时:女1——男1、女2——男2
    • 男士优先时:女2——男1、女1——男2

代码:

# coding:utf-8
"""
Demo of Gale-Shapley stable marriage algorithm.

Usage is:
   python marriage.py  [menfile]  [womenfile]

or

   python marriage.py  [menfile]  [womenfile]  V

for verbose mode.

For simplicity, the file format is assumed (without checking) to match
the following format:

  bob:     alice,carol
  david:   carol,alice

and likewise for the womenfile, where there should be precisely same
number of men as with women, and the identifiers should be
self-consistent between the two files.
"""
import sys

# 原作者的例子:
womanChoices = '''
W:       D,B,C,A
X:       D,B,A,C
Y:       A,D,B,C
Z:       B,A,D,C
'''
manChoices = '''
A:      W,X,Z,Y
B:      W,Y,Z,X
C:      X,W,Y,Z
D:      W,Z,X,Y
'''

# 1.0、优秀女、男互相看中,普通女、男互相看中
womanChoices = '''
女1:男1,男2
女2:男2,男1
'''
manChoices = '''
男1:女1,女2
男2:女2,女1
'''

# 2.1.1、两优秀女争两优秀男、两优秀男争两优秀女
womanChoices = '''
女1:男1,男2
女2:男1,男2
'''
manChoices = '''
男1:女1,女2
男2:女1,女2
'''

# 3.1.1、两女争优秀男,优秀男争优秀女、普通男争普通女
womanChoices = '''
女1:男1,男2
女2:男1,男2
'''
manChoices = '''
男1:女1,女2
男2:女2,女1
'''

# 4、四角恋:女1->男1->女2->男2->女1
womanChoices = '''
女1:男1,男2
女2:男2,男1
'''
manChoices = '''
男1:女2,女1
男2:女1,女2
'''
# ---------------------------------------------------------------------------------------
# 现场活动男女们的选择:(格式类似上面的,一人一行,冒号、逗号注意)
# 女生
womanChoices = '''

'''

# 男生选择的结果:放这里
manChoices = '''

'''
# ---------------------------------------------------------------------------------------

class Person:
    """
    Represent a generic person
    """

    def __init__(self, name, priorities):
        """
        name is a string which uniquely identifies this person

        priorities is a list of strings which specifies a ranking of all
          potential partners, from best to worst
        """
        self.name = name
        self.priorities = priorities
        self.partner = None

    def __repr__(self):
        return 'Name is ' + self.name + '\n' + \
               'Partner is currently ' + str(self.partner) + '\n' + \
               'priority list is ' + str(self.priorities)


class Man(Person):
    """
    Represents a man
    """

    def __init__(self, name, priorities):
        """
        name is a string which uniquely identifies this person

        priorities is a list of strings which specifies a ranking of all
          potential partners, from best to worst
        """
        Person.__init__(self, name, priorities)
        self.proposalIndex = 0                   # next person in our list to whom we might propose

    def nextProposal(self):
        goal = self.priorities[self.proposalIndex]
        self.proposalIndex += 1
        return goal

    def __repr__(self):
        return Person.__repr__(self) + '\n' + \
            'next proposal would be to ' + self.priorities[self.proposalIndex]


class Woman(Person):
    """
    Represents a woman
    """

    def __init__(self, name, priorities):
        """
        name is a string which uniquely identifies this person

        priorities is a list of strings which specifies a ranking of all
          potential partners, from best to worst
        """
        Person.__init__(self, name, priorities)

        # now compute a reverse lookup for efficient candidate rating
        self.ranking = {}
        for rank in range(len(priorities)):
            self.ranking[priorities[rank]] = rank

    def evaluateProposal(self, suitor):
        """
        Evaluates a proposal, though does not enact it.

        suitor is the string identifier for the man who is proposing

        returns True if proposal should be accepted, False otherwise
        """
        return self.partner == None or self.ranking[suitor] < self.ranking[self.partner]


def parseFileOld(filename):
    """
    Returns a list of (name,priority) pairs.
    """
    people = []
    f = file(filename)
    for line in f:
        pieces = line.split(':')
        name = pieces[0].strip()
        if name:
            priorities = pieces[1].strip().split(',')
            for i in range(len(priorities)):
                priorities[i] = priorities[i].strip()
            people.append((name, priorities))
    f.close()
    return people


def parseFile(choices):
    choices = choices.replace(':', ':')
    choices = choices.replace(',', ',')
    people = []
    for line in choices.split('\n'):
        pieces = line.split(':')
        name = pieces[0].strip()
        if name:
            priorities = pieces[1].strip().split(',')
            for i in range(len(priorities)):
                priorities[i] = priorities[i].strip()
            people.append((name, priorities))
    return people


def printPairings(men):
    for man in men.values():
        print man.name, 'is paired with', str(man.partner)


def main_old():
    verbose = len(sys.argv) > 3

    # initialize dictionary of men
    menlist = parseFile(sys.argv[1])
    men = dict()
    for person in menlist:
        men[person[0]] = Man(person[0], person[1])
    unwedMen = men.keys()

    # initialize dictionary of women
    womenlist = parseFile(sys.argv[2])
    women = dict()
    for person in womenlist:
        women[person[0]] = Woman(person[0], person[1])

    ############################### the real algorithm ##################################
    while unwedMen:
        m = men[unwedMen[0]]             # pick arbitrary unwed man
        w = women[m.nextProposal()]      # identify highest-rank woman to which
        #    m has not yet proposed
        if verbose:
            print m.name, 'proposes to', w.name

        if w.evaluateProposal(m.name):
            if verbose:
                print '  ', w.name, 'accepts the proposal'

            if w.partner:
                # previous partner is getting dumped
                mOld = men[w.partner]
                mOld.partner = None
                unwedMen.append(mOld.name)

            unwedMen.remove(m.name)
            w.partner = m.name
            m.partner = w.name
        else:
            if verbose:
                print '  ', w.name, 'rejects the proposal'

        if verbose:
            print "Tentative Pairings are as follows:"
            printPairings(men)
            print

    # we should be done
    print "Final Pairings are as follows:"
    printPairings(men)


def main(manChoices, WomanChoices):
    # initialize dictionary of men
    menlist = parseFile(manChoices)
    men = dict()
    for person in menlist:
        men[person[0]] = Man(person[0], person[1])
    unwedMen = men.keys()

    # initialize dictionary of women
    womenlist = parseFile(WomanChoices)
    women = dict()
    for person in womenlist:
        women[person[0]] = Woman(person[0], person[1])

    ############################### the real algorithm ##################################
    while unwedMen:
        m = men[unwedMen[0]]             # pick arbitrary unwed man
        w = women[m.nextProposal()]      # identify highest-rank woman to which
        #    m has not yet proposed
        if w.evaluateProposal(m.name):
            if w.partner:
                # previous partner is getting dumped
                mOld = men[w.partner]
                mOld.partner = None
                unwedMen.append(mOld.name)

            unwedMen.remove(m.name)
            w.partner = m.name
            m.partner = w.name
    #####################################################################################

    # we should be done
    print "Final Pairings are as follows:"
    printPairings(men)


if __name__ == "__main__":
    # 女士优先,womanChoices放前面, 否则放后面
    main(womanChoices, manChoices)

作者源代码:


参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值