zoukankan      html  css  js  c++  java
  • 学生积分派位算法研究

    • 场景描述

                  假设条件
                 1、schoolCount = 20学校志愿
                 2、placeCount = 2000个学位
                 3、personCount = 4000个学生报名
                 4、按照积分从高到低录取
                 5、接受调剂的则由系统调剂,不接受调剂的落选
                 6、选中误差<= 20%,即 2000 * 20% = 400 属于personCount - placeCount
                 7、设定可报志愿为N个
                 问题:当N为多少时,分数从高到低的前placeCount个学生中落选比例小于20%,并处于基本稳定状态

    • 建模分析

                 志愿命中概率因素有5个
                 1、可填报的志愿数
                 2、是否接受调剂
                 3、学生的分数
                 4、填报志愿的顺序
                 5、各个学校的招收人数


                 1)接受调剂
                     1、当前N=placeCount都接受调剂时, 都可以上
                     2、当前N=placeCount部分不接受调剂时,则可能存在高分落选的学生   
                  2)    
                     1、当N = placeCount, 则
                           a)当前placeCount学生都全部填写20个志愿时,则积分前placeCount名的学生都可以上
                           b)当前2000名的学生中部分人填写志愿错误或未全部填写,则可能存在部分人落选,后面的2000名学生存在很小的机会
                     2、当N < 20,则
                           a)当前2000名学生的每个人都接受调剂时,则积分前2000名的学生都可以上,后面2000名学生没有任何概率机会
                           b)当前2000名学生的部分不调剂,则积分前2000名的学生则可能部分不能上

                  分析结果:
                           分数高的学生落选跟志愿数N没有决定关系,跟填报的学校顺序有关,跟是否接受调剂有关,若接受调剂,按照分数的高低,高分的学生必然优先上
                           志愿数N跟学生对多个学校组合优先有关,同时只有在不选择调剂的情况下,N的值越大(即学生可填写的志愿越多)分数高的学生越能保证上线,即当N=schoolCount时相当于接受调剂,但调剂的学校顺序未必与学生的设定的学校顺序一致

    • 数据建表
    /*学生表*/
    CREATE TABLE [bm_person](
    	[personId] [int] NOT NULL,
    	[personNo] [varchar](50) NULL, /*学生号*/
    	[personName] [varchar](50) NULL,/*学生名称*/
    	[point] [int] NULL,/*学生分数*/
    	[schoolId] [int] NULL,/*被分配的学校*/
    	[isAdjust] [int] NULL,/*是否调剂*/
    	[isLose] [int] NULL/*是否高分落选*/
    )
    go
    
    /*产生随机数表,用于初始化使用*/
    CREATE TABLE [bm_random](
        [id] [int] NOT NULL,
        [random] [int] NULL,
        [isUsed] [int] NULL
    )
    go
    
    /*报考学校*/
    CREATE TABLE [bm_school](
        [schoolId] [int] NOT NULL,
        [schoolName] [varchar](200) NULL, /*学校名称*/
        [placeCount] [int] NULL,/*学校招收人数*/
        [residue] [int] NULL/*学校剩余招收人数*/
    )
    go
    
    /*学生填报志愿表*/
    CREATE TABLE [bm_wish](
        [wishId] [int] NOT NULL,
        [personId] [int] NULL, /*学生id*/
        [schoolId] [int] NULL,/*学生填报学校*/
        [priority] [int] NULL/*学校志愿优先顺序*/
    )
    GO
    
    • 数据初始化
    /*-------------------------------学校和学生数据初始化-------------------------------------------------------*/ 
    由于学校和学生的数据比较多,请自行随机生成
    本人的学校测试数据为20个学校,学生测试数据为4000
    
    ---------------------------------随机初始化学校招生人数数据--------------------------------------------
     DECLARE  @COUNT INT = 0;
     DECLARE @loopId INT = 1;
     DECLARE @schoolId  INT = 0;
     
     SELECT @COUNT = COUNT(1)
     FROM bm_school;
     
     SELECT ID = IDENTITY(INT, 1,1),CONVERT(VARCHAR(50),schoolId) schoolId INTO #aa 
     FROM bm_school
     
     while (@loopId <= @COUNT)
     BEGIN
        SELECT @schoolId = schoolId
        FROM #aa
        WHERE id = @loopId
        
        UPDATE bm_school
        SET placeCount = rand() * 250
        WHERE schoolId = @schoolId 
    
        
        SET @loopId += 1;
     END 
     
      DROP table #aa
     
    -------------------------------------随机分配积分---------------------------------------------------------
    DECLARE @COUNT INT = 0;
    DECLARE @loopId INT = 1;
    DECLARE @personId INT = 0;
    DECLARE @random  INT = 0;
    DECLARE @id      INT = 0;
    
    SELECT @COUNT = COUNT(1)
    FROM  bm_person
    
    while(@loopId <= @COUNT)
        BEGIN
            SELECT @personId = personId
            FROM bm_person
            WHERE personId = @loopId
            
            SELECT 
                @id = id,
                @random = random
            FROM bm_random
            WHERE isUsed = 0
            ORDER BY NEWID()
            
            UPDATE  bm_random
            SET isUsed = 1
            WHERE id = @id
            
            
            UPDATE bm_person
            SET point = @random
            WHERE personId = @personId 
            
            
            SET @loopId +=1;
        END 
     
    -------------------------------------初始化学生志愿和志愿填报顺序--------------------------------------------------
    DECLARE @personCount Int = 0 ;
    DECLARE @personLoopId   INt = 1;
    DECLARE @personid       INT = 0;
    
    CREATE TABLE #priority(Id int IDENTITY(1,1), schoolId INT)
    TRUNCATE TABLE bm_wish;
    
    SELECT @personCount = count(1)
    FROM bm_person
    
    WHILE(@personLoopId <= @personCount)
        BEGIN
            Select @personid = personId 
            FROm bm_person 
            WHERE personid =  @personLoopId
            
            INSERT INTO bm_wish(personId,schoolId)
            SELECT
                @personid, schoolId
            FROM bm_school;
            
            INSERT INTO #priority
            SELECT convert(VARCHAR(20),schoolId) schoolId
            FROM bm_school
            ORDER BY NEWID()        
            
            /*随机更新学生志愿的优先顺序*/
            UPDATE bm_wish
            SET priority = pr.id
            FROM #priority pr, bm_wish  bw
            WHERE personId = @personId 
            And pr.schoolId = bw.schoolId
            
            truncate TABLE #priority
    
            SET @personLoopId += 1;
        END 
    DROP TABLE #priority;
    -----------------------------------------更新学生是否调剂志愿--------------------------------------------
    DECLARE @personCount Int = 0 ;
    DECLARE @personLoopId   INt = 1;
    
    SELECT @personCount = count(1)
    FROM bm_person
    
    print @personCount;
    WHILE(@personLoopId <= @personCount)
        BEGIN
            print @personLoopId;
            update bm_person 
            set isAdjust = CASE WHEN rand() > 0.5 THEN 1 ELSE 0 END 
            where personid =  @personLoopId
    
            SET @personLoopId += 1;
        END 
    
    • 算法实现
    /*
        支持分数从高到低分配
        支持按照学生志愿的优先顺序分配
        支持学生的调剂分配
    */
    ALTER PROCEDURE dbo.[Pro_StudentAssign]
    @wishCount      INT
    AS
    BEGIN;
        BEGIN TRY;
            BEGIN TRAN;
                /*学生数*/
                DECLARE @personCount    INT = 0;
                /*学生循环因子*/
                DECLARE @personLoopId   INT = 1;
                /*学生志愿循环因子*/
                DECLARE @wishLoopId     INT = 1;
                /*学校id*/
                DECLARE @schoolId       INT = 0;
                /*学生id*/
                DECLARE @personId       INT = 0;
                /*是否可以按志愿分配*/
                DECLARE @isCanWish      INT = 0;
                /*是否调剂*/
                DECLARE @isAdjust       INT = 0;
                /*可分配的学校学位总数*/
                DECLARE @sumPlaceCount  INT  = 0;   
                
                SELECT 
                    @sumPlaceCount = SUM(placeCount)
                FROM bm_school
                
                /*重置学校学位数*/
                UPDATE bm_school 
                SET residue = placeCount
    
                /*重置学生志愿分配结果*/
                UPDATE bm_person
                SET schoolId = 0,
                isLose = 0;
    
                /*按照积分从高到低读取学生数据*/
                SELECT 
                    CONVERT(VARCHAR,personId) personId,id=IDENTITY(INT,1,1), point,isAdjust
                INTO #person
                FROM bm_person
                ORDER BY point DESC 
    
                /*读取学生数*/
                SELECT 
                    @personCount = COUNT(1)
                FROM #person
                
    
                /*遍历学生*/
                WHILE(@personLoopId <= @personCount)
                    BEGIN
                        SET @wishLoopId = 1; 
                        SET @schoolId   = 0  
                        SET @isCanWish  = 0
    
                        SELECT
                            @personId = personId, 
                            @isAdjust = isAdjust
                        FROM  #person
                        WHERE Id = @personLoopId;
                        
                        /*判断该学生是否可以按志愿分配学校学位*/
                        /*
                            是否按照志愿来分配受两个因素影响
                             1、填报的志愿个数
                             2、填报志愿的招收人数
                        */
                        With TopCountWishCTE AS(
                            /*读取该学生的最优先的@wishCount个志愿*/
                            SELECT 
                                TOP(@wishCount) *
                            FROM bm_wish 
                            WHERE personId = @personId 
                            ORDER BY priority ASC                     
                        )
                        SELECT @isCanWish =  COUNT(1)
                        FROM bm_school bs
                        WHERE residue > 0 
                        AND EXISTS(
                            SELECT 
                                1
                            From TopCountWishCTE bw
                            Where bs.schoolId = bw.schoolId
                        );
    
                        print ('@isAdjust='+ convert(varchar, @isAdjust)+'@wishCount='+ CONVERT(varchar,@wishCount) +',@personId='+ convert(varchar, @personId) +',@isCanWish='+ convert(varchar,@isCanWish));
                        
                        /*志愿内的学校学位未满,可以分配到学位*/
                        IF @isCanWish > 0
                            BEGIN
                                /*逐个检索填写优先志愿学校,若第一志愿已满,则检索第二个志愿,逐个检索*/            
                                SELECT
                                    Top 1 @schoolId = bw.schoolId
                                FROM bm_wish bw, bm_school bs
                                WHERE personId = @personId
                                AND bw.schoolId = bs.schoolId 
                                AND bs.residue > 0
                                ORDER BY priority ASC
                                
                                print '@schoolId = '+ convert(varchar,@schoolId);
                            END
                        ELSE 
                            BEGIN
                                /*填报志愿学校学位已满并且接受调剂*/
                                IF @isAdjust = 1
                                    BEGIN
                                        /*随机读取一个有剩余学位的学校*/
                                        SELECT
                                            @schoolId = schoolId
                                        FROM bm_school
                                        WHERE residue > 0
                                        ORDER BY NEWID()                            
                                    END
                                ELSE
                                    BEGIN
                                        /*记录落选的学生*/
                                        UPDATE bm_person
                                        SET isLose = 1
                                        WHERE personId = @personId
                                                                         
                                        --PRINT '若未接受调剂有可能高分落榜,只能去读民办小学'
                                        /*若未接受调剂有可能高分落榜,只能去读民办小学*/
                                    END
                            END
                        
                        /*若学生成功分配上则对学生标记学校学位,以及更新学位数*/
                        IF @schoolId > 0 And @schoolId IS NOT NULL
                            BEGIN
                                /*更新剩余的学位数*/
                                UPDATE bm_school
                                SET residue = residue - 1
                                WHERE schoolId = @schoolId
                                
                                /*标记学生已经被分配上*/
                                UPDATE bm_person
                                SET schoolId = @schoolId
                                WHERE  personId = @personId
                            END
                        
                        SET @personLoopId += 1;
                    END
                    
                /*前@sumPlaceCount落选的高分学生占比*/
                IF @personCount > 0
                    BEGIN
                        SELECT
                             COUNT(1) failCount,convert(DECIMAL(4,2), 
                             COUNT(1) * 100.00 / (@sumPlaceCount * 1.00))failRate
                        From(
                            SELECT 
                                TOP(@sumPlaceCount) *
                            FROM bm_person
                            ORDER BY Point DESC                      
                        )as TableLose    
                        WHERE isLose = 1;   
                    END          
                    
                    PRINT '@sumPlaceCount='+CONVERT(VARCHAR,@sumPlaceCount);
                DROP TABLE #person;
            COMMIT TRAN ;
        END TRY 
    
        BEGIN CATCH
            ROLLBACK TRAN ;
        END CATCH 
    END;
    
    • 结果测试
    exec dbo.[Pro_StudentAssign] N;/*N为设定学生本次可填报的志愿个数,返回结果为高分落选占本次所有学校招生总数的比率*/
    
    • 总结

                  由于本算法的中的测试样本是按照随机方式生成的,因此与实际填报情况是有差别的,但是根据人们的行为心理学分析,家长们都喜欢往好的报,比较理性的都会选择调剂保证能够上公办学校,因此实际情况中,学生高分的只要志愿填报合理并且接受调剂,就不会落选。

  • 相关阅读:
    力扣算法题—091解码
    力扣算法题—090子集2
    力扣算法题—089格雷编码
    力扣算法题—088合并两个有序数组
    HDU 1509 Windows Message Queue
    HDU 1241 Oil Deposits
    HDU 1096 A+B for Input-Output Practice (VIII)
    HDU 1108 最小公倍数
    HDU 1106 排序
    HDU 1003 Max Sum * 最长递增子序列(求序列累加最大值)
  • 原文地址:https://www.cnblogs.com/wala-wo/p/5119234.html
Copyright © 2011-2022 走看看