1. 程式人生 > 其它 >對本命專業測試演算法與結果分佈的簡要分析

對本命專業測試演算法與結果分佈的簡要分析

本命專業測試演算法與結果分佈的簡要分析

五月底發現並種草,事情實在有點多於是咕到現在

  • 測試過程

    • PC瀏覽器開啟

      • 這個高階的自適應上來就給了我一個下馬威,不得不說圖片清晰度還不錯
    • Network分析

      • 點選F12後重新整理,通過Network進行分析,可以看到收到大量png圖片
      • 簡單瀏覽一下,可以發現其中包含了全部題目選項,但沒有結果
      • 輸入任意字串開始答題,此時會發現收到另一批png圖片,其中包含了全部可能的結果,共存在28種,看來這個測試的作者是個爽快人
    • 尋找JavaScript

      • 既然題目和結果已經在測試開始前傳送到本地,很自然的猜測計算結果也在本地完成
        ,為此需要在JavaScript中尋找,一個可能的突破口是從按鈕的點選事件入手分析,不過由於Network中只發現了三個JavaScript,依次瀏覽即可發現如下結構,進一步閱讀此JavaScript中的程式碼可以得到從選項計算結果的演算法
      • 此外,瀏覽這個JavaScript還可以發現,題目切換也在此實現,擷取的部分程式碼如下,不過這並非本文重點,在此不做過多的討論
        T = (t(123), [{
        	key: "a",
        	value: "文科"
        }, {
        	key: "b",
        	value: "理科"
        }, {
        	key: "c",
        	value: "再讓我想想..."
        }]),
        K = (t(124), [{
        	key: "a",
        	value: "外向且理性的人"
        }, {
        	key: "b",
        	value: "外向但感性的人"
        }, {
        	key: "c",
        	value: "內向且感性的人"
        }, {
        	key: "d",
        	value: "內向但理性的人"
        }]),
        
  • 演算法解析

    • 演算法完整程式碼如下

      var J = function(A) {
      	Array.prototype.shuffle || (Array.prototype.shuffle = function() {
      		for (var A, e, t = this.length; t; A = parseInt(Math.random() * t),
      			e = this[--t],
      			this[t] = this[A],
      			this[A] = e)
      			return this
      	});
      	for (var e = {/* 見下表1 */}, t = [], n = 0; n < A.length; n++)
      		for (var r = 0; r < A[n].length; r++) {
      			var o = A[n][r], i = e[n][o];
      			t = t.concat(i)
      		}
      	var a = {}, s = -1;
      	t.shuffle();
      	for (var l = 0; l < t.length; l++)
      		void 0 === a[t[l]] && (a[t[l]] = 0),
      		a[t[l]]++, -1 == s ? s = t[l] : a[t[l]] > a[s] && (s = t[l]);
      	return s
      },
      
    • 演算法流程描述如下

      • 第一階段

        • 預先為每一題的的每個選項打若干專業標籤(使用下面給出的表1),合併前九題對應選項的標籤列表為一個列表
          • 演算法開始時,最終列表被初始化成一個空列表
          • 以第一題選擇C為例,第一題C選項的標籤列表是[22, 23, 24, 25, 27],選擇了第一題C選項,那麼最終列表會和[22, 23, 24, 25, 27]合併,即變為[22, 23, 24, 25, 27]
          • 以第二題選擇C為例,第二題C選項的標籤列表是[5, 14, 22],選擇了第二題C選項,那麼最終列表會和[5, 14, 22]合併,即變為[22, 23, 24, 25, 27, 5, 14, 22]
          • 對其餘題目以此類推,需要注意的是第九題是多選題最終列表需要和每個選項對應的標籤列表合併
        • 這一段的程式碼如下
          for (var e = {/* 見下表1 */}, t = [], n = 0; n < A.length; n++)
          	for (var r = 0; r < A[n].length; r++) {
          		var o = A[n][r], i = e[n][o];
          		t = t.concat(i)
          	}
          
        • 其中A是選項id列表,t最終列表e[n][o]是第n題第o個選項的標籤列表
        • 因為允許多選,選項id列表A中的每一項還是一個列表
        • 第一至八題是單選題,因此A中的第一至八項的列表中有且僅有一項,即滿足A[n].length1
        • 第九題是允許選2-3項的多選題,因此A中的這一項的列表有2-3項,即滿足A[n].length23
      • 第二階段

        • 遍歷最終列表,找出頻率最高的一項,如果存在不止一個頻率最高則從中隨機選擇一個(這裡沒有真的隨機,具體分析見下文)
        • 我們先看這一段程式碼,s是最終結果,a是一個dict或者說map,它建立了列表項到其出現頻率的對映
        • 這段程式碼遍歷了最終列表的列表項,如果其不在a中則加入a,對a中的對應項,增加其頻率,如果增加後的頻率超過了最終結果的頻率則更新最終結果
          var a = {}, s = -1;
          for (var l = 0; l < t.length; l++)
          	void 0 === a[t[l]] && (a[t[l]] = 0),
          	a[t[l]]++, -1 == s ? s = t[l] : a[t[l]] > a[s] && (s = t[l]);
          
        • 可以發現,如果存在不止一個頻率最高,上述程式碼返回的是首次出現最早的而不是隨機的,作者實現隨機返回一個通過的是在上面程式碼開始執行前使用t.shuffle()隨機打亂最終列表
      • 演算法最終返回一個數字,作為專業結果的編號,這個對應關係同樣可以在JavaScript中找到,見下面給出的表2
    • 演算法補充分析如下

      • 筆者在開始分析前進行了多次測試,並沒有發現結果不唯一的情況,經過上述分析之後筆者構造了資料希望看到這種隨機情況,採用的資料如下
        C C A B A A B C BC A
        
      • 對這組資料很容易得到下面的最終列表,從而得出22/23/27都有最高的頻率6,因此結果有高几率出現不同
        22, 23, 24, 25, 27
        5, 14, 22
        1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27
        1, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 26, 27
        1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27
        1, 0, 2, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26, 27
        5, 22, 23, 24
        15, 16, 17, 18, 19, 25, 27
        6, 15, 13
        
      • 但經過多次測試,期待的結果不同始終沒有發生,為此筆者進行了單步除錯發現了問題所在,即下面這一段程式碼
        (Array.prototype.shuffle = function() {
        	for (var A, e, t = this.length; t; A = parseInt(Math.random() * t),
        		e = this[--t],
        		this[t] = this[A],
        		this[A] = e)
        		return this
        });
        
      • 注意下面for迴圈的執行順序是,執行A,執行B,若B假則跳出迴圈,否則執行D再執行C再執行B,若B假則跳出迴圈,以此類推
      • 那麼對於上面的迴圈,先執行A也就是var A, e, t = this.length,再執行B也就是t,如果結果為真執行C也就是return this返回,等於什麼都沒有改變
        for (/* [A] */; /* [B] */; /* [C] */) {
        	/* [D] */
        }
        
      • 所以結果出現了不同那才是見鬼,這位作者應該是漏寫for迴圈右括號外面的分號,不得不說這編碼能力測試能力還有待提高啊
    • 給出上文提到的兩個表

      • 1(擷取自上述JS)

        0: {
        	0: [1, 0, 2, 3, 4, 5, 6, 7, 8, 21, 26],
        	1: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26],
        	2: [22, 23, 24, 25, 27]
        },
        1: {
        	0: [0, 2, 6, 7, 20, 21, 25],
        	1: [3, 4, 24],
        	2: [5, 14, 22],
        	3: [1, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 23, 26, 27]
        },
        2: {
        	0: [1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27],
        	1: [0, 2, 6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 20, 25, 26]
        },
        3: {
        	0: [0, 2, 3, 4, 6, 7, 20, 21, 24, 25],
        	1: [1, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 26, 27]
        },
        4: {
        	0: [1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27],
        	1: [0, 2, 6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 20, 25, 26]
        },
        5: {
        	0: [1, 0, 2, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26, 27],
        	1: [3, 4, 5, 14, 22, 24]
        },
        6: {
        	0: [1, 8, 9, 10, 11, 12, 13, 20, 26],
        	1: [5, 22, 23, 24],
        	2: [15, 16, 17, 18, 19, 25, 27],
        	3: [3, 4, 6, 7, 14],
        	4: [0, 2, 21]
        },
        7: {
        	0: [1, 8, 9, 10, 11, 12, 13, 20, 26],
        	1: [5, 22, 23, 24],
        	2: [15, 16, 17, 18, 19, 25, 27],
        	3: [3, 4, 6, 7, 14],
        	4: [0, 2, 21]
        },
        8: {
        	0: [4, 5, 7, 14, 21, 23, 6],
        	1: [6, 15],
        	2: [13],
        	3: [10, 16, 17, 18],
        	4: [12, 19, 20, 25],
        	5: [1, 2, 7, 21],
        	6: [0, 9, 15, 16, 18, 10, 14],
        	7: [17, 22, 24, 23],
        	8: [3, 8, 26, 13],
        	9: [11, 12, 19, 20, 26],
        	10: [25, 27],
        	11: [22, 24]
        },
        9: {
        	0: [],
        	1: [],
        	2: []
        }
        
      • 2(擷取自上述JS)

        W = ["jingjixue", "zhexue", "faxue", "shehuixue", "jiaoyuxue", "hanyuyan", "waiguo", "xinwenxue", "lishixue", "shuxue", "wulixue", "huaxue", "shengmingkexue", "dilixue", "xinlixue", "jisuanjikexue", "tumugongcheng", "jianzhuxue", "jidiangongcheng", "nonglinxue", "yixue", "guanlixue", "yishu", "xijuyingshi", "biaoyan", "tiyuxue", "kaoguxue", "dianzijingji"],
        
    • 給出直觀的選項標籤對應關係

      • 選項標籤對應關係說明

        • 從上面給出的兩個表看題目選項的對應關係是痛苦的,為此筆者使用CPP對上面的內容進行了一些格式化,直觀的給出每道題目每個選項對應關係
        • 最後一題全部選項的標籤列表都是空列表,因此沒有在下面的圖中展示
      • 選項標籤對應關係一覽

      • 格式化程式的程式碼與一些說明

        • 對映字元陣列定義如下
          const char* text[]=
          {
          	"經濟學","哲學","法學","社會學","教育學",
          	"漢語言","外國語","新聞學","歷史學","數學",
          	"物理學","化學","生命科學","地理學","心理學",
          	"電腦科學","土木工程","建築學","機電工程","農林學",
          	"醫學","管理學","藝術","戲劇影視","表演",
          	"體育學","考古學","電子競技"
          };
          
        • 格式化的實質是多行不定量數字輸入的解析,這裡手寫快讀來實現,s被讀取的字元陣列,idx是字元陣列讀到的位置的引用
        • (o<<3)+(o<<1)+(c^48)其實是o*10+c-'0'的意思,有興趣可以自行了解位運算
        • 由於正常讀取不會讀到負數,我們在讀到頭的時候返回負數進行區分
          inline int read(char* s,int& idx)
          {
          	int o=0; char c=s[idx++];
          	while (c && (c<'0' || c>'9')) c=s[idx++];
          	while (c>='0' && c<='9')
          		o=(o<<3)+(o<<1)+(c^48),c=s[idx++];
          	return c?o:-1;
          }
          
        • 注意到形如2: [5, 14, 22],,按行讀取的時候需要先跳過第一個數字,之後利用read讀取即可,順便可以統計讀取到的數量
          inline void solve(char* s)
          {
          	int i,idx=0,cnt=0;
          	printf("\t[");
          	read(s,idx);
          	if ((i=read(s,idx))!=-1)
          	{
          		printf("%s",text[i]),cnt++;
          		while ((i=read(s,idx))!=-1)
          			printf(", %s",text[i]),cnt++;
          	}
          	printf("], total is %d\n",cnt);
          	return;
          }
          
        • 最後完成主函式,重定向輸入輸出後按行讀取直到檔案末尾,特別的,如果一行中不包含中括號,則無需處理直接輸出即可
          int main()
          {
          	char *s=(char*)malloc(1<<10);
          	freopen("tag.txt","r",stdin);
          	freopen("fixed.txt","w",stdout);
          	while (fgets(s,1<<10,stdin))
          	{
          		if (strchr(s,'[')) solve(s);
          		else fputs(s,stdout);
          	}
          	return 0;
          }
          
  • 結果評價

    • 直觀分析

      • 分析上述選項標籤對應關係,可以直觀的得到兩個結論
      • 你的性別/目前的專業對結果沒有影響,沒整男理女文之類的詭異操作,還是值得肯定
      • 對錢的熱愛程度對結果沒有影響,算是個超然於金錢之外的清高作者
      • 更多的分析就見仁見智了,筆者將不進行更多的討論,而是採用統計的方式進行進一步的解讀
    • 統計分析

      • 統計分析說明

        • 顯然選項的組合數量是有限的,且演算法已知,我們可以列舉全部選項組合統計結果,觀察各個專業的佔比情況
      • 給出按照原始錯誤演算法計算出的結果

        • 按照專業id升序排序的結果
          major count percent
          經濟學 55896 4.07%
          哲學 53735 3.91%
          法學 30227 2.20%
          社會學 73748 5.37%
          教育學 39249 2.86%
          漢語言 73436 5.35%
          外國語 64519 4.70%
          新聞學 43233 3.15%
          歷史學 33503 2.44%
          數學 35957 2.62%
          物理學 29806 2.17%
          化學 13529 0.99%
          生命科學 28147 2.05%
          地理學 36290 2.64%
          心理學 93522 6.81%
          電腦科學 59620 4.34%
          土木工程 28676 2.09%
          建築學 26632 1.94%
          機電工程 0 0.00%
          農林學 25619 1.87%
          醫學 69655 5.07%
          管理學 65040 4.74%
          藝術 95100 6.93%
          戲劇影視 57054 4.16%
          表演 82970 6.04%
          體育學 89138 6.49%
          考古學 28874 2.10%
          電子競技 39625 2.89%
        • 按照結果佔比降序排序的結果
          major count percent
          藝術 95100 6.93%
          心理學 93522 6.81%
          體育學 89138 6.49%
          表演 82970 6.04%
          社會學 73748 5.37%
          漢語言 73436 5.35%
          醫學 69655 5.07%
          管理學 65040 4.74%
          外國語 64519 4.70%
          電腦科學 59620 4.34%
          戲劇影視 57054 4.16%
          經濟學 55896 4.07%
          哲學 53735 3.91%
          新聞學 43233 3.15%
          電子競技 39625 2.89%
          教育學 39249 2.86%
          地理學 36290 2.64%
          數學 35957 2.62%
          歷史學 33503 2.44%
          法學 30227 2.20%
          物理學 29806 2.17%
          考古學 28874 2.10%
          土木工程 28676 2.09%
          生命科學 28147 2.05%
          建築學 26632 1.94%
          農林學 25619 1.87%
          化學 13529 0.99%
          機電工程 0 0.00%
      • 給出按照修復shuffle後的演算法計算出的結果

        • 按照專業id升序排序的結果
          major count percent
          經濟學 39508 2.88%
          哲學 33830 2.46%
          法學 30929 2.25%
          社會學 55427 4.04%
          教育學 40098 2.92%
          漢語言 48557 3.54%
          外國語 59138 4.31%
          新聞學 51414 3.75%
          歷史學 19030 1.39%
          數學 16750 1.22%
          物理學 49155 3.58%
          化學 7487 0.55%
          生命科學 28192 2.05%
          地理學 44696 3.26%
          心理學 90569 6.60%
          電腦科學 41542 3.03%
          土木工程 24746 1.80%
          建築學 42353 3.09%
          機電工程 24616 1.79%
          農林學 38035 2.77%
          醫學 67257 4.90%
          管理學 78235 5.70%
          藝術 80525 5.87%
          戲劇影視 61330 4.47%
          表演 104148 7.59%
          體育學 84743 6.17%
          考古學 67820 4.94%
          電子競技 42670 3.11%
        • 按照結果佔比降序排序的結果
          major count percent
          表演 104148 7.59%
          心理學 90569 6.60%
          體育學 84743 6.17%
          藝術 80525 5.87%
          管理學 78235 5.70%
          考古學 67820 4.94%
          醫學 67257 4.90%
          戲劇影視 61330 4.47%
          外國語 59138 4.31%
          社會學 55427 4.04%
          新聞學 51414 3.75%
          物理學 49155 3.58%
          漢語言 48557 3.54%
          地理學 44696 3.26%
          電子競技 42670 3.11%
          建築學 42353 3.09%
          電腦科學 41542 3.03%
          教育學 40098 2.92%
          經濟學 39508 2.88%
          農林學 38035 2.77%
          哲學 33830 2.46%
          法學 30929 2.25%
          生命科學 28192 2.05%
          土木工程 24746 1.80%
          機電工程 24616 1.79%
          歷史學 19030 1.39%
          數學 16750 1.22%
          化學 7487 0.55%
        • 雖然使用了shuffle,導致存在隨機性,但筆者進行了多次測試,除了土木工程機電工程佔比相近有一定概率順序反轉外,可以認為結果分佈是固定的
      • 簡要對分析程式的CPP程式碼實現進行一些說明

        • 對映字元陣列定義如下
          const char* text[]=
          {
          	"經濟學","哲學","法學","社會學","教育學",
          	"漢語言","外國語","新聞學","歷史學","數學",
          	"物理學","化學","生命科學","地理學","心理學",
          	"電腦科學","土木工程","建築學","機電工程","農林學",
          	"醫學","管理學","藝術","戲劇影視","表演",
          	"體育學","考古學","電子競技"
          };
          
        • 選項標籤陣列定義如下,vector就是香啦
          vector<vector<vector<int>>> reflect=
          {
          	{
          		{1,0,2,3,4,5,6,7,8,21,26},
          		{9,10,11,12,13,14,15,16,17,18,19,20,26},
          		{22,23,24,25,27}
          	},
          	{
          		{0,2,6,7,20,21,25},
          		{3,4,24},
          		{5,14,22},
          		{1,8,9,10,11,12,13,15,16,17,18,19,23,26,27}
          	},
          	{
          		{1,3,4,5,9,10,14,21,22,23,24,27},
          		{0,2,6,7,8,11,12,13,15,16,17,18,19,20,25,26}
          	},
          	{
          		{0,2,3,4,6,7,20,21,24,25},
          		{1,5,8,9,10,11,12,13,14,15,16,17,18,19,22,23,26,27}
          	},
          	{
          		{1,3,4,5,9,10,14,21,22,23,24,27},
          		{0,2,6,7,8,11,12,13,15,16,17,18,19,20,25,26}
          	},
          	{
          		{1,0,2,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,23,25,26,27},
          		{3,4,5,14,22,24}
          	},
          	{
          		{1,8,9,10,11,12,13,20,26},
          		{5,22,23,24},
          		{15,16,17,18,19,25,27},
          		{3,4,6,7,14},
          		{0,2,21}
          	},
          	{
          		{1,8,9,10,11,12,13,20,26},
          		{5,22,23,24},
          		{15,16,17,18,19,25,27},
          		{3,4,6,7,14},
          		{0,2,21}
          	},
          	{
          		{4,5,7,14,21,23,6},
          		{6,15},
          		{13},
          		{10,16,17,18},
          		{12,19,20,25},
          		{1,2,7,21},
          		{0,9,15,16,18,10,14},
          		{17,22,24,23},
          		{3,8,26,13},
          		{11,12,19,20,26},
          		{25,27},
          		{22,24}
          	}
          };
          
        • 定義結果類,包含兩個類屬性和三個物件屬性
        • 兩個類屬性分別是搜尋到的總結果數28個專業對應結果的個數,初始化為0
        • 三個物件屬性分別是前八題的結果第九題的結果數量結果
        • pushUp方法在搜尋完成後被呼叫,根據三個物件屬性計算結果,並更新兩個類屬性
        • 值得注意的是CPP提供了shuffle函式,無需我們自行實現
          class Ans
          {
          public:
          	static int total;
          	static int summary[28];
          	int cnt,last[3],normal[8];
          
          	inline void pushUp()
          	{
          		vector<int> temp;
          		if (++total%50000==0) printf("%07d searched\n",total);
          		for (int i=0;i<(int)(sizeof(normal)/sizeof(*normal));i++)
          			for (vector<int>::iterator it=reflect[i][normal[i]].begin();
          				it!=reflect[i][normal[i]].end();it++)
          				temp.push_back(*it);
          		for (int i=0;i<cnt;i++)
          			for (vector<int>::iterator it=reflect[reflect.size()-1][last[i]].begin();
          				it!=reflect[reflect.size()-1][last[i]].end();it++)
          				temp.push_back(*it);
          		if (SHUFFLE) shuffle(temp.begin(),temp.end(),generator);
          		int out=-1,vis[28]={0};
          		for (int i=0;i<(int)temp.size();i++)
          		{
          			vis[temp[i]]++;
          			if (out==-1) out=temp[i];
          			else if (vis[temp[i]]>vis[out]) out=temp[i];
          		}
          		summary[out]++;
          		return;
          	}
          };
          
          int Ans::total=0;
          int Ans::summary[28]={0};
          
        • 實現搜尋函式,傳入當前搜尋下標最大下標Ans物件的指標
        • 第九題的處理採用分別二層迴圈三層迴圈的偷懶方式,如果允許多選的數量更多,再套一個dfs搜一下組合即可
          void dfs(int cur,int top,Ans* ans)
          {
          	if (cur==top)
          	{
          		int range=reflect[reflect.size()-1].size();
          		ans->cnt=2;
          		for (int i=0;i<range;i++)
          			for (int j=i+1;j<range;j++)
          			{
          				ans->last[0]=i;
          				ans->last[1]=j;
          				ans->pushUp();
          			}
          		ans->cnt=3;
          		for (int i=0;i<range;i++)
          			for (int j=i+1;j<range;j++)
          				for (int k=j+1;k<range;k++)
          				{
          					ans->last[0]=i;
          					ans->last[1]=j;
          					ans->last[2]=k;
          					ans->pushUp();
          				}
          		return;
          	}
          	for (int i=0;i<(int)reflect[cur].size();i++)
          	{
          		ans->normal[cur]=i;
          		dfs(cur+1,top,ans);
          	}
          	return;
          }
          
        • 定義結果分析類管理專業id專業數量專業佔比
        • 過載兩個比較運算子用於排序,實現兩個輸出方法用於正常輸出MarkDown表格格式的輸出
          class Node
          {
          public:
          	int idx,val;
          	double percent;
          
          	inline Node(int idx,int val,int total)
          	{
          		this->idx=idx,this->val=val;
          		percent=1.0*val/total*100;
          		return;
          	}
          
          	inline void print() const
          	{
          		printf("major = %-10s  |  count = %06d  |  percent = %5.2lf%%\n",
          			text[idx],val,percent);
          		return;
          	}
          
          	inline void toMarkDown() const
          	{
          		printf("| %s | %d | %5.2lf%% |\n",
          			text[idx],val,percent);
          		return;
          	}
          
          	inline bool operator>(Node& rhs) const
          	{
          		return this->val<rhs.val;
          	}
          
          	inline bool operator<(Node& rhs) const
          	{
          		return this->val>rhs.val;
          	}
          };
          
        • 完成主函式,搜出全部結果後統計分析並輸出一次,按百分比排序後再輸出一次即可
          int main()
          {
          	Ans ans;
          	puts("----- search started -----\n");
          	dfs(0,sizeof(ans.normal)/sizeof(*ans.normal),&ans);
          	printf("\n----- search finished, total = %d, analysing -----\n\n",Ans::total);
          	vector<Node> analyse;
          	for (int i=0;i<(int)(sizeof(Ans::summary)/sizeof(*Ans::summary));i++)
          	{
          		printf("%02d / %d done\n",i,(int)(sizeof(Ans::summary)/sizeof(*Ans::summary)));
          		Node node(i,Ans::summary[i],Ans::total);
          		analyse.push_back(node);
          	}
          	if (MARKDOWN) puts("| major | count | percent |\n| :---: | :---: | :---: |");
          	else puts("\n----- result sort by major id -----\n");
          	for (vector<Node>::iterator it=analyse.begin();it!=analyse.end();it++)
          		if (MARKDOWN) it->toMarkDown();
          		else it->print();
          	sort(analyse.begin(),analyse.end());
          	if (MARKDOWN) puts("| major | count | percent |\n| :---: | :---: | :---: |");
          	else puts("\n----- result sort by percent -----\n");
          	for (vector<Node>::iterator it=analyse.begin();it!=analyse.end();it++)
          		if (MARKDOWN) it->toMarkDown();
          		else it->print();
          	return 0;
          }
          
        • 最後,這個計算量還是有一些的,為了獲得更好的體驗,可以吸兩口氧
          #pragma GCC optimize(2)
          #pragma GCC optimize(3,"Ofast","inline")
          
  • 結語

    • 這個小測試和上次的網易雲一樣,都採用了打標籤的形式,這種形式躲不開的一點就是多個標籤數量相同的處理,個人認為衡量這類測試的靠譜程度很重要的一點就是這裡,筆者不認為任何形式的隨機(如這次作者想實現還寫錯了的打亂,網易雲的遍歷HashMap帶來的隨機)是靠譜對使用者負責的,換句話說結果必須是確定的
    • 對於多個標籤數量相同的處理,個人想到的一種處理是設定優先順序,給出多個相同標籤中優先順序最高的作為結果,當然打標籤並不是這種小測試唯一的實現思路,下次筆者將選擇一個不基於打標籤的小測試進行分析和解讀和一些拓展
    • 分析這些小測試還是挺有趣也挺有收穫的,過程中包括HTML/CSS/JSNetwork/抓包/爬蟲CPP/Java/Python在內的能力都得到了或多或少的鍛鍊
    • 送給看到這裡的2021高考的學弟學妹們,計算機超有趣的,如果不確定選什麼專業的話,計算機也是個不錯的選擇哦