c# 盖尔-沙普利算法的改进

盖尔-沙普利算法

“盖尔-沙普利算法”(the Gale-Shapley algorithm),也被称为“延迟接受算法”(deferred-acceptance algorithm),简称“GS算法”。是盖尔和沙普利为了寻找一个稳定匹配而设计出的市场机制。市场一方中的对象(医疗机构)向另一方中的对象(医学院学生)提出要约,每个学生会对自己接到的要约进行考虑,然后抓住自己青睐的(认为它是可接受的),拒绝其它的。该算法一个关键之处在于,合意的要约不会立即被接受,而只是被“抓住”(hold on to),也就是“延迟接受”。要约被拒绝后,医疗机构才可以向另一名医学院学生发出新的要约。整个程序一直持续到没有机构再希望发出新的要约为止,到那个时候,学生们才最终接受各自“抓住”的要约。

本文在此算法的基础上添加了一些其他要素以适应实际情况。

1. 添加个人得分系统,估分系统。结婚时双方的得分差距不能太大

2. 增加离婚难度

3. 控制群体的接触范围。即一个男性只能接触到一部分女性。

原始的盖尔-沙普利算法中,男性的满意度接近100,而女性的满意度则接近50。算法调整后双方满意度较为接近(调整部分参数后女性满意度甚至会高于男性)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//盖尔-沙普利[Gale-Shapley]婚姻稳定匹配算法
//有N人男性,M个女性,每个人有一个实际得分(考虑分布方式),个人估分,每个人对其他人都有一个估分
//每个人进行喜好排序,获得自己的追求名单(按照自己的自身估分加减一定的分值获得一个区间)。
//循环进行请求,女性按照自己的估分和对男性的估分接受或拒绝。
//直到每个人的追求名单结束
namespace GaleShapley
{
    class Program
    {
        static void Main(string[] args)
        {
            Marry marry = new Marry(200, 200);
            int number = marry.MaleDic.Count;
            marry.Start();
            Console.WriteLine("MaleSatisfaction : " + marry.MaleSatisfaction);
            Console.WriteLine("FeMaleSatisfaction : " + marry.FemaleSatisfaction);
            //Console.WriteLine("男性ID	得分	满意度	女性ID	得分	满意度");
            //foreach (Male male in marry.MaleDic.Values)
            //{
            //    if (!male.Marryed)
            //    {
            //        continue;
            //    }
            //    Female female = marry.FemaleDic[male.PartnerID];
            //    Console.WriteLine(male.ID + "	" + male.Point.ToMyString() + "	" + male.Satisfaction.ToMyString() + "	" + 
            //        female.ID + "	" + female.Point.ToMyString() + "	" + female.Satisfaction.ToMyString());
            //}
            //List<Male> maleList = (marry.MaleDic.Values.Where(p => !p.Marryed)).ToList<Male>();
            //List<Female> femaleList = (marry.FemaleDic.Values.Where(p => !p.Marryed)).ToList<Female>();
            //for (int i = 0; i < maleList.Count; i++)
            //{
            //    Male male = maleList[i];
            //    Female female = femaleList[i];
            //    Console.WriteLine(male.ID + "	" + male.Point.ToMyString() + "	" + male.Satisfaction.ToMyString() + "	" +
            //       female.ID + "	" + female.Point.ToMyString() + "	" + female.Satisfaction.ToMyString());
            //}
            Console.ReadLine();
        }
    }
    public static class DoubleExtendMethod
    {
        public static string ToMyString(this double num)
        {
            return num.ToString("0.##");
        }
    }
    /// <summary>
    /// 请求对象
    /// </summary>
    public class RequestObj
    {
        /// <summary>
        /// 对象编号
        /// </summary>
        public int ID { get; private set; }
        /// <summary>
        /// 对象在自己心目中的估分
        /// </summary>
        public double EstimatePoint { get; private set; }

        public RequestObj(int id, double estimatePoint)
        {
            this.ID = id;
            this.EstimatePoint = estimatePoint;
        }
    }
    public class People
    {
        private static double MaxPoint = 1000;
        private static double MinPoint = 200;
        /// <summary>
        /// 个人编号
        /// </summary>
        public int ID { get; set; }
        /// <summary>
        /// 配偶编号
        /// </summary>
        public int PartnerID { get; set; }
        public bool Marryed
        {
            get
            {
                return this.PartnerID < 0 ? false : true;
            }
        }
        /// <summary>
        /// 实际得分
        /// </summary>
        public double Point { get; set; }
        /// <summary>
        /// 个人估分
        /// </summary>
        public double MyEstimatePoint { get; set; }
        /// <summary>
        /// 配偶得分
        /// </summary>
        public double PartnerEstimatePoint { get; set; }
        /// <summary>
        /// 满意度
        /// </summary>
        public double Satisfaction
        {
            get
            {
                if (!this.Marryed)
                {
                    return 0;
                }
                //很难找到合适的计算满意度的方法
                double mul = Math.Abs(this.MyEstimatePoint - this.PartnerEstimatePoint) / People.MaxPoint;
                if (this.MyEstimatePoint > this.PartnerEstimatePoint)
                {
                    return 50.0 * (1 - mul);
                }
                else
                {
                    return 50.0 * (1 + mul);
                }
            }
        }
        public People(int id)
        {
            this.PartnerID = -1;
            this.ID = id;
            this.Point = Marry.Rnd.NextDouble() * (People.MaxPoint - People.MinPoint) + People.MinPoint;   //个人得分在0-1000之间,平均分布
            this.MyEstimatePoint = People.GetEstimatePoint(this.Point);
        }

        /// <summary>
        /// 估分系统
        /// </summary>
        /// <param name="point">实际得分</param>
        /// <returns>估分</returns>
        public static double GetEstimatePoint(double point)
        {
            //return point;
            double mul = 0.8 + Marry.Rnd.NextDouble() * 0.4;    //控制估分在80% - 120% 之间
            return point * mul;
        }
    }
    public class Male : People
    {
        public List<RequestObj> RequestList { get; set; }
        public Male(int id)
            : base(id)
        {

        }
        public void InitRequestList(Dictionary<int, Female> femaleDic)
        {
            this.RequestList = new List<RequestObj>();
            foreach (Female female in femaleDic.Values)
            {
                if (Marry.Rnd.Next(10) != 1)//控制此人可以接触到的女性人数,目前所有男性的范围相同,可以由个人的交际能力代替
                {
                    continue;
                }
                double point = People.GetEstimatePoint(female.Point);//对对方评分
                if (point > this.MyEstimatePoint)
                {
                    double mul = (point - this.MyEstimatePoint) / this.MyEstimatePoint;
                    if (mul < 0.2)
                    {
                        this.RequestList.Add(new RequestObj(female.ID, point));
                    }
                }
                else
                {
                    double mul = (this.MyEstimatePoint - point) / this.MyEstimatePoint;
                    if (mul < 0.2)
                    {
                        this.RequestList.Add(new RequestObj(female.ID, point));
                    }
                }
            }
            this.RequestList = this.RequestList.OrderByDescending(a => a.EstimatePoint).ToList<RequestObj>();//降序
        }

        /// <summary>
        /// 求婚
        /// </summary>
        /// <param name="maleDic"></param>
        /// <param name="femaleDic"></param>
        public void Request(Dictionary<int, Male> maleDic, Dictionary<int, Female> femaleDic)
        {
            if (this.Marryed)
            {
                return;
            }
            if (this.RequestList.Count == 0)
            {
                return;
            }
            Female female = femaleDic[this.RequestList[0].ID];
            if (female.BeRequest(this, maleDic))
            {
                this.PartnerID = female.ID;
                this.PartnerEstimatePoint = this.RequestList[0].EstimatePoint;
            }
            this.RequestList.RemoveAt(0);
        }

        /// <summary>
        /// 离婚
        /// </summary>
        public void Divorce()
        {
            this.PartnerID = -1;
            this.PartnerEstimatePoint = 0;
        }
    }
    public class Female : People
    {
        public Female(int id)
            : base(id)
        {

        }

        public bool BeRequest(Male male, Dictionary<int, Male> maleDic)
        {
            double estimatePoint = People.GetEstimatePoint(male.Point);//先评分
            if (this.Marryed)//和配偶比较
            {
                if (this.PartnerEstimatePoint < estimatePoint)
                {
                    double difference = estimatePoint / this.PartnerEstimatePoint;
                    if (difference > 1.5)
                    {
                        maleDic[this.PartnerID].Divorce();
                        this.PartnerID = male.ID;
                        this.PartnerEstimatePoint = estimatePoint;
                        return true;
                    }
                }
                return false;
            }
            else//未婚
            {
                if (estimatePoint > (this.MyEstimatePoint * 0.8))
                {
                    this.PartnerID = male.ID;
                    this.PartnerEstimatePoint = estimatePoint;
                    return true;
                }
                return false;
            }
        }
    }
    public class Marry
    {
        /// <summary>
        /// 全局使用的随机数
        /// </summary>
        public static Random Rnd = new Random();
        public Dictionary<int, Male> MaleDic { get; set; }
        public Dictionary<int, Female> FemaleDic { get; set; }
        /// <summary>
        /// 结婚数
        /// </summary>
        public int MarriageCount
        {
            get
            {
                int count = 0;
                foreach (Male male in this.MaleDic.Values)
                {
                    if (male.Marryed)
                    {
                        count++;
                    }
                }
                return count;
            }
        }
        /// <summary>
        /// 单身人数
        /// </summary>
        public int SingleCount
        {
            get
            {
                return this.MaleDic.Count + this.FemaleDic.Count - this.MarriageCount * 2;
            }
        }
        public double MaleSatisfaction
        {
            get
            {
                double satisfaction = 0;
                foreach (Male male in this.MaleDic.Values)
                {
                    satisfaction += male.Satisfaction;
                }
                return satisfaction / this.MaleDic.Count;
            }
        }
        public double FemaleSatisfaction
        {
            get
            {
                double satisfaction = 0;
                foreach (Female female in this.FemaleDic.Values)
                {
                    satisfaction += female.Satisfaction;
                }
                return satisfaction / this.FemaleDic.Count;
            }
        }
        /// <summary>
        /// 需要继续匹配
        /// </summary>
        public bool NeedMatch
        {
            get
            {
                foreach (Male male in this.MaleDic.Values)
                {
                    if (male.RequestList.Count > 0 && !male.Marryed)
                    {
                        return true;
                    }
                }
                return false;
            }
        }
        public Marry(int maleNum, int femaleNum)
        {
            this.MaleDic = new Dictionary<int, Male>();
            this.FemaleDic = new Dictionary<int, Female>();
            for (int i = 0; i < maleNum; i++)
            {
                MaleDic.Add(i, new Male(i));
            }
            for (int i = 0; i < femaleNum; i++)
            {
                FemaleDic.Add(i, new Female(i));
            }
            foreach (Male male in this.MaleDic.Values)
            {
                male.InitRequestList(this.FemaleDic);
            }
        }
        public void Start()
        {
            while (this.NeedMatch)
            {
                foreach (Male male in this.MaleDic.Values)
                {
                    male.Request(this.MaleDic, this.FemaleDic);
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/aitong/p/10973774.html