武汉博文官方博客http://blog.csdn.net/bvbook

技术凝聚实力 专业创新出版 与向上的心合作 共同成长!

原创 《编程之美》读书笔记(一):中国象棋将帅问题收藏

《编程之美》读书笔记(一):中国象棋将帅问题

 
         作者:薛笛     


     

      千呼万唤始出来,在跳票了快一个月之后,虽然明知道书里还有不少错误没改过来(附了一整页的勘误),但是感觉已经不能等下一版了。赶快去书店买回来,吃完饭躺床上舒舒服服地看。大致翻看之后,总体感觉是书中的内容没有“脱离群众”,很多都是我们平时生活、工作中经常能遇到的。题目不见得难,基本上给一本《算法导论》和足够的时间,大多数人都能解决其中的问题。但注意副标题--“微软技术面试心得”,这就给这本书定下一个基调:面对这些我们并不陌生、也并非特别困难的问题,在有限的时间里,(可能)比较紧张的心情之下,如何充分发挥自己分析问题和解决问题的能力,如何正确且漂亮地解决问题才是关键。我想,在平时学习的时候或许我们左手《算法导论》,右手《编程之美》效果会更好一些。

  中国象棋将帅问题由于比较简单,所以我们暂时不用请出《算法导论》。该问题的具体描述是:(根据中国象棋的基本原则)在只有双的将帅棋盘上,找出所有双方可以落子的位置(将帅不能碰面),但只能使用一个变量。直觉上我们想到,只要遍历将帅所有可能的位置,去除将帅冲突的位置即可。可见,剩下的问题就在于如何使用一个变量来做二重循环的遍历。书中解法一给出的方法是将一个Byte变量拆成两个用,前一半代表“帅”可以走的位置,后一个变量代表“将”可以走的位置(事先已经将“将”和“帅”可以走的3*3的位置进行了编号),利用位操作即可获得两个计数器的功能。书中的解法三采用结构体来解决一个变量遍历二重循环的问题,思想上换汤不换药。真正有趣的是解法二,它的代码如下:

  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同时获得了两个变量的数值。

  简单即是美,相对于解法一的大段代码,我更希望我以后再面试中写出解法二。
 
  其实这个问题还可以进行一些扩展,即如何利用一个变量达到三重循环的效果。也就是说,如果给定下面的循环:

  int counter = 0;
  for( int i = 0; i < 5; i++ )
 for( int j = 0; j < 4; j++ )
   for( int k = 0; k < 3; k++ )
   {
    System.out.println("counter="+counter+"\t, i="+i+", j="+j+", k="+k);
    counter++;
   }
   其结果如下:
 counter=0 , i=0, j=0, k=0
 counter=1 , i=0, j=0, k=1
 counter=2 , i=0, j=0, k=2
 counter=3 , i=0, j=1, k=0
 counter=4 , i=0, j=1, k=1 
    ....中间略
    counter=59  , i=4, j=3, k=2
  
   问题是(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)]

  谨以此文与大家共勉 2008/04/05

 

发表于 @ 2008年04月09日 13:57:00|评论(loading...)

新一篇: 横评:一本书,两个人 | 旧一篇: Application = Code + Markup

用户操作
[即时聊天] [发私信] [加为好友]
武汉博文视点
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
武汉博文视点的公告
参加读者调查,免费获取新书
博文视点,年底重磅原创
Web2.0好书推荐
文章分类
收藏
    博文视点合作伙伴
    01.《程序员》杂志
    02.CSDN读书频道
    03.51CTO读书频道
    04.博客园
    05.JavaEye技术网站
    06.中国互动出版网
    07.卓越亚马逊
    08.华储网
    09.新风雨网络书城
    10.当当网
    11.IT专家网Linux频道
    博文视点图书网站
    《RESTful Web Services中文版》官方网站
    《Silverlight 2完美征程》官方网站
    《代码大全》官方网站
    《移山之道》读者社区(RSS)
    《编程之美》“智慧碰撞”专栏
    《走出软件作坊》官方论坛
    《软件调试》官方网站
    博文视点团队博客
    策划营销团队博客
    编校团队博客
    设计团队博客
    博文视点专家博客
    DBA notes-冯大辉
    Delphi专家-李维
    GIS专家-蒋波涛
    InfoQ中文站主编-霍泰稳
    ITECN博客主编-盆盆
    LVS创始人--章文嵩
    MySQL专家-简朝阳
    OSGi先锋-林昊
    Python源码剖析-陈儒
    Silverlight 2完美征程-李会军
    Windbg专家-熊力(RSS)
    Windows系统技术研究-潘爱民
    冒号和他的学生们-郑晖
    大道至简-周爱民
    开源网格GIS实验田-高昂
    搜索专家-车东
    测试专家-陈绍英
    移山之道-邹欣
    系统架构师-张宴
    设计模式-王翔
    走出软件作坊-阿朱
    软件调试-张银奎
    轻轻松松自动化测试-朱少民
    存档
    Csdn Blog version 3.1a
    Copyright © 武汉博文视点