递归通常很直白地描述了一个求解过程,因此也是最容易被想到和实现的算法。循环其实和递归具有相同的特性(即:做重复任务),但有时,使用循环的算法并不会那么清晰地描述解决问题步骤。单从算法设计上看,递归和循环并无优劣之别。然而,在实际开发中,因为函数调用的开销,递归常常会带来性能问题,特别是在求解规模不确定的情况下。而循环因为没有函数调用开销,所以效率会比递归高。除少数编程语言对递归进行了优化外,大部分语言在实现递归算法时还是十分笨拙,由此带来了如何将递归算法转换为循环算法的问题。算法转换应当建立在对求解过程充分理解的基础上,有时甚至需要另辟蹊径。  
     前段时间遇到过这样的问题:已知一2D地图格子的长宽(w、h)及每个格子的边长(a,格子为正方形),给定物体的2D坐标(pos[x , y])及半径(r),求解物体在2D地图格子中所占的格子,仅考虑n*n的情况。大概的求解过程如下:  
1)根据半径,确定n*n中的n。假定计算公式为:n = Round(2*r / a)  
2)根据2D坐标得到物体的“中心格子”。根据n的奇偶,计算公式不同,如下图所示。  
 
                  
 n为偶数[1]                                                                    n为奇数[2]
 [1]:grid(x , y) = Round(pos / a)  
[2]:grid(x , y) = Floor(pos / a)  
其中,格子坐标x >= 0 , y >= 0。  
3)以“中心格子”为基础,求出物体占据的其他格子。这样的描述,让人容易想到递归,就像用深度优先方法遍历树那样,伪代码算法如下:
 if n is even
{
    get the index of 'center grid' (row , col)
    ExtendHeldGrid(row , col , n)
    ExtendHeldGrid(row - 1 , col , n)
    ExtendHeldGrid(row - 1 , col - 1 , n)
    ExtendHeldGrid(row , col - 1 , n)
}
else
{
    get the index of 'center grid' (row , col)
    ExtendHeldGrid(row , col , n)
}
function ExtendHeldGrid(row , col , level)
{
    if(level <= 0)
        return
    if((row >= 0 and row < MaxGridWidth) and (col >= 0 and col < MaxGridHeight))
    {
        mark the grid(row , col)
        ExtendHeldGrid(row , col , level - 2)
        if(level - 2 > 0)
        {
            ExtendHeldGrid(row + 1 , col , level - 2)
            ExtendHeldGrid(row - 1 , col , level - 2)
            ExtendHeldGrid(row , col + 1 , level - 2)
            ExtendHeldGrid(row , col - 1 , level - 2)
            ExtendHeldGrid(row + 1 , col + 1 , level - 2)
            ExtendHeldGrid(row - 1 , col + 1 , level - 2)
            ExtendHeldGrid(row + 1 , col - 1 , level - 2)
            ExtendHeldGrid(row - 1 , col - 1 , level - 2)
        }
    }
}
虽然,该算法得到了正确的求解结果,但是由于每个格子都会标记周围的8个格子,所以存在大量的重复,再者如果上面的过程每帧都进行的话,函数调用开销也是相当可观。
循环自然是不可避免的,消除重复便成了优化的目标。分析格子图和n为2和3的情况,试图找出用循环代替递归的方法,我发现了下面一个有趣的规律:
 
                
从“中心格子”出发,顺时针(或逆时针)以上图方式可以走遍所求解的每个格子而不重复。在实现上,每个转角也是有规律的,可以通过一个2*2的转角矩阵来控制:
[1 , 0][0 , -1]
[0 , 1][-1 , 0]
顺时针方式的转角阵
 
矩阵中的每个元素代表从当前格子走到下个格子在row和col上的变化。加之,在转角之间的路长(以格子个数计)有每转两次递增单位1的规律,算法就不难得到了,下面同样以伪代码示:
conerMat = 
{
    {0 , -1} , 
    {-1 , 0} ,
    {0  , 1} ,
    {1  , 0}
}
dir = 0     /// 转角控制,四个转角顺时针0~3
span = 1    /// 转角间的跨度
count = 1   /// 每两次增加一个跨度
rin = 1     /// 下一个转角的循环索引
if n is even
    get the index of 'center grid' (row , col)
else
    get the index of 'center grid' (row , col)
for(i = 0; i < n * n; ++i)
{
    if((row >= 0 and row < MaxGridWidth) and (col >= 0 and col < MaxGridHeight))
        mark the grid(row , col)
    if(i == rin)
    {
        dir = (dir + 1) % 4
        if(count == 2)
        {
            ++span
            count = 1
        }
        else
            ++count
        rin = i + span
    }
    row = row + conerMat[dir][0]
    col = col + conerMat[dir][1]
}
用MFC程序验证了一下算法的正确性,标号展示了循环的路线(注意GDI的坐标系中Y的正方向朝下):
