Table of Contents:
匹配市场#
匹配市场讨论一群卖方和一群同样数量买方的一一匹配问题,旨在找到社会最优的匹配方式。买家对每个卖家有自己的估值,形成估值矩阵。通过调整卖家的价格至市场清仓价,买方可以得到与估值差价最大的商品,不存在冲突。
这里的“社会最优”指的是所有人对买到的商品的估值总和最大。清仓价格是不唯一的,算法设计部分中的失败案例中会提到案例。
算法设计#
一一对应算法(失败)#
书上介绍的匹配市场算法需要先找到受限买家集合,即中的买家偏好的商品集合满足。
开始实现了一个版本,试图找到价格更新后能够一一对应的结果,即试图让每个商品只有一个人偏好。代码如下:
import numpy as np |
但这种算法在课上的3*3的例子是有效的,能够得到与的解,其估值之和和的一样,都是社会最优解。
但遇到比如买家a和b的估值一致时,他们的偏好永远一致,不可能出现算法终止点,因此这个方法行不通,还是要回归受限集的算法。
受限集+匈牙利算法#
算法设计如下:
- 估值矩阵减去价格得到买家-卖家的效益矩阵
profit
,对每个买家,找到效益最大的卖家将两者连线。 - 对步骤1得到的二部图找最大匹配
- 如果匹配数=n,则找到了清仓价格和一一对应的匹配方式,算法结束。
- 如果匹配数<n,则找到卖家受限集,将它们价格+1,返回步骤1。
具体实现主要有三个部分:
- 主体循环结构
- 二部图最大匹配
- 受限集价格更新
主体部分#
得到prefer
这个表示二部图关系的01矩阵,用于最大匹配算法计算匹配成功与否。
while True: |
二部图最大匹配:匈牙利算法#
给定二部图判断最大匹配数量,这里使用的是匈牙利算法的递归实现。
算法思路是依次为每个卖家匹配买家,如果匹配到的是已有配对的,则递归回溯已配对路径,看是否调整匹配以新增一对。
def match(i, n): |
受限集价格更新#
给定一个已匹配的卖家,心仪它的买家中有未匹配上的,则该卖家是受限集的一员,需要涨价。
def add_price(n, p): |
实验结果#
-
test14.txt
清仓价格: [ 5. 5. 5. 0. 3. 0. 5. 5. 10. 10. 15. 3. 15. 10.]
每个买家的商品选择: [ 7. 10. 0. 3. 2. 13. 4. 6. 8. 5. 1. 12. 9. 11.]
社会最优: 500 -
·
test24.txt
清仓价格: [ 0. 6. 31. 5. 1. 33. 22. 5. 7. 5. 5. 10. 23. 38. 8. 7. 5. 12.
6. 12. 8. 22. 0. 0.]
每个买家的商品选择: [ 8. 9. 4. 15. 23. 20. 1. 14. 18. 17. 12. 21. 5. 13. 7. 3. 2. 0. 16. 6. 22. 10. 19. 11.]
社会最优: 911 -
test26.txt
清仓价格: [11. 6. 9. 10. 19. 1. 11. 6. 6. 6. 0. 3. 0. 6. 0. 0. 6. 11.
0. 8. 8. 8. 3. 0. 3. 0.]
每个买家的商品选择: [21. 20. 8. 11. 24. 10. 3. 0. 1. 17. 4. 2. 13. 25. 22. 19. 23. 18. 16. 9. 6. 5. 7. 15. 14. 12.]
社会最优: 936 -
test31.txt
清仓价格: [ 0. 16. 2. 20. 50. 0. 43. 32. 31. 10. 6. 6. 6. 20. 7. 0. 6. 0.
10. 53. 0. 6. 0. 0. 25. 1. 10. 1. 2. 2. 22.]
每个买家的商品选择: [28. 6. 4. 24. 9. 26. 23. 14. 19. 21. 0. 15. 8. 3. 7. 11. 27. 5. 10. 12. 17. 25. 20. 22. 2. 16. 1. 13. 30. 18. 29.]
社会最优: 1387
实验心得#
-
其实python里
from scipy.optimize import linear_sum_assignment
可以三行写完代码。感觉纯调包不太好就实现了一遍匈牙利算法,没想到把矩阵的ij写反这么一个小bug却de了好久,有点哭笑不得。 -
有一个问题没有想到答案,假设已知匹配结果,可以倒过来推出一组合理的清仓价格吗?
-
没有找到能直观解释全部清仓价格和商品选择的方法,不过找清仓价格的过程还是符合经济学概念的。比如受限集的思想本质上是「物以稀为贵」,根据供需关系市场进行了价格调整,从而价格上涨的商品需求就可能变少,更有利于分散分配。