河内变异塔的高效算法

时间:2015-09-08 17:14:58

标签: algorithm recursion

所以这是河内问题塔的变种。这里的区别在于,您只能将一个环移动到右侧的杆(3个杆),如果您在第3个杆上,向右移动意味着您最终会在第一个杆上移动。 因此,例如,如果你只在杆1中有一个环并且如果你想将它移动到杆3,你必须首先将环移动到杆2,然后移动到杆3.现在是一个非常简单的递归算法(伪代码)解决这个问题如下:

// moving from pole A to pole B
SpecialTowerOfHanoi(n, A, B, C):
  if (n == 1):
    move ring from A to B
  else:
    // move the top n-1 to poll B
    SpecialTowerOfHanoi(n-1, A, B, C);

    // move the top n-1 from poll B to poll C
    SpecialTowerOfHanoi(n-1, B, C, A);

    move remaining ring from A to B

    // move n-1 from poll C to poll A
    SpecialTowerOfHanoi(n-1, C, A, B);

    // move n-1 from poll A to poll B
    SpecialTowerOfHanoi(n-1, A, B, C);

然而,这显然不是最有效的算法,以下应该更有效:

TowerOfHanoiMoveOnceToRight(n, A, B, C):
  if (n == 1):
    move ring from A to B
  else:
    // use a new function to move two times to the right from A to C
    TowerOfHanoiMoveTwiceToRight(n-1, A, C, B);
    move remaining ring from A to B

    // move n-1 from C to B
    TowerOfHanoiMoveTwiceToRight(n-1, C, B, A);

TowerOfHanoiMoveTwiceToRight(n, A, C, B):
  if (n == 1):
    move ring from A to B
    move ring from B to C
  else:
    // move n-1 from A to C
    TowerOfHanoiMoveTwiceToRight(n-1, A, C, B);
    move remaining ring from A to B

    // now only move n-1 from C to A (so once only)
    TowerOfHanoiMoveOnceToRight(n-1, C, A, B);
    move remaining ring from B to C

    // now move n-1 from A to C
    TowerOfHanoiMoveTwiceToRight(n-1, A, C, B);

显然,您可以证明第二种方式比通过一些非常繁琐的分析的第一种方式更有效。但是,我在概念上陷入了为什么第二个更有效率的问题。我想到这个的方式是你想尽快移动底部的一块,你只能在顶部的n-1被移动两次后才能这样做。因此,通过创建一个新函数,第一个函数(TowerOfHanoiMoveOnceToRight)看起来比SpecialTowerOfHanoi函数简单得多。但TowerOfHanoiMoveTwiceToRight看起来要复杂得多。我想对如何思考这个问题有一些见解。

2 个答案:

答案 0 :(得分:0)

好的,我认为考虑到这一点的逻辑(无证据)方法是在第二种算法中,您试图尽可能快地移动底部磁盘。你能做到这一点的唯一方法是将前n-1个磁盘移动到poll c。因为第二算法中的每一步都将底盘推进到正确位置,所以它不会做任何不必要的事情,因此效率更高。因此,例如,在第一个算法中,您首先将n-1堆栈从A移动到B,这并不会真正推进底部磁盘。如果你们中的任何人认为这是一个很好的思考方式,请告诉我。

答案 1 :(得分:0)

这比递归的要快:

unsigned long i;  
static const int MAXNUMBEROFDISKS = 32;
vector<int> pow2Vec;
uint_fast32_t mPinFrom    = 0;
uint_fast32_t mNumDisk    = 0;
unsigned long numDiskLong = 0;
uint_fast32_t mOffset[MAXNUMBEROFDISKS];
uint_fast32_t mDir[MAXNUMBEROFDISKS]          = { 0 };
uint_fast32_t mPositionDisk[MAXNUMBEROFDISKS] = { 0 };
const uint_fast32_t mRedirectArr[5] = { 2, 0, 1, 2, 0 };

Algos::Algos()
{ 
    for (int i = 0; i < MAXNUMBEROFDISKS; ++i)
    {
        pow2Vec.push_back(pow(2, i));
        mOffset[i] = 1;
    }

    for (int i = 1; i < MAXNUMBEROFDISKS; i += 2)
    {
        mDir[i] = 2;
    }

    mOffset[0] = 0;
}

void Algos::calculListBinExperiment(vector<tuple<int, int, int>>& listeFinale, int nbDisk)
{
    listeFinale.resize(pow2Vec[nbDisk] - 1);
    _BitScanForward(&i, nbDisk);
    for (int noCoup = 1; noCoup < pow2Vec[nbDisk] ; ++noCoup)
    {
        _BitScanForward(&numDiskLong, noCoup);
        mNumDisk = numDiskLong;
        mPinFrom = mPositionDisk[mNumDisk];
        mPositionDisk[mNumDisk] = mRedirectArr[mPositionDisk[mNumDisk] + mDir[mNumDisk + 
        mOffset[i]]];
        listeFinale[noCoup - 1] = make_tuple(mNumDisk, mPinFrom, mPositionDisk[mNumDisk]);
    }
}