1.2-编程之美-中国象棋将帅问题

来源:互联网 发布:java线程 第三版 编辑:程序博客网 时间:2024/06/11 19:26

真正有趣的是解法二,它的代码如下:

  int var = 81;
  while( var-- )
  {
    if( var / 9 % 3 == var % 9 % 3 )//发生冲突 
     continue;
    else
 printf(/** 打印可行的位置 **/);
  }

      当看到这个解法的时候,我心里有一些感慨。在前几个月,我一直未MSRA面试没通过而恼火。但看到这个解法之后,我觉得我确实还要再努力一些才行。短短几行,体现了简约之美,仅看看这个就值回钱了(开玩笑)。虽然可能有牛人说这没什么了不起,但我觉得如果我在面试这个问题的时候能写下这样的代码,我会很有成就感。在大多数时候我们无需知道希尔排序的时间复杂度的一点几次方是怎么算出来的,也无需去证明一个最优化问题是否满足“拟阵”的条件,我们只需要在这样一个“简单”的问题上做得漂亮,就够了。

     回过头来分析这个解法。“将”和“帅”各在自己的3*3的格子间里面走动,我们共需要验证9*9=81种位置关系,这也是i=81的由来。此外我们要明白 i/9和i%9的含义。我们知道,整数i可以由部两分组成,即var=(var/9)*9+var%9 ,其中var<n。我们注意到,在i从81到0变化的过程中,var%9的变化相当于内层循环,var/9的变话相对于内层循环。这样,作者就妙地用一个变量i同时获得了两个变量的数值。

      简单即是美,相对于解法一的大段代码,我更希望我以后再面试中写出解法二。

 问题是(1)我们如何用一个打印出相同的结果?(2)如果是N重循环呢?
   
     面对第一个问题,实际上就是对原始的中国象棋将帅问题进行了一个扩展,即在棋盘上添加一个“王”,其行走规则和将帅 一样。于是棋盘变成了三国争霸:-) ,将帅王可以走动的格子数分别为3、4、5,它们之间的互斥条件可以按需要设定。
   
     这时,就需要只用一个变量遍历一个三重循环。直观的方法是像方法一那样把一个4字节的INT拆开来用。我这里只关注方法二。
   
     只用一个变量解决扩展的中国象棋将帅问题,我们的代码应该是如下的样子:
   int var = 3*4*5;
   while( var-- )
   {
     if( /** 冲突条件 **/ )//发生冲突 
      continue;
     else    
         printf(/** 打印可行的位置 **/);
   }

     在冲突条件中,我们需要知道var取得某个特定的值(即第var+1次循环)的时候的i,j,k分别是多少(这样我们才能判定将帅位置是否冲突)
     从上例的结果中我们可以看到,counter的值(即当前的循环次数)和三元组(i,j,k)是一一对应的,越是外层的循环变化越慢,他们满足什么关系呢?
     k的取值最好确定,我们都知道是var%3。
     在原始的将帅问题中我们知道,j的值应该是 var/3,但是由于j上面还有一层循环,就需要做些调整,变成var/3%4
     最外层循环i的值则为(var/(3*4))%5.
          即:k=var%3      //其下没有循环了
             j=var/3   //其下有几个循环长度为3的循环
            i=var/(3*4). //其下有几个循环长度为3*4的循环
      于是4重循环的公式我们也可以轻松得出:
  for( int i = 0; i < 5; i++ )
     for( int j = 0; j < 4; j++ )
       for( int k = 0; k < 3; k++ )
         for( int p = 0; p < 3; p++ )
    
   p=var%2 //其下没有循环了
   k=var/2      //其下有几个循环长度为2的循环
   j=var/(2*3)) //其下有几个循环长度为2*3的循环
   i=var/(2*3*4)//其下有几个循环长度2*3*4的循环
   
   下面就是一个变量实现三重循环

int var = 2*3*4*5;
while( var-- > 0){
 System.
out.println("var="+var+" , i="+((var/(2*3*4))%5)+
                                                    
", j ="+((var/(2*3))%4)+",
                                                       k="+((var/2)%3)+",
                                                       p
="+var%2);
}

  结果是:
   var=119 , i=4, j=3, k=2, p=1
   var=118 , i=4, j=3, k=2, p=0
   var=117 , i=4, j=3, k=1, p=1
   ...中间略
   var=5 , i=0, j=0, k=2, p=1
   var=4 , i=0, j=0, k=2, p=0
   var=3 , i=0, j=0, k=1, p=1
   var=2 , i=0, j=0, k=1, p=0
   var=1 , i=0, j=0, k=0, p=1
   var=0 , i=0, j=0, k=0, p=0   
   
   N重循环原理也是一样,就不再赘述了。
   PS:看到最后一例的结果是不是与《算法导论》中平摊分析一章的二进制计数器很像?只不过这里进制不一样而已:-)
   [勘误: P19 代码清单1-7的第七行,应该改为if(i.a%3 != i.b%3)]

 


0 0
原创粉丝点击