• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            雁過無痕

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::

            《編程之美》讀書筆記061.13 NIM(3)兩堆石頭的游戲

             

            問題:

            假設(shè)有兩堆石子,兩人輪流取石子,每次可以從一堆取任意個石子,或者從兩堆取相等數(shù)量的任意個石子,但不能不取。

            若先把石子取光的一方為勝方,先取者有什么必勝策略?

            若先把石子取光的一方為輸方,先取者的策略要進行怎樣調(diào)整?

             

            記得初中時在學校門口的書店,買到一本《智力游戲中的數(shù)學方法》,當時如獲至寶。書中提到一個“皇后登山”游戲,就是在空的圍棋棋盤上放一個棋子,該棋子每次只能向上或向右或沿對角線向右上方向移動(和國際象棋中的皇后移動相似),可以移動任意格,但不能不移動,兩人輪流移動棋子,先將棋子移動到右上角者贏,問先移棋者的必勝策略。書中還提到這個撿石子游戲,并說明這兩個游戲是“同構(gòu)”,必勝策略是相同的。

             

            NIM游戲的“必勝策略”可以概括為:找出最終獲勝局面具有的某種性質(zhì),對具有該性質(zhì)的局面的一次操作得到的新局面必然不具有這種性質(zhì),而對不具有該性質(zhì)的局面,總可以通過一次操作,得到一個具有該性質(zhì)的新局面。假設(shè)游戲雙方分別為AB,只要A方能到達具有該性質(zhì)的某個局面,則B方一定不能到達具有該性質(zhì)的任意一個局面,從而不能到達獲勝局面,因而B方必敗,即A方必勝。這種性質(zhì)可以是對稱性(1.11的策略)、每堆數(shù)二進制表示各個位的和的奇偶性(1.12的策略)、屬于某個集合(1.13的策略)。

             

            對于1.13NIM游戲,就是尋找這樣的一系列關(guān)鍵局面(包括最終獲勝局面),總可以通過一次操作將一個非關(guān)鍵局面轉(zhuǎn)為關(guān)鍵局面,而一次操作必將一個關(guān)鍵局面轉(zhuǎn)為非關(guān)鍵局面。因此,從一個關(guān)鍵局面到另一個關(guān)鍵局面至少要(并且能只要)兩次操作(“兩次操作原則”)

             

            <a, b> (a<=b)表示兩堆的石子數(shù)分別為ab的局面。對所有的關(guān)鍵局面<x, y>,按x值大小升序排列,最終可用<Fn, Gn >(其中 Fn <= Gnn>=0)來表示第n個關(guān)鍵局面Mn, 并記<F0, G0 >為最終獲勝局面M0(對拿到最后一個獲勝的,M0=<0,0>,而對拿到最后一個輸?shù)模?span lang=EN-US>M0=<0,1>),定義Tn=Gn-Fn >=0。每次取石子的操作規(guī)則為:從某一堆取任意個石子,或從兩堆同時取任意個相同的石子。從M0開始利用這個規(guī)則去除不合的局面。根據(jù)規(guī)則,顯然在已知M0 M1 … Mn 情況下,Mn+1 應(yīng)該滿足:Fn+1不與任意一個FiGi0<=i<=n)相同,Tn+1也不與任意一個Ti0<=i<=n)相等。因而,可以假設(shè):Fn+1是不與任意一個FiGi0<=i<=n)相同的最小非負整數(shù),Tn+1是不與任意一個Ti相等的最小非負整數(shù)(0<=i<=n。由該假設(shè)得到的<Fn, Gn >即為關(guān)鍵局面,符合“必勝策略”。

             

            (證:由假設(shè),顯然有Fi >= Ti (i>=1)

             對關(guān)鍵局面<Fn, Gn >,設(shè)石子較少的那堆為X,較多的那堆為Y

              A方若在X堆,或在兩堆同時取k個石子,則由假設(shè)可知,X堆剩余的石子數(shù)必然與某個i或某個Gi0<=i<n)相等,B方可在Y堆取石子,到達另一個關(guān)鍵局面< Fi, Gi >)。

            ⑵ A方若在Y堆取走k個石子:

              k <= Tn,由假設(shè)必然可找到i,使得Ti=Tn-k0<=i<n),B方只要在兩堆同時取Fn-Fi個石子,即可到達關(guān)鍵局面< Fi, Gi >)。

            ⒉ 若k > Tn,則B堆剩余石子小于Fn,必然與某個i或某個Gi0<=i<n)相等。

            A 若與某個Gi相等,則A堆只要再取Fn-Fi個石子,即可到達關(guān)鍵局面< Fi, Gi >

            B  若與某個Fi相等:

            如果Fn>Gi則在則A堆只要再取Fn-Gi個石子,即可到達關(guān)鍵局面< Fi, Gi >

            如果Fn<Gi,必然可以找到j0<=j<i)使得Tj=Fn-Fi,兩堆同時取Fi-Fj個石子到達關(guān)鍵局面< Fj, Gj >

             對非關(guān)鍵局面<a, b>,由假設(shè)可知,a必與某個i或某個Gi0<=i<n)相等,因而,可以從另一堆取石子數(shù),到達關(guān)鍵局面< Fi, Gi >;或從關(guān)鍵局面< Fi, Gi >某堆取石子到達非關(guān)鍵局面<a, b>,而由上面的證明可知,可以再通過一次操作,到達某個關(guān)鍵局面< Fj, Gj >。因此,總可以一次操作從非關(guān)鍵局面到達關(guān)鍵局面。

            總之,由假設(shè)得到的關(guān)鍵局面符合“必勝策略”。)

             

            拿最后一個贏:

            n

            0

            1

            2

            3

            4

            T

            0

            1

            2

            3

            4

            F

            0

            1

            3

            4

            6

            G

            0

            2

            5

            7

            10

            由于T0=0,根據(jù)Tn的定義可得Tn=n,剛好是beatty序列,可由beatty序列的通項公式求得Fn的通項公式

             

            拿最后一個輸:

            n

            0

            1

            2

            3

            4

            T

            1

            0

            2

            3

            4

            F

            0

            2

            3

            4

            6

            G

            1

            2

            5

            7

            10

            n>=2時,關(guān)鍵局面與拿最后一贏的相同。

             

            勝利條件為拿最后一個贏時,要找出所有關(guān)鍵局面(即書中的不安全點),最快的辦法就是得用通項公式,雖然通項公式有無理數(shù),轉(zhuǎn)成浮點數(shù),引起的誤差對結(jié)果沒多大影響(只有當N極大時,才會使結(jié)果不對)。對浮點計算十分昂貴的平臺,還是用篩選法,利用“在兩個GiGj間的數(shù)必然是某個Fk值”,篩選過程只須保留一部分Gn就可以計算出新的Fn

            另外,對關(guān)鍵點<Fn, Gn=Fn+n>,當n>1時,從1FnFn個數(shù),有nFi,還有Fn-n個在Gj。因而0<Fn-n<n,即有:n< Fn<2*n (n>1),在判斷<x,y>y>=x)是否是關(guān)鍵點,先判斷是否滿足這個不等式,如果滿足,才生成第y-xF值再進行判斷。由通項公式可知,

            Fnint(1.618*n),可以強化邊界條件為:int(n*3/2)int(n*7/4)

             

            (書上給出的定理是beatty定理。該定理的證明相當簡單,有興趣的可以google下。另外,上個世紀五六十年代,我國數(shù)學家曾在《數(shù)學通論》(有網(wǎng)絡(luò)版)發(fā)表過證明,好像還用到了Fibonacci的通項公式。)


            void nim(int num) //print all the unsafe points
            {
              assert(num
            >0);
              vector
            <int> g(num+3);
              
            int n=2;            // f[2] is between [ g[1]+1 , g[2]-1 ]
              g[1]=2; g[2]=99;   //  g[2] should be initialized with a value above g[1]+1=3.
              int fn=g[1];
              
            int *low=&g[2];
              
            int *high=&g[n];
              
            for( ; n <= num; ++n){
                
            if++fn == *low){
                  
            ++low;
                  
            ++fn;
                }

                
            *high++ = fn + n;      //f(n)=fn, g(n)=f(n)+n
              }


              
            for(int i=1;i<=num;++i) {
                
            //const double SS=(1.0+sqrt(5))/2.0+1.0;
                
            //if ( (int)(i*SS) != g[i] )   cout<<"Error: " << i<< "\n";
                cout<< g[i]-i<< ","<<g[i]<<"  ";
              }

              cout
            <<endl;

            }




            bool nim(int x, int y)     //whether win the game
            {
              assert(x
            >=0);
              assert(y
            >=0);
              assert( 
            !(x==0 && y==0));
              
            int ff=x, gg=y, num;
              
            if (x > y) ff=y, gg=x;   
              num
            =gg-ff;            
              
            if ( ff < num || ff > ( num << 1) ) return true;  //win the game

              vector
            <int> g(num+3);
              g[
            1]=2; g[2]=99;
              
            int n=2, fn=g[1];
              
            int *low=&g[2];
              
            int *high=&g[n];
              
              
            for( ; n <= num; ++n){
                
            if ( ++fn == *low) ++low; ++fn;}
                
            *high++ = fn + n;
              }
              

             
            if (*--high == gg) return false;
             
            else return true;
            }



            posted on 2010-08-15 23:58 flyinghearts 閱讀(2057) 評論(0)  編輯 收藏 引用 所屬分類: 編程之美
            久久久WWW成人| 久久久久亚洲精品天堂久久久久久| 久久婷婷五月综合97色直播| 久久久久久久久久久精品尤物| 日本欧美久久久久免费播放网| 久久久91精品国产一区二区三区 | 国产精品VIDEOSSEX久久发布| 999久久久免费国产精品播放| 97精品伊人久久大香线蕉| 久久久久久亚洲Av无码精品专口| 精品久久久噜噜噜久久久 | 成人综合久久精品色婷婷| 国产成人精品久久一区二区三区| 国内精品久久久久久中文字幕| 国产成人无码精品久久久性色| 成人亚洲欧美久久久久| 久久精品国产亚洲AV电影| 久久青青色综合| 久久国产香蕉一区精品| 久久精品国产99国产精偷| 亚洲AV日韩精品久久久久久久 | 亚洲精品乱码久久久久久蜜桃不卡| 91精品日韩人妻无码久久不卡| 久久婷婷成人综合色综合| 久久亚洲精品无码aⅴ大香| 久久露脸国产精品| 亚洲国产天堂久久综合网站| 99久久精品午夜一区二区| 久久人人爽人人爽人人av东京热| 久久精品国产99久久香蕉| 久久国产成人午夜AV影院| 91精品国产综合久久四虎久久无码一级| 日韩精品久久无码人妻中文字幕| 久久人人爽人人爽人人片AV东京热 | 2021国产精品久久精品| 一本一道久久a久久精品综合| 久久久久亚洲精品中文字幕| 国产综合免费精品久久久| 精品无码久久久久久国产| 天堂无码久久综合东京热| 四虎国产精品免费久久|