受到世界盃的影響,我想寫一個程序,可以手動錄入一場比賽所有盤口的賠率,勝平負3種情況、讓球勝平負3種情況、比分26種情況、進球數8種情況、半全場9種情況,有的場次不能單買勝平負、讓球勝平負,或者直接不開這種盤口,但相互之間串在一起不受限制。
以A、B、C、D爲場次的標識,默認一種情況投入10塊錢,算出能夠贏的錢再排序。當只有一場比賽的時候,很好算。當有A、B兩場時,場次本身的結果有A、B、AB,當有A、B、C、三場的時候,場次本身組合的結果就有A、B、C、AB、AC、BC、ABC。
首先要有得出涉及場次所有組合的算法,其中的規律在於,當處理的場次序號時i,i本身是一種組合,先把i之前的組合和i結合一遍,再把i放進去,依次類推,直到數組的最後一項,那麼算法如下。
const teamList = ["A", "B", "C", "D"];
let teamCombinationList = [];
getTeamCombinationList(0);
function getTeamCombinationList(index) {
const list = [];
teamCombinationList.forEach((el) => {
list.push([...el, teamList[index]]);
});
list.push([teamList[index]]);
teamCombinationList = [...teamCombinationList, ...list];
if (index < teamList.length - 1) {
getTeamCombinationList(index + 1);
}
}
結果如下
[
[ 'A' ],
[ 'A', 'B' ],
[ 'B' ],
[ 'A', 'C' ],
[ 'A', 'B', 'C' ],
[ 'B', 'C' ],
[ 'C' ],
[ 'A', 'D' ],
[ 'A', 'B', 'D' ],
[ 'B', 'D' ],
[ 'A', 'C', 'D' ],
[ 'A', 'B', 'C', 'D' ],
[ 'B', 'C', 'D' ],
[ 'C', 'D' ],
[ 'D' ]
]
此時只需遍歷teamCombinationList
,有一個就是單場、有兩個就是2串1、有三個就是3穿1,因爲場次的個數不確定,現在需要一個通用算法實現類似多層for循環的結果。因爲每個場次最多有51種結果,以A0、A1這種方式簡單替代下。
已
[
["A0", "A1", "A2"],
["B3", "B4"],
],
舉例,所有的結果是 'A0B3','A0B4', 'A1B3', 'A1B4', 'A2B3','A2B4'
算法核心邏輯是實現一個for循環,如果不是最後一個場次,則當前結果傳給下一個遞歸,再進行for循環,如果是最後一個場次,則組合結果放進公共數組裏。
/**
* A 'A0','A1','A2'
* B 'B3','B4'
* C 'C0','C5'
*/
let oddResultList = [];
let teamCombinationList = [
[["A0", "A1", "A2"]],
[
["A0", "A1", "A2"],
["B3", "B4"],
],
[["B3", "B4"]],
[
["A0", "A1", "A2"],
["C0", "C5"],
],
[
["A0", "A1", "A2"],
["B3", "B4"],
["C0", "C5"],
],
[
["B3", "B4"],
["C0", "C5"],
],
[["C0", "C5"]],
];
for (let i = 0; i < teamCombinationList.length; i++) {
const list = teamCombinationList[i];
againForEach([], 0);
function againForEach(againList, index) {
list[index].forEach((el) => {
if (index === list.length - 1) {
let str = "";
[...againList, el].forEach((item) => {
str += item;
});
oddResultList.push(str);
} else {
againForEach([...againList, el], index + 1);
}
});
}
}
oddResultList
的結果如下
[
'A0', 'A1', 'A2', 'A0B3',
'A0B4', 'A1B3', 'A1B4', 'A2B3',
'A2B4', 'B3', 'B4', 'A0C0',
'A0C5', 'A1C0', 'A1C5', 'A2C0',
'A2C5', 'A0B3C0', 'A0B3C5', 'A0B4C0',
'A0B4C5', 'A1B3C0', 'A1B3C5', 'A1B4C0',
'A1B4C5', 'A2B3C0', 'A2B3C5', 'A2B4C0',
'A2B4C5', 'B3C0', 'B3C5', 'B4C0',
'B4C5', 'C0', 'C5'
]
獲得所有結果後進行排序,我一開始是調接口實現,後臺基於nodejs實現的,當場次少於等於3場返回還是很快,但場次是4的時候,報錯內存溢出,我以爲是自己算法寫的有問題,造成死循環,但是前面也沒啥問題...
因爲實際的數據結構比較複雜,我就使用--max-old-space-size命令加大,打開任務管理器,發現內存一致逐漸攀升道6、7千M的位置就不上去,加了幾個輸出,發現很快就算出結果,因爲數組本身存儲的是引用,其實這一塊是不會內存溢出的,而是卡在了排序上,把排序代碼去掉,還是不行,因爲要把所有數據序列化後再返給前端,那麼這個json是很大的。
其實所有數據都在前端的時候,是沒必要再掉接口的,於是我把代碼移到前端執行,4個場次雖然慢了些,但還是能展示,5個場次的時候也不行了,頁面崩潰。
1個場次的時候最多51種,2個場次最多3015,3個場次最多166359,4個場次最多9150603,5個場次僅僅是計算處所有結果頁面就崩潰了。至於優化的方式,嘗試用簡單的標記代替對象,等用到的時候再解析讀取,然而一頓操作後還是不行,5個場次有503284374種結果,上億級別了,我只是執行Array(503284374).fill(1)
就崩潰了。