031402324 巫振格
031402338 解宇虹
編碼實現一個畢設導師的智能匹配的程序。提供輸入包括:30個老師(包含帶學生數的要求的上限,單個數值,在[0,8]內),100個學生(包含績點信息),每一個學生有5個導師志願(志願的導師能夠重複但不能空缺)。實現一個智能自動分配算法,根據輸入信息,輸出導師和學生間的匹配信息(一個學生只能有一個確認導師,一個導師能夠帶少於等於其要求的學生數的學生) 及 未被分配到學生的導師 和 未被導師選中的學生。git
一、輸入的數據,另外寫生成程序隨機實現。 二、爲輸入輸出設計標準化、通用化、可擴展的接口,爲該智能匹配程序模塊後期可能的整合入系統提供便利。 三、輸入輸出的格式,如採用文本文件或數據庫的方式輸入,可自由討論肯定,但須要明確,爲後期可能的整合入系統提供便利。 四、須要爲智能匹配算法確立幾條分配或排序原則,好比 績點優先、或其餘、或其餘等等,請大家結對討論肯定。 五、算法評價的目標是:對於同一組輸入,輸出的未被導師選中的學生數越少越好。
權重值即將志願與績點,根據一個評價函數打分獲得權重值,最後學生將根據權重值從大到小排序 #define p 0.5 w(v,s)=pv+(1-p)s//第p志願,s爲績點 分析: 好處:績點高的學生即使第五志願也可能比績點低的學生的第一志願更有競爭力,保證了績點高的學生的選擇權 壞處:評價函數決定了最終的分配,但權重比例難以肯定,並且績點低的學生可競爭力更弱 因爲權重比例難以肯定,因此咱們設定比例爲p,而後去實驗出一個較優的p,能夠更加接近目標,最終咱們算法肯定p爲0.5。
導師競爭度即某導師實際報的學生數與該導師滿額人數之商,即競爭度 = 實報人數/滿額人數,按照競爭度從小到大給老師排序。 cpt(m,n)=m/n //m爲實報人數,n爲滿額人數 分析: 好處:導師流行度能夠儘可能使全部學生都選到導師 壞處:存在一種極端狀況,當導師所帶的學生名額不多,學生選擇導師的數量多的狀況下,這個導師極可能選不到學生,具體會在下文算法評價中指出。
通過討論,咱們將兩個思想結合,兼顧全部學生,以相對的公平,保證他們選擇心怡老師的權利。具體規則以下:算法
1)從志願一開始篩選到志願五,共五輪篩選,規則均相同; 2)計算每一個導師的競爭度,按照競爭度從小到大排序,肯定優先匹配的導師; 3)計算每一個學生的權重值,按照權重值從大到小排序,肯定被選擇的優先權; 4)每一輪,從優先匹配的導師的角度出發,選擇符合條件學生,若是一個導師帶的學生有名額,就將選擇這個導師的學生權重值從大到小篩選學生,若導師在本輪篩選中未選滿全部名額,則按相同規則在下一輪繼續篩選。
主要函數: void srand(unsigned seed):隨機數發生器的初始化函數。 代碼以下:
#include<stdio.h> #include<stdlib.h> #include<time.h> #define TEANUM 30 #define STUNUM 100 #define ASPIRATION 5 #define TXTNAME "data.txt" int main() { FILE *f1; if((f1 = fopen(TXTNAME,"w")) == NULL) { printf("沒法打開文件!\n"); return 0; } int i,j,tea[TEANUM]; srand((unsigned)time(NULL));//隨機數種子初始化 while(1) { int sum = 0; for(i = 0;i < TEANUM;i++) { tea[i] = rand() % 9;//隨機生成導師所帶人數 sum += tea[i]; } if(sum >= STUNUM)break; } for(i = 0;i < TEANUM;i++) fprintf(f1,"%d\t%d\n",i,tea[i]); fprintf(f1,"\n\n"); for(i = 0;i < STUNUM;i++) { fprintf(f1,"%d\t%.3f\t\t",i,rand() % 5000 / 1000.0);//隨機生成學生績點,保留三位小數 for(j = 0;j < ASPIRATION;j++) fprintf(f1,"%d\t",rand() % TEANUM);//隨機生成志願 fprintf(f1,"\n"); } return 0; }
分析:這個程序隨機生成導師所帶的學生數,學生的績點以及志願;學生績點須要保留三位小數,採用生成0-5000之間的隨機數,再除以1000.0獲得的。
咱們使用最熟悉的C語言編寫程序,並且代碼規範,可讀性強;輸入輸出的格式,均採用文本文件,程序實現均用函數封裝,具備標準化、通用化、可擴展的接口,爲該智能匹配程序模塊後期可能的整合入系統提供便利。數據庫
文件讀入編程
void read(Teacher tea[],Student stu[][ASPIRATION],FILE *f1) { int i,j; for(i = 0;i < TEANUM;i++) { fscanf(f1,"%d%d",&tea[i].teaNum,&tea[i].leadNum); tea[i].popular = 0; tea[i].full = false; } for(i = 0;i < STUNUM;i++) { fscanf(f1,"%d%lf",&stu[i][0].stuNum,&stu[i][0].score); for(j = 1;j < ASPIRATION;j++) stu[i][j].stuNum = stu[i][0].stuNum; for(j = 0;j < ASPIRATION;j++) { fscanf(f1,"%d",&stu[i][0].aspiration[j]); stu[i][0].evalScore[j] = 0; stu[i][j].chosen = false; } stu[i][0].next = NULL; } }
評價函數函數
void evaluate(Student stu[][ASPIRATION]) { int i,j; for(i = 0;i < STUNUM;i++) { for(j = 0;j < ASPIRATION;j++) stu[i][0].evalScore[j] = (j + 1) * (1 - SCORE_PERCENT) + stu[i][0].score * SCORE_PERCENT; } }
按照導師競爭度排序學習
void sort(Student *leadStu[],Teacher tea[]) { int i,j; for(i = 0;i < TEANUM;i++) { for(Student *p = leadStu[i];p;p = p->next) { tea[i].popular++; } if(tea[i].leadNum) tea[i].popular /= (double)tea[i].leadNum; else tea[i].popular = 0; } for(i = 0;i < TEANUM;i++) { for(j = i;j < TEANUM;j++) { if(tea[i].popular > tea[j].popular) { Teacher temp = tea[i]; tea[i] = tea[j]; tea[j] = temp; } } } }
導師選擇學生測試
void teaSelect(Teacher tea[],Student *leadStu[],Student stu[][ASPIRATION]) { int i,j,num; for(i = 0;i < TEANUM;i++) { num = 0; if(!tea[i].full) { //printf("%d\t",i); for(Student *p = leadStu[tea[i].teaNum];p && num < tea[i].leadNum;p = p->next) { if(p->chosen)continue; tea[i].teaLeadStu[num++] = p->stuNum; for(j = 0;j < ASPIRATION;j++) { stu[p->stuNum][j].chosen = true; } //printf("%d ",num); } if(num == tea[i].leadNum) tea[i].full = true; tea[i].realNum = num; //printf("\n"); } } }
學生志願與導師匹配編碼
void stuChoose(Student stu[][ASPIRATION],Student *leadStu[]) { int i,j,num; for(i = 0;i < ASPIRATION;i++) { for(j = 0;j < STUNUM;j++) { num = stu[j][0].aspiration[i]; if(leadStu[num]) { Student *p = leadStu[num]->next,*up = leadStu[num]; if(stu[j][0].evalScore[i] > leadStu[num]->evalScore[i]) { stu[j][i].next = leadStu[num]; leadStu[num] = &stu[j][i]; continue; } for(;p;up = p,p = p->next) { if(stu[j][i].evalScore[i] > p->evalScore[i]) { Student *temp = up->next; up->next = &stu[j][i]; stu[j][i].next = temp; break; } } if(p == NULL) { up->next = &stu[j][i]; stu[j][i].next = NULL; } } else { leadStu[num] = &stu[j][i]; stu[j][i].next = NULL; } } } }
打印匹配結果lua
void printMatch(Teacher tea[],FILE *f2) { int i,j; fprintf(f2,"師生匹配信息:\n\n"); for(i = 0;i < TEANUM;i++) { fprintf(f2,"導師號: %3d 滿額: %d 實招: %d 學生號: ",tea[i].teaNum,tea[i].leadNum,tea[i].realNum); for(j = 0;j < tea[i].realNum;j++) fprintf(f2,"%d ",tea[i].teaLeadStu[j]); fprintf(f2,"\n"); } } void printStu(Student stu[][ASPIRATION],FILE *f2) { int i,j,sum = 0; fprintf(f2,"\n\n未被導師選中的學生: "); for(i = 0;i < STUNUM;i++) if(!stu[i][j].chosen) { sum++; fprintf(f2,"%d ",stu[i][0].stuNum); } fprintf(f2,"\n共 %d 個\n",sum); fprintf(f2,"\n\n"); } void printTea(Teacher tea[],FILE *f2) { int i,sum = 0; fprintf(f2,"未被學生選的導師: "); for(i = 0;i < TEANUM;i++) if(tea[i].realNum == 0) { sum++; fprintf(f2,"%d ",tea[i].teaNum); } fprintf(f2,"\n共 %d 個\n",sum); }
算法最終實現對於同一組輸入,輸出的未被導師選中的學生數越少越好。爲此咱們小組將學生和老師的數量減小,作了個測試,測試結果以下表所示:.net
從折線中能夠看出,這個算法基本實現學生的徹底匹配,可是另外一方面,沒有學生選的導師也比較多。這部分導師主要是滿額學生數少與算法自己形成的。滿額數少根據公式cpt(m,n)=m/n,m爲實報人數,n爲滿額人數,容易得出競爭度cpt較高,然而實際選的學生數可能並很少,根據算法競爭度高的要到後期匹配,原本有選這個導師的學生由於其餘導師的匹配成功可能致使該導師可以匹配的學生數急劇減小,甚至沒法匹配,因此滿額度低的導師容易選不到學生。另外一方面,由於從低競爭度開始匹配,因此學生就可以比較高度匹配。
咱們小組是男女搭配的小組,不一樣的腦回路帶來的確定是思想上的各類摩擦,可是摩擦事後迎來的確定是思想的融合。因此咱們的任務完成的也算成功,接下來是咱們兩結對過程當中的感覺及對對方的見解。
解宇虹:結對編程真的是一個互相學習的好機會,在這個過程當中,當對方有問題的時候能夠互相討論,並及時提出相應的措施,避免了大量時間的浪費,提升了咱們代碼的學習成本。但在此次結對編程中最大的感覺就是當兩我的對一個問題各持己見時引起的「口水戰」。雖然我是女生,可是我性子有點急,還好個人隊友夠耐心,每次遇到這種狀況都能不驕不躁,而且努力讓我理解他的想法,靜下心來思考,我想這是他身上最大的閃光點。
巫振格:此次結對編程感受仍是比較耗時間的,開始連題目都有一點懵逼,後來開始討論算法的規則,一個一個點的考慮,仍是有不少不足之處,想的不全面,致使後來走了彎路,設計了一個不過高效的算法,有四、五、六、7個學生未匹配都有,再就是翹課重編了……而後就一直討論,後面勉強憋了一個程序出來,勉強看。總的體會就是,結對討論確實能夠彌補一我的的不足,也可讓人相互督促,互相進步。
附件:智能匹配算法源程序