Nim理论初探——编程之美1.12

Nim Game是非常著名的游戏,它的变体也很多,在《编程之美》上也用了三章讲解它。
Wikipedia
 上说的很详细,本文则是在对wiki上的内容阅读后的个人理解。 游戏概述: 有M堆石子,每堆石子的个数不一定相同,Bob和Alice交替取石子,只能在一堆石子上取,取的数目任意,但不能为0,也不能大于此堆石子的数目。获胜的判定有两种:一种是谁先把石子去完谁获胜,这种叫Normal Play;另一种是最最后把石子取完谁输,这种叫Misere Play。 假设有M堆石子,(H1,H2…HM),XOR(H1…HM) = X,我们的目标是将 (H1,H2…HM)变为(0,0…0),即XOR()=0,那么我们说任何XOR()=0的情况都是“安全状态”,对于Normal Play来说,取石子的那个人都想让自己取完石子后,石子堆为“安全状态”。 存在两个定理:

  • 任何“不安全状态”都可以通过一次取石子变为“安全状态”
  • 任何“安全状态”都通过一次取石子只能变为不安全状态

所以,在Normal Play情况下,初始状态很重要,如果初始状态为“不安全状态”,Bob获胜(先取者获胜),否则Alice获胜。

那在Misere Play情况下又怎么样那,先看一下两个特殊情况:

  1. 所有的堆的石子都是1,且有偶数个堆,那么Bob一定获胜
  2. 所有的堆的石子都是1,且有奇数个堆,那么Alice一定获胜

好的,任何取石子者都希望自己去完后,存在上面“2”提到的情况——所有的石子堆都不大于2,且有奇数个大小为1的堆。 那么先取者Bob怎样可以使自己获胜呢:

  • 如果一开始就是上面“1”提到的状态,Bob必输
  • 如果一开始就是上面“2”提到的状态,Bob必胜
  • 不是“1”“2”状态,初始状态是“不安全状态”,Bob一开始完全可以按“Normal Play”的方式取石子,当发现可以使石子产生奇数个1时,则不按“Normal Play”的方式去取了,让其变为奇数个1。下面证明一下为什么这样Bob就会获胜:如果Alice想产生一个奇数个1的不安全状态,那Alice一定是从一个安全状态产生一个不安全状态的(因为Bob先取,他总会产生一个安全状态),那么产生奇数个1的安全状态就是“偶数个1”,此时Bob便会再取一个石子,直接产生一个奇数个1的不安全状态,不需要Alice产生
  • 不是“1”“2”状态,初始状态是“安全状态”,Bob怎么取都会是一个“不安全状态”,Bob想让此状态为奇数个1,但可以达到此状态的安全状态是偶数个1,这是“1”提到的情况,矛盾。因此Bob不可能产生奇数个1,因此决定权在Alice手上了,Alice会获胜。

假设初始状态是不安全状态,Bob要怎样取石子才能产生一个安全状态呢?方法很简单。 我们假设XOR()=X,将X于每一堆石子的数量T做异或操作,如果结果小于T,则Bob将取此堆的石子,取得个数为T-T^X。

那么《编程之美》的扩展第一题也就解决了。

下面的代码实现了上面讲的内容:

public class NimClass {
      
       private int XOR(int[] heaps){
             int ret = 0;
             for(int i=0;i<heaps.length;i++)
                  ret ^= heaps[i];
             return ret;
      }
      
       private int max(int[] heaps){
             int ret =0;
             for(int i=0;i<heaps.length;i++)
                   if(heaps[i]>ret)
                        ret = heaps[i];
             return ret;
      }
      
       /*
       * If Pair.nb_remove is zero , it means that this taker will lose.
       */
       public Pair nim( int[] heaps, boolean misere){
            
             int X = XOR(heaps);
            
             if(X==0){
                   /* The safe state */
                   if(max(heaps) > 1)
                         return new Pair (-1,0);
                   else{
                         for(int i=0;i<heaps.length;i++)
                               if(heaps[i]==1){
                                    heaps[i]=0;
                                     return new Pair (i,1);
                              }
                         return new Pair (-1,0);
                  }
            }
             else{
                   /* The unsafe state */
                   int chosen_heap=-1,nb_remove=-1;
                   for(int i=0;i<heaps.length;i++)
                         if((heaps[i]^X)<heaps[i]){
                              chosen_heap = i;
                              nb_remove = heaps[i]-(heaps[i]^X);
                              heaps[i] = heaps[i]^X;
                               break;
                        }
                   boolean hasTowMoreHeap = false;
                   int oneHeapNum = 0;
                   for(int i=0;i<heaps.length;i++)
                         if(i!=chosen_heap){
                               if(heaps[i]>1){
                                    hasTowMoreHeap = true;
                                     break;
                              }
                               else if (heaps[i]==1) oneHeapNum++;
                        }
                   if(misere){
                         if(hasTowMoreHeap)
                               return new Pair (chosen_heap,nb_remove);
                         else{
                              heaps[chosen_heap] = 1;
                               return new Pair (chosen_heap, nb_remove-1);
                        }
                  }
                   else{
                         return new Pair (chosen_heap, nb_remove);
                  }
            }
      }
      
       class Pair{
             int chosen_heap ,nb_remove ;
             public Pair(int chosen_heap, int nb_remove){
                   this.chosen_heap = chosen_heap;
                   this.nb_remove = nb_remove;
            }
      }

}
    原文作者:whosemario
    原文地址: https://blog.csdn.net/whosemario/article/details/8231564
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞