您的位置:首页 > 编程语言

读书笔记--《编程之美》--中国象棋将帅问题

2016-06-14 14:45 211 查看
《编程之美》---1.2 中国象棋将帅问题

中国象棋将帅问题由于比较简单,所以我们暂时不用请出《算法导论》。该问题的具体描述是:(根据中国象棋的基本原则)在只有双的将帅棋盘上,找出所有双方可以落子的位置(将帅不能碰面),但只能使用一个变量。直觉上我们想到,只要遍历将帅所有可能的位置,去除将帅冲突的位置即可。可见,剩下的问题就在于如何使用一个变量来做二重循环的遍历。

书中解法一给出的方法是将一个Byte变量拆成两个用,前一半代表“帅”可以走的位置,后一个变量代表“将”可以走的位置(事先已经将“将”和“帅”可以走的3*3的位置进行了编号),利用位操作即可获得两个计数器的功能。书中的解法三采用结构体来解决一个变量遍历二重循环的问题,思想上换汤不换药。真正有趣的是解法二,它的代码如下:

int var = 81;

while( var-- )

{

if( var / 9 % 3 == var % 9 % 3 )//发生冲突

continue;

else

printf(/** 打印可行的位置 **/);

}

“将”和“帅”各在自己的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:看到最后一例的结果是不是与《算法导论》中平摊分析一章的二进制计数器很像?只不过这里进制不一样而已:-)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: