魏印福,李舟軍
(北京航空航天大學(xué) 計(jì)算機(jī)學(xué)院智能信息處理研究所,北京 100191)
中國象棋是一種廣為流傳的完全知識二人零和博弈游戲,形式上與國際象棋極為相似[1-2],在博弈技術(shù)上二者也有許多共通之處。2016年以來,谷歌相繼推出AlphaGo[3]、AlphaGo Zero[4]、AlphaZero[5]等博弈模型,不僅在圍棋領(lǐng)域完勝人類棋手,在國際象棋、日本將棋上也超越了之前最先進(jìn)的博弈引擎,計(jì)算機(jī)博弈受到人們廣泛關(guān)注。
中國象棋博弈常提及的一個(gè)問題就是中國象棋空間復(fù)雜度,即中國象棋狀態(tài)總數(shù)。現(xiàn)有文獻(xiàn)僅給出中國象棋空間復(fù)雜度數(shù)值卻沒有提供計(jì)算方式,同時(shí)不同文獻(xiàn)給出的數(shù)值存在較大差異。長期以來,中國象棋狀態(tài)總數(shù)這一計(jì)數(shù)問題無人問津,卻又莫衷一是。本文利用中國象棋棋子著法特點(diǎn)把求解中國象棋狀態(tài)總數(shù)問題分解為若干個(gè)子問題,通過動態(tài)規(guī)劃方法分別求解各個(gè)子問題,最終準(zhǔn)確求出中國象棋狀態(tài)總數(shù),為以后描述中國象棋狀態(tài)總數(shù)提供可靠依據(jù)。同時(shí),這種計(jì)算中國象棋狀態(tài)總數(shù)的方法除了可用于計(jì)算其他棋類的空間復(fù)雜度,也可以用于構(gòu)造中國象棋棋盤局面哈希函數(shù)[6]、構(gòu)造中國象棋殘局庫[7-8]等方面。
中國象棋棋盤9條豎線,10條橫線,總計(jì)90個(gè)位置[9-10]。棋子分為紅黑兩色,每方16枚棋子,棋子分為7類:車、馬、炮、相、士、將、卒。開始局面如圖1所示。
圖 1 中國象棋開始局面Fig. 1 The start state of Chinese Chess
中國象棋中,“將”和“帥”、“相”和“象”、“卒”和“兵”描述的是同一類棋子。為敘述方便,本文對這3類棋子分別采用“將”、“相”、“卒”的叫法。
為便于用公式表示,本文使用英文縮寫表示相關(guān)棋子,表1描述了棋子的縮寫及其含義,本文使用縮寫表示棋子,例如使用KR表示紅將,KB表示黑將。
表 1 棋子英文表示及其縮寫Table 1 The chess representation and their abbreviation
車、馬、炮3類棋子可以出現(xiàn)在棋盤的任意位置;相不能過河,只能出現(xiàn)在己方空間中的7個(gè)位置,為便于下文敘述,從左到右、從上到下對“相”的可去位置依次編號為0~6,如圖2所示。
圖 2 相的可去位置及其編號Fig. 2 Xiang’s positions and number
士和將只能在九宮格中,士有5個(gè)可去位置,將有9個(gè)可去位置。如圖3,將九宮格中的9個(gè)位置按照從左到右、從上到下的順序依次編號為0~8。
圖 3 九宮格內(nèi)位置編號Fig. 3 Jiugong position and number
卒的著法分為過河和未過河兩種情況,過河卒可以出現(xiàn)在敵方半棋盤空間中的任意一個(gè)位置,未過河的卒只可能出現(xiàn)在己方最上方2行5列共10個(gè)位置格點(diǎn)上,并且每列至多有一個(gè)卒。
定義中國象棋狀態(tài)總數(shù)問題首先需要定義子問題:給定若干棋子,在滿足中國象棋規(guī)則的情況下,把這些棋子擺放在棋盤上,有多少種可能的放置方式。這里給定的每類棋子個(gè)數(shù)必然都不大于開局時(shí)每類棋子的個(gè)數(shù)。中國象棋狀態(tài)總數(shù)可以用式(1)表示,式中S即為中國象棋狀態(tài)總數(shù),put表示從棋型到放置方法個(gè)數(shù)的映射,棋型指14種棋子組成的14維向量,每維數(shù)字表示每類棋子的個(gè)數(shù)。
關(guān)于計(jì)算的中國象棋狀態(tài)總數(shù),需要說明3點(diǎn):
1) 本文討論的是中國象棋可能出現(xiàn)的全部狀態(tài),在每步著法都完美的情況下,從初始狀態(tài)出發(fā)可能無法到達(dá)全部狀態(tài)。
2) 本文統(tǒng)計(jì)的狀態(tài)集合中每個(gè)狀態(tài)雙方“將”都是存在的,即本文計(jì)算的全部局面都是游戲尚未結(jié)束的局面。
3) 本文討論的狀態(tài)包括“將帥見面”的情況。在中國象棋規(guī)則中,促成“將帥見面”局面的一方為輸。
本節(jié)對中國象棋棋子、著法進(jìn)行了簡要介紹,對棋子、棋盤進(jìn)行了一些符號約定,對問題給出了形式化描述并明確了中國象棋狀態(tài)總數(shù)所包括的局面。
計(jì)數(shù)問題的兩個(gè)基本方法是分類加法和分步乘法[11-12]。當(dāng)使用分步乘法時(shí),合理的步驟可以減少分類個(gè)數(shù)[13-14],從而簡便地解決計(jì)數(shù)問題。
給定若干棋子之后計(jì)算擺放方式的個(gè)數(shù)時(shí),擺放次序至關(guān)重要。下面舉例描述擺放次序的重要性。給定2個(gè)紅車、2個(gè)紅相共4枚棋子,計(jì)算擺放方式個(gè)數(shù)。車的位置比較隨意,可以擺放在棋盤上90個(gè)格點(diǎn)的任意位置,相的位置限制較多,只能擺放在如圖2所示的7個(gè)位置上。如果先考慮相再考慮車,可知有 C72×C828種擺放方式;而如果先考慮車再考慮相就需要分3類情況進(jìn)行討論:
1) 當(dāng)車占用0個(gè)相位時(shí),車有83個(gè)位置可選,相有7個(gè)位置可選,這種情況有 C823×C72種局面。
2) 當(dāng)車占用1個(gè)相位時(shí),第1個(gè)車有7種相位可選,第2個(gè)車有83個(gè)位置可選,2個(gè)相有6個(gè)位置可選,這種情況有 C71×C813×C62種局面。3) 當(dāng)車占用2個(gè)相位時(shí),擺放方式有 C72×C52種。
以上3類情況擺法個(gè)數(shù)之和與“先擺放相,再擺放車”所得結(jié)果相同,但顯然第一種方法需要較少的分類討論。由此例可以看出,計(jì)算狀態(tài)個(gè)數(shù)時(shí)合理規(guī)劃擺放棋子的順序能夠極大簡化問題。
計(jì)算中國象棋狀態(tài)總數(shù)時(shí),需按照一定次序放置棋子,核心思想是“先難后易”,即先放置限制較多、活動范圍窄的棋子,然后再放置行動靈活、位置自由的棋子。
車、馬、炮可以到達(dá)棋盤上的任何一個(gè)位置,放置最為自由,只需要知道棋盤上有多少個(gè)空白格點(diǎn),即可通過排列組合計(jì)算出擺法總數(shù),因此最后考慮這3種棋子的擺放。卒過河后可以到達(dá)敵方陣地的每一個(gè)位置,自由度占半個(gè)棋盤,放在倒數(shù)第2位考慮。將的位置會影響士相的擺放,相的位置會影響未過河卒子的擺放??紤]將、士、相、卒這4類棋子之間的互相影響關(guān)系及著法特點(diǎn),擬定擺放順序?yàn)椋簩ⅰ⑹?、相、卒?/p>
綜上,根據(jù)先考慮受限制多的棋子再考慮受限制少的棋子的思路,最終擺放順序?yàn)椋簩?、士、相、卒、車馬炮。
利用分步乘法原理依次處理棋子的擺放,這個(gè)過程可以對問題進(jìn)行層層分解,把問題劃分為若干個(gè)有依賴關(guān)系的子問題。先擺放“將”,“將”擺放完成之后,后面要擺放的“士”和“相”會受到“將”的影響,所以把“將”的位置作為輸入?yún)?shù)向后續(xù)求解模塊傳遞。擺放“士”的時(shí)候可以根據(jù)已擺放“將”的位置,來決定“士”可以擺放的位置有哪些。
整體求解思路為:把已擺放的棋子對未擺放的棋子的影響作為參數(shù)傳遞給后續(xù)子問題,擺放棋子時(shí)參考已擺放棋子的位置來決定當(dāng)前可行的擺放方法。
本節(jié)詳細(xì)描述把中國象棋擺法總數(shù)這個(gè)問題劃分成將、士、相、卒、“車馬炮”5個(gè)子問題,每個(gè)子問題都有明確的輸入,每個(gè)子問題的輸出都是擺法個(gè)數(shù)。子問題之間逐層調(diào)用,最終能夠求得中國象棋狀態(tài)總數(shù)。子問題的定義及其輸入的形式化是整個(gè)求解過程中較為關(guān)鍵的部分。
2.2.1 將
先擺放好雙方的“將”,再考慮子問題:在“將”位置確定的情況下,“士相卒車馬炮”總共有多少種擺法。記此子問題為getShi(KR, KB),該子問題輸入KR, KB為紅黑雙方“將”的位置。
對“將”的每一種擺放方式,將其余棋子的擺法個(gè)數(shù)累加起來即為中國象棋狀態(tài)總數(shù)。偽代碼如下:
函數(shù)名稱 getJiang
輸入 無;
輸出 擺法種數(shù)s。
1) s = 0;
2) 對于紅將的每個(gè)位置KR∈[0, 9);
3) 對于黑將的每個(gè)位置KB∈[0, 9);
4) s+=getShi(KR, KB)
在以上代碼中,KR表示紅方將的位置,KB表示黑方將的位置,累加雙將位置確定之后“士相卒車馬炮”的擺法個(gè)數(shù)即得到中國象棋狀態(tài)總數(shù),如式(2):
2.2.2 士
在2.2.1中,在給定“將”位置的情況下,需要計(jì)算“士相卒車馬炮”有多少種擺法。當(dāng)“將”的位置確定后,“士”的可選位置隨之確定。如果“將”在圖3中的0、2、4、6、8號位置,則“將”占用一個(gè)士位,“士”有4個(gè)位置可選;如果“將”在1、3、5、7、9號位置,“將”沒有占領(lǐng)士位,士有5個(gè)位置可選。
對于“士”的每一種擺法,累加“相卒車馬炮”的擺法個(gè)數(shù),即可得到在“將”位置確定情況下“士相卒車馬炮”的擺法個(gè)數(shù)。記子問題“相卒車馬炮”的擺法個(gè)數(shù)為getXiang(KR, KB, SpaceR, SpaceB),其中KR、KB輸入表示紅黑雙方將的位置,SpaceR、SpaceB表示雙方空白格點(diǎn)數(shù)。getShi函數(shù)輸入?yún)?shù)為紅將位置和黑將位置,輸出“士相卒車馬炮”擺法總數(shù)。偽代碼如下:
函數(shù)名稱 getShi
輸入 紅將位置KR;黑將位置KB;
輸出 “士相卒車馬炮”的擺法總數(shù)s。
1) s=0;
2) 定義紅士可去位置個(gè)數(shù):若KR不在士位上NR=5,否則NR=4;
3) 定義黑士可去位置個(gè)數(shù):若KB不在士位上NB=5,否則NB=4;
4) 對于紅士個(gè)數(shù)的可取值A(chǔ)R∈{0, 1, 2};
5) 對于黑士個(gè)數(shù)的可取值A(chǔ)B∈{0, 1, 2};
6) 紅方空白格點(diǎn)數(shù)SpaceR=45-1-AR;
7) 黑方空白格點(diǎn)數(shù)SpaceB=45-1-AB;
8) s+=CNARRCNABBgetXiang(KR, KB, SpaceR, SpaceB)
在上述偽代碼中,擺放好“士”之后,需要乘以“相卒車馬炮”的擺法個(gè)數(shù),“相卒車馬炮”擺法個(gè)數(shù)通過getXiang函數(shù)實(shí)現(xiàn)??紤]已擺放的“將”、“士”對“相卒車馬炮”的影響,可以發(fā)現(xiàn)后續(xù)棋子的擺放只依賴于紅將位置、黑將位置、紅方空白格點(diǎn)數(shù)、黑方空白格點(diǎn)數(shù)4個(gè)變量。
2.2.3 相
經(jīng)過以上兩步,“將”和“士”的位置確定了,這兩種棋子對后續(xù)棋子的“影響因素”包括:
1) “將”可能會占用相位,影響“相”的可擺放位置的個(gè)數(shù);
2) 紅方和黑方各自的空白格點(diǎn)數(shù),影響“卒車馬炮”的擺放。
問題轉(zhuǎn)化為給定“將”的位置和紅黑雙方空白格點(diǎn)個(gè)數(shù)之后,“相卒車馬炮”有多少種擺法。
“相卒車馬炮”的擺法只跟4個(gè)變量有關(guān):紅將位置、黑將位置、紅方空白格點(diǎn)數(shù)、黑方空白格點(diǎn)數(shù)。這4個(gè)變量一旦確定,“相卒車馬炮”的擺法個(gè)數(shù)便隨之確定。求解子問題“相卒車馬炮”需要先考慮相的擺法。根據(jù)“將”是否已經(jīng)放在如圖3的1號位置,可以確定相的可選位置的個(gè)數(shù)。
對于“相”的每一種擺法,累加“卒車馬炮”的擺法個(gè)數(shù),即得“相卒車馬炮”的擺法總數(shù)。記子問題“卒車馬炮”的擺法個(gè)數(shù)為getZu(BPR, BPB,SpaceR, SpaceB),BPR、BPB分別表示紅相、黑相占用卒位的個(gè)數(shù),SpaceR、SpaceB分別表示紅黑雙方空白格點(diǎn)數(shù)。偽代碼如下:
函數(shù)名稱 getXiang
輸入 紅將位置KR;黑將位置KB;紅方空白格點(diǎn)數(shù)SpaceR;黑方空白格點(diǎn)數(shù)SpaceB;
輸出 “相卒車馬炮”的擺法總數(shù)s。
1) 定義紅相可去位置個(gè)數(shù):若KR不在相位上NR=7否則NR=6;
2) 定義黑相可去位置個(gè)數(shù):若KB不在相位上NB=7否則NB=6;
3) s=0;
4) 對于紅相個(gè)數(shù)的可取值BR∈{0, 1, 2};
5) 對于黑相個(gè)數(shù)的可取值BB∈{0, 1, 2};
6) 對于紅相占用紅卒位置的個(gè)數(shù)BPR∈{0, 1, 2};
7) 對于黑相占用黑卒位置的個(gè)數(shù)BPB∈{0, 1, 2};
8) 相的放法種數(shù) placeXiang=CBPRCBPBCBR-BPR
22NR-2 CBB-BPB;NB-2
9) s+=placeXiang×getZu(BPR, BPB, SpaceR-BR,SpaceB-BB)。
2.2.4 卒
經(jīng)過以上步驟,“將士相”擺放完成,“卒車馬炮”子問題的輸入包括4個(gè)變量:紅相占用卒位個(gè)數(shù)(可取值0、1、2)、黑相占用卒位個(gè)數(shù)(可取值0、1、2)、紅方空白格點(diǎn)數(shù)、黑方空白格點(diǎn)數(shù)。
“卒”需要分為2類進(jìn)行討論:過河卒和未過河卒。各方過河卒個(gè)數(shù)加上未過河卒的個(gè)數(shù)不超過5,需考慮紅方、黑方各自的過河卒、未過河卒共4類棋子的擺放。
為便于敘述,下面進(jìn)行一些符號約定:
1) SpaceB表示黑方棋盤空白格點(diǎn)數(shù),SpaceR表示紅方棋盤空白格點(diǎn)數(shù);
2) PR表示紅方未過河卒的個(gè)數(shù),PB表示黑方未過河卒的個(gè)數(shù);
3) P′R表示紅方過河卒的個(gè)數(shù), P′B表示黑方過河卒的個(gè)數(shù)。
下面以紅方為例,討論過河卒和未過河卒的擺放。
對于過河卒,有 CP′
R
種擺法,其中
SpaceB-PB
SpaceB-PB表示黑方空白格點(diǎn)數(shù),從這些空白格點(diǎn)選擇 P′
R個(gè)格點(diǎn)分配給 P′R個(gè)紅方過河卒。
對于紅方未過河卒,需要根據(jù)相占用的卒位個(gè)數(shù)和未過河卒的個(gè)數(shù)兩個(gè)變量求出有多少種放置方法,這個(gè)問題可以明確定義為:在2行5列共10個(gè)位置上,放置P個(gè)卒子,其中BP個(gè)相占用了卒位,每列至多放置1個(gè)卒子,在這些約束下求P個(gè)卒子的擺法總數(shù)。這個(gè)子問題解法如下:假設(shè)有i個(gè)卒子放在了被占用的列上,這i個(gè)卒子只有唯一位置。而剩下的P-i個(gè)卒子都有2個(gè)空白格點(diǎn)可選,共計(jì)2P-i種情況。未過河卒放法種數(shù)如式(3)所示:
對于卒子的每一種擺放方式,累加“車馬炮”的擺放方式即得“將士相”確定后,“卒車馬炮”的擺放方式。
2.2.5 車馬炮
“車馬炮”的擺放僅與一個(gè)變量有關(guān),即整個(gè)棋盤上空白格點(diǎn)數(shù)。枚舉紅黑雙方車、馬、炮的個(gè)數(shù),使用分步乘法計(jì)算有多少種擺法,如式(4):
jvmapao(RB, RR, HB, HR, CB, CR)函數(shù)可以使用分步乘法實(shí)現(xiàn)。例如,擺完“將相士卒”之后剩余80個(gè)空白格點(diǎn),在這些空白位置上擺放2個(gè)紅車、2個(gè)紅馬、2個(gè)黑炮。根據(jù)分步乘法很容易得出擺法總數(shù)為 C820×C728×C726。jvmapao函數(shù)實(shí)現(xiàn)如以下偽代碼所示:
函數(shù)名稱 jvmapao
輸入 空白格點(diǎn)數(shù)Space;紅黑雙方的車馬炮的個(gè)數(shù)RB、RR、HB、HR、CR、CB;
輸出 s,表示“車馬炮”的擺法總數(shù)。
1) s=CRB×CRR;SpaceSpace-RB
2) Space=Space-RB-RR;
3) s=s×CSHpBace×CSHpRa ce-HB;
4) Space=Space-HB-HR;
5) s=s×CCB×CCR。SpaceSpace-CB
利用2.2節(jié)中各個(gè)子問題的性質(zhì),可以對上述計(jì)數(shù)方法進(jìn)行優(yōu)化。各個(gè)部分輸入?yún)?shù)如圖4。
圖4描述了中國象棋狀態(tài)總數(shù)求解的5個(gè)子問題:
1) 將的擺放;
2) 士的擺放,輸入為紅將、黑將的位置;
3) 相的擺放,輸入為紅將、黑將的位置、紅黑雙方空白格點(diǎn)數(shù)4個(gè)變量;
4) 卒的擺放,輸入為紅黑雙方空白格點(diǎn)數(shù)、紅黑雙方相占用卒位的個(gè)數(shù);
5) 車馬炮的擺放,輸入為整個(gè)棋盤上空白格點(diǎn) 數(shù)。
每個(gè)子問題都有一條重要性質(zhì):輸入到輸出為單射。根據(jù)這條性質(zhì)可以為每個(gè)子問題建立一個(gè)映射表,保存函數(shù)輸入和輸出的映射關(guān)系。當(dāng)求解某個(gè)子問題時(shí),先查詢映射表,如果存在答案則不必再調(diào)用后續(xù)子問題進(jìn)行求解,直接查表得出;如果不存在,則求解該問題并把最終結(jié)果存儲到映射表中,以備下次查詢。這種方法相當(dāng)于為每個(gè)子問題加一層緩存,若未命中緩存則進(jìn)行求解并使用求解結(jié)果更新緩存,若命中緩存則直接返回緩存的答案。這種動態(tài)規(guī)劃技巧又叫備忘錄方法[15-18],是動態(tài)規(guī)劃的一種變形。
各個(gè)子問題之間具有單向依賴性,動態(tài)規(guī)劃方法避免了子問題之間?層層調(diào)用和重復(fù)調(diào)用。
實(shí)驗(yàn)得出中國象棋狀態(tài)總數(shù)具體數(shù)值為7 547 040 878 332 418 571 694 532 043 654 081 760 159,用科學(xué)計(jì)數(shù)法表示為7.54×1039.88?,F(xiàn)有文獻(xiàn)中提到的中國象棋狀態(tài)總數(shù)如表2所示,這些結(jié)果都遠(yuǎn)遠(yuǎn)高估了中國象棋空間復(fù)雜度。
表 2 參考文獻(xiàn)中中國象棋空間復(fù)雜度Table 2 The Space Complexity of the Reference Document
如果用定長編碼來描述中國象棋的一個(gè)狀態(tài),只需對中國象棋狀態(tài)總數(shù)取以2為底的對數(shù),得到結(jié)果132.47 bit,這就是描述一個(gè)棋盤狀態(tài)最少需要的bit數(shù)。若將中國象棋全部狀態(tài)使用定長編碼存儲下來,需要將狀態(tài)占用bit數(shù)乘以狀態(tài)個(gè)數(shù),結(jié)果為1.14×1029TB。
為佐證本文結(jié)論,使用另一種粗略方法估計(jì)中國象棋狀態(tài)總數(shù)。下面給出一種簡略的計(jì)算中國象棋狀態(tài)總數(shù)的方法,這種方法給出的是中國象棋狀態(tài)總數(shù)的上界。
首先,只考慮紅方的棋子擺放,記為s。中國象棋包括紅方和白方兩方,所以全部狀態(tài)總數(shù)為s2。下面介紹s的計(jì)算方法。
將和士擺放方法為 j iangShi=C93×3+C92×2+C91。將士在九宮中, C93表示選定3個(gè)位置,乘以3表示為“將”分配3個(gè)位置中的1個(gè)位置。
相的擺放方法數(shù)為: xiang=C72+C71+C70。卒的擺放需要考慮過河卒和未過河卒,過河卒有4放5方個(gè)式格。點(diǎn)故空卒間的,擺未放過方河式卒有有zu5=∑列5i=,0∑每5j=-列0iC有4i5×2C種5j×擺2j種。車的擺放方式: jv=C920+C190+C900。馬和炮的擺法與車完全相同。
在這種簡略計(jì)算方法中,忽略了棋子之間的互相影響,所以應(yīng)該采用分步乘法的方式計(jì)算s,只考慮紅方共有s=jiangShi×xiang×zu×jv3種擺法。
中國象棋狀態(tài)總數(shù)即為s2,約為1042.78??梢姶致怨烙?jì)結(jié)果與本文計(jì)算結(jié)果更為接近,即便是粗略估計(jì),現(xiàn)有文獻(xiàn)中的數(shù)據(jù)都是極不準(zhǔn)確的。
基于動態(tài)規(guī)劃的方法求解中國象棋狀態(tài)總數(shù)充分挖掘了中國象棋棋子著法規(guī)律,快速而準(zhǔn)確地求出了中國象棋狀態(tài)總數(shù),為描述中國象棋空間復(fù)雜度提供了充分依據(jù),實(shí)驗(yàn)證明現(xiàn)有文獻(xiàn)提供的數(shù)據(jù)遠(yuǎn)遠(yuǎn)不夠準(zhǔn)確。
該計(jì)數(shù)方法創(chuàng)新點(diǎn)包括兩方面:1)先難后易,把問題分解為若干個(gè)子問題,先擺放約束較多的棋子,后擺放約束較少的棋子,把影響后續(xù)擺放的因素作為參數(shù)向后傳遞;2)在求解每個(gè)子問題時(shí),動態(tài)規(guī)劃可以減少重復(fù)計(jì)算。
本文提出的計(jì)數(shù)方法可能的應(yīng)用方向包括:1)計(jì)算其他博弈游戲狀態(tài)總數(shù);2)用于構(gòu)造中國象棋棋盤局面哈希函數(shù),建立棋盤局面和數(shù)字之間的一一映射;3)尋找可暴力枚舉全部狀態(tài)的棋型,為構(gòu)建中國象棋殘局庫提供依據(jù)。