031402429張秀鋒 031402310洪志興git
項目連接:https://coding.net/u/dtewx/p/Pair-Programming-Work/git算法
編碼實現一個畢設導師的智能匹配的程序。提供輸入包括:30個老師(包含帶學生數的要求的上限,單個數值,在[0,8]內),100個學生(包含績點信息),每一個學生有5個導師志願(志願的導師能夠重複但不能空缺)。實現一個智能自動分配算法,根據輸入信息,輸出導師和學生間的匹配信息(一個學生只能有一個確認導師,一個導師能夠帶少於等於其要求的學生數的學生) 及 未被分配到學生的導師 和 未被導師選中的學生。 編程
此次的做業要求比較簡單,條件也比較少,只有輸入數據輸出結果,重點在於儘可能減小未被分配到的學生數量,但同時也應該知足一些要求,好比學生的績點排名、志願順序等,通過討論咱們肯定了下面幾個原則:學習
1.績點優先:績點較高的學生應該有優先權選擇導師
2.志願順序優先:優先考慮學生前面的志願
3.學生意願優先:只把學生分配給他志願中的導師,不強制分配測試
咱們討論的方法比較簡單,主要是排序的方法,根據對象有下面三種排序方式:編碼
1.按績點分配:把學生按績點從高到低排序,從第一個學生開始,依次考慮他的五個志願,若該志願的導師指望人數不爲零則將該學生分配給導師,不然不分配,輪到下一個學生,若五個志願都沒法分配,那麼這個學生被剩下。全部學生輪到事後結束。.net
2.按志願分配:先對學生的績點從大到小進行排序,而後進行5次循環:對全部學生的第i(i從1到5)志願按從績點高的開始分配(只看第i志願),第i志願導師限選人數已滿就跳過,放到下一次循環(第i+1志願),直到剩下的學生的全部志願導師都被選滿爲止。設計
3.按導師分配:選出有被選擇的導師,按導師被選人數從大到小排序並按這個順序進行:對每一個導師,先把選擇他的學生按志願排列,再對每級志願按績點排列,按這個順序把學生分配給導師直到限選人數滿了或者沒學生了,輪到下一個導師,全部導師輪到事後結束。code
上述的三種方法都各有特色,其中第一種方法強調績點,對於績點高的同窗頗有好處,但績點低的就很沒有優點,很容易落選;第二種方法綜合了學生的績點和志願順序,比前者均衡,績點越高志願越靠前越能選中;第三種從導師角度選擇,也比較均衡,就是導師的順序對結果影響較大,越靠前的導師越能選中學生,不過對導師排序彷佛不妥。最後咱們選擇了第二種方法,雖然並非最好的方法,不過也兼顧了績點和志願順序。對象
三種方法在學生扎堆選擇導師時落選的學生都會比較多,不過考慮到學生意願優先,在沒有強制分配的狀況下結果也是由學生本身選擇的,跟他們的選擇分佈有很大關係。
1.對學生按績點進行排序;
2.開始第i輪循環:只考慮第i志願,從績點最高的開始,若導師限選人數未滿,則將該學生分配給該導師,不然繼續下一個學生;
3.五輪循環結束後,輸出結果;(輸出格式爲:「導師號:學生號」,即每一個導師被分配到的學生以及已分配的學生數和未分配學生、導師的編號)
流程圖:
定義結構體:
struct student{ int sno; int cho[5]; double gpa; bool chos; }; struct teacher{ int tno; int wnum; int snum; bool chos; int stud[8]; };
生成隨機數據:
freopen("input.txt","w",stdout); srand((int)time(0)); for(i=1;i<=snum;i++){ printf("%d ",i); for(k=0;k<5;k++){ j=1+(int)((float)tnum*rand()/(RAND_MAX+1.0)); printf("%d ",j); } gpa=1+(float)((40.0*rand()/(RAND_MAX+1.0))/10); printf("%.2f ",gpa); }
讀取文件:
void readdata(student s[],teacher t[]){ int i,j; freopen("input.txt","r",stdin); for(i=0;i<snum;i++){ scanf("%d",&s[i].sno); for(j=0;j<5;j++) scanf("%d",&s[i].cho[j]); scanf("%lf",&s[i].gpa); s[i].chos=false; } for(i=0;i<tnum;i++){ scanf("%d%d",&t[i].tno,&t[i].wnum); t[i].snum=0; t[i].chos=false; } fclose(stdin); }
利用快排進行排序:
void sort(student s[],int l,int r){ int i,j; student x; if(l<r){ i=l; j=r; x=s[i]; while(i<j){ while(i<j&&s[j].gpa>x.gpa) j--; if(i<j) s[i++]=s[j]; while(i<j&&s[i].gpa<x.gpa) i++; if(i<j) s[j--]=s[i]; } s[i]=x; sort(s,l,i-1); sort(s,i+1,r); } }
分配及輸出:
void fp(student s[],teacher t[]){ freopen("output.txt","w",stdout); int i,j,x,k=0,h; for(i=0;i<5;i++) for(j=snum-1;j>=0;j--){ x=match(t,s[j].cho[i]); if(s[j].chos==false&&t[x].wnum>0){ s[j].chos=true; t[x].chos=true; t[x].wnum--; t[x].snum++; h=t[x].snum-1; t[x].stud[h]=s[j].sno; k++; } else continue; } printf("Result(Teacher: student):\n"); for(i=0;i<tnum;i++) if(t[i].chos==true){ printf("%d:",t[i].tno); for(j=0;j<t[i].snum;j++) printf("%d ",t[i].stud[j]); printf("\n"); } printf("\nNumber of arranged students: %d\n",k); printf("Unarranged students: "); for(i=0;i<snum;i++) if(s[i].chos==false) printf("%d ",s[i].sno); printf("\nUnarranged teachers: "); for(i=0;i<tnum;i++) if(t[i].chos==false) printf("%d ",t[i].tno); fclose(stdout); }
設s爲有被學生選擇的全部導師的指望人數之和,a爲學生總人數,b爲用算法實際完成分配的學生人數,若s>=a,則算法應儘可能使b接近或等於a;若s<a,則算法應儘量使b接近或等於s;用y=b/[min(a,s)]來表示算法的分配完成度。因爲實際狀況下s不小於a,因此用y=b/a來計算。
用隨機生成的數據進行了10次測試,每次都是100個學生和30個導師,測試結果以下:
能夠看到效果仍是能夠的,整體的中選率在90%以上。雖然隨機數的結果跟實際上的會有一些差異,但在學生選擇較爲平均的狀況下仍是能保證必定的中選率。另外因爲隨機數生成時不能保證導師指望人數和老是大於學生總數,結果也會有些誤差,形成中選率偏小。測試隨機數據只能用來作一個大概的參考,並不能很好地模擬實際狀況。總之,仍是要看實際的檢驗。
咱們所用的算法規則很簡單,只是機械地按照排好的順序進行分配,沒有進行權重計算、動態分配,缺少靈活性,不能保證結果是最優的,另外對於一些狀況好比扎堆選擇不能有效的避免學生落選。
雖然此次的要求中沒有導師對學生的選擇狀況,不過咱們能夠在代碼中把導師的選擇加入規則,當學生被導師接受時照常進行分配,當被拒絕時能夠直接排除。
其實就是一些咱們結對合做過程當中遇到的一些小問題,而後想起《構建之法》中老師提到的幾個點:
一、一開始咱們的代碼中有中文的字符(提示或是註釋),在咱們傳送文件,另外一人接收後,中文字符就都亂碼了。多是環境不一樣的緣故,因此按照書中提到的「註釋(包括全部源代碼)應該只用ASCII字符,不要用中文或其餘特殊字符,不然會極大影響程序的可移植性。」因此,咱們就用蹩腳的英語寫了註釋,儘可能作到簡明扼要能讓人看懂。
二、第二點也是代碼風格的問題,由於曾經就遇到過,原本在實驗室寫的代碼,帶回打開後發現縮進變得很彆扭,有的長有的段,緣由是「tab鍵在不一樣的狀況下會顯示不一樣的長度,嚴重感染閱讀體驗。」因此,咱們就把全部的tab縮進替換成4個空格。
可能這是些小細節,卻值得咱們去吸取和學習。
zxf:通過第一次做業的幾回交流討論磨合,此次做業的討論比上次更像是在真正地討論問題了,更放得開大膽地講咱們的想法,隊友提出疑問而後再一塊兒想改進的方法。在討論過程當中咱們經常會錯意,沒有徹底弄清楚隊友的意思,更深刻的交流和討論,這應該也是進一步磨合的過程吧。咱們的默契還有提高的空間!
hzx:此次的做業中咱們採起了很簡單的方法來實現,只能說實現了「分配」,算不上是「智能」,雖然隨機數模擬的效果還行,我的感受並無很好地達到目標。咱們有進行過討論,不過沒有抓住重點,更多地在考慮一些實際的細節和特殊狀況,最後採用了簡單的方法,另外因爲我編程上比較渣,咱們動手也比較晚,結果就這樣了吧,總的來講還有不少能夠改進的地方。