第3屆83行程式碼大賽第2關賽題官方解析
簡介:由阿里云云效主辦的2021年第3屆83行程式碼挑戰賽已經收官。超2萬人圍觀,近4000人蔘賽,85個團隊組團來戰。大賽採用遊戲闖關玩兒法,融合元宇宙科幻和劇本殺元素,讓一眾開發者玩得不亦樂乎。
其中大賽第二題,號稱魔鬼演算法題,攔住諸多程式碼好漢。
我們請來了第二題的出題人,劉力華(阿里云云效程式碼平臺),為大家系統揭祕,從設計到攻略,還有優秀程式碼解析供大家參考。
賽題設計
我設計的第二關是希望能考察參賽者的基礎演算法、資料結構的能力。
設計來源
本賽題採用的是字串字首匹配演算法,引數者需要先通過OSS獲取待匹配的資料集,然後參賽者需要從中找出與指定字首字串相匹配的字串資料。為什麼會選擇該演算法呢?源於近期我在使用我們自己的開發外掛時,它能夠當我在程式碼搜尋的輸入框中輸入Java API關鍵詞時,可以自動提供API名稱的補全提示,受該功能模組啟發,因此希望設計一個題目讓參賽者實現一個Java API名稱的字首匹配演算法。
此處打個廣告:我們的外掛是阿里雲智慧編碼外掛,現已上架JetBrains外掛市場,可以在IntelliJ IDEA中通過搜尋Alibaba Cloud AI Coding Assistant下載使用。外掛包含了程式碼智慧補全及程式碼示例搜尋功能,讓開發者更快速、行雲流水的完成編碼。
整個賽題的設計難點在於如何讓本賽題具備一定的挑戰性。但又保證一定的通關率。該賽題在外部題庫有類似的演算法題,屬於中等難度,所以中等難度能提高一定的通過率。為了避免參賽者通過Java的String.startsWith及雙迴圈就直接通過,所以增大了評測的資料量,但是為了控制賽題難度,評測的資料集分為幾十萬的小資料集以及幾百萬的大資料集,只要能較好的解決小資料集問題,就能通過該賽題。
評分系統
本賽題的打分系統基於函式計算設計,系統會隨機選擇一個小規模資料集和一個大規模資料集,並依次序列執行參賽者的程式碼,並針對這兩個資料集對參賽者編寫的程式碼從準確性、效能開銷、記憶體消耗維度進行評估。
賽題攻略
OSS資料獲取
本賽題的資料集儲存在OSS中,所以需要參賽者通過OSS的SDK進行資料的獲取,參賽者可以通過程式碼註釋中的文件連結去學習OSS SDK如何使用,也可以通過近期釋出的阿里雲智慧編碼外掛(Cosy)快速檢視OSS SDK的示例程式碼。
如上圖所示,如果參賽者想知道OSSObject的資料如何獲取,可以在該API上右鍵點選“檢視程式碼示例”,智慧編碼外掛就能快速查找出實現OSS資料獲取相關的程式碼示例,參賽者只需要選擇性複製並修改部分程式碼即可。
此外,開發者也能通過程式碼智慧補全功能,通過選擇整行的程式碼補全結果,快速的編寫出獲取OSS資料的程式碼,如下圖所示。
演算法攻略
該賽題的解題方法是比較多樣化的,參賽者採用的方法主要分為以下幾種。
第一種:自己實現演算法及資料結構
賽題攻略中提到了Trie Tree,一個較為基礎的資料結構,一個較好的Trie Tree也能夠通關本賽題。但是Trie Tree的效能開銷及記憶體消耗也都是比較大的,可以考慮Trie Tree的諸多變種,比如Double Array Trie,用雙陣列去節省空間,比如Radix Tree及其諸多變種,通過減少樹的深度去壓縮Trie Tree的記憶體佔用。為了對比各種Trie Tree實現的效果,這裡引用《MergedTrie: Efficient textual indexing》論文的相關評測資料。
還有很多參賽者通過減少雙層迴圈的次數、減少字首匹配的效能開銷等方法,也同樣獲得了較高的分數。
第二種,使用JDK內建的資料結構
JDK內建資料結構的效能也是比較高的,部分參賽者使用了TreeSet進行排序,然後通過用subSet方法就能直接獲取匹配的字首字串資料,該方式比較簡單快捷,且程式碼量較小。
參賽程式碼SHOW
參賽者對本賽題的解法諸多,由於篇幅限制,本文章只展示四位參賽者的程式碼片段。
參賽者程式碼片段一
該解法通過String.substring方法去擷取字串不同長度的字首,並將擷取的字首直接從result的Map中進行查詢,該方法無需像String.startsWith逐一進行字元的比較,所以效率會有較大提升,使用這種解法的參賽者比較多,還有部分參賽者會對擷取的長度進行限制,比如事先統計字首字串的最短及最長的長度,只需遍歷生成該長度範圍內的字首即可。
參賽者程式碼片段二
該解法會先排除超出字首字串最短及最長長度範圍的資料,然後對資料集進行排序。隨後遍歷待匹配的字首字串列表,為每個字首字串通過二分查詢計算資料集中,與其匹配位置的陣列上界及下界,然後將該範圍內的資料抽取出來即可。
參賽者程式碼片段三
該解法與排序方法類似,使用了JDK內建的TreeSet進行排序,然後通過TreeSet.subSet方法擷取與字首字串匹配的資料。
部分參賽者為了提高挑戰性,放棄使用TreeSet,自己實現了相關的排序及查詢方法
參賽者程式碼片段四
這是排名第一的參賽者的程式碼,該參賽者對Trie樹進行了優化,使用CharNode、ArrayNode減少部分儲存消耗,其中ArrayNode中通過shift儲存偏移量,陣列的下標位置加上shift偏移量即為字元的ASCII碼。
並且該參賽者在輸出資料時沒有將字串儲存到字串陣列中,而是定義了ByteBuf,在輸出時直接以JSON字串形式儲存到位元組陣列中。
總結
儘管很多參賽者在這一關遇到了較多困難,尤其是在處理大規模資料集時。但正因為如此,我們也發現很多選手並不止步於通關,他們會努力嘗試不同的解法、不斷地優化演算法,哪怕只是提升了0.01分,這一點對我們團隊的觸動也是很大的。賽事後我們也反思了一些做得不夠好的地方,比如IDE評分欄缺少記憶體消耗、效能開銷等具體數字的展示,導致部分參賽者不知道實際的記憶體消耗,後續我們會對這些細節點上進行優化,如果大家有好的想法及建議,歡迎反饋給我們!
看完攻略如果你還想體驗賽題,依然可以前往https://code83.ide.aliyun.com,我們目前仍然開放給大家進行體驗。
最後,歡迎大家試用智慧編碼外掛:https://developer.aliyun.com/tool/cosy,它作為一款AI開發外掛,擁有強大的程式碼智慧補全、程式碼示例搜尋等功能,讓開發行雲流水般編碼,事半功倍地完成開發工作。你可以在IntelliJ IDEA或JetBrains外掛市場中搜索Alibaba Cloud AI Coding Assistant或Cosy,使用中如果有任何問題或建議都可以反饋到Github Issues中,我們會認真傾聽你的聲音。
大賽目前全部關卡開放體驗,域名地址:https://code83.ide.aliyun.com/,歡迎你來。