河内的解决方案比O(2 ^ n)更好?

时间:2012-05-21 02:18:15

标签: java algorithm towers-of-hanoi

是否有河内塔楼的解决方案,其运行时间小于O(2 n )其中 n 是磁盘的数量移动?我的解决方案需要O(2 n )时间。

此外,下面的解决方案是递归。我们可以使用动态编程和memoization的概念在较短的时间内解决这个问题吗?

public void towersOfHanoi(
        int num, 
        MyStack<Integer> from,
        MyStack<Integer> to, 
        MyStack<Integer> spare
) {
    if (num == 1) {
        int i = from.pop();
        to.push(i);
        System.out.println("Move "+i+" from "+from.getName()+" to " + to.getName());
        return;
    }
    towersOfHanoi(num - 1, from, spare, to);
    towersOfHanoi(1, from, to, spare);
    towersOfHanoi(num - 1, spare, to, from);
}

MyStack是Java中Stack类的扩展版本,它添加了name字段和访问者。

此外,同一问题有任何变化吗?

5 个答案:

答案 0 :(得分:17)

鉴于解决河内塔楼总是花费2 ^ n - 1步......不,你不会找到更快的算法,因为只需要打印出台阶就可以得到O(2 ^ n)少计算它们。

答案 1 :(得分:9)

我不会证明(正如史蒂芬所做的那样),但我会尝试直观地解释2 ^ n-1是min: 在每个州,磁盘只有三种可能的移动方式。 让当前状态表示为有序的seq(1,1,...,1),使得第一个数字表示较大磁盘的位置,最后一个数字表示最小磁盘的位置。 (1,1,..,1)表示所有磁盘都在位置1上。同样来自(1,1,.1)只有两个下降状态:(1,1,... 2)和( 1,1,...... 3)。从(1,1,... 2)有三种下降状态:

  1. 返回(1,1,... 1)
  2. 转到(1,1,...,3)
  3. 转到(1,1,... 3,2)
  4. 如果继续,您将得到节点为可能状态的图形,边缘(转换)为“磁盘移动”。

    您将获得如下所示的图像(如果继续,它将看起来像三角形,顶点将是(1,1,... 1),(2,2,。。2),(3, 3,... 3))。步数实际上是图中的路径。

    如果沿着三角形的边缘行走,步数为2 ^ n-1。所有其他路径长度相同或更长。

    enter image description here

    如果您使用策略:移动除最大磁盘以外的所有磁盘放置3,然后将大移动到位置2,最后将所有窗体3移动到2,可以按以下方式设计公式:

    f(n)=
        f(n -1)//移动除1到3之外的所有最大值     + 1 //从1到2移动最大     + f(n -1)//将全部从3移动到2 - &GT;
    f(n)= 1+ 2 * f(n-1)

    该循环方程的解决方案为您提供该策略所需的步骤数(恰好是最小步数)

答案 2 :(得分:9)

河内塔的解决方案是不可避免的2 n 。但是,在动态编程解决方案中,每个子问题只计算一次,然后通过组合第一个子问题解决方案,当前磁盘移动和第二个子问题解决方案来解决问题。

因此,生成每个解决方案有两个组件:为当前解决方案分配内存,然后填充该内存。内存分配几乎与分配的内存大小无关,是昂贵的组件。内存复制在复制的内存大小上是线性的,虽然速度很快,但在n中是指数式的,作为塔的解决方案。

时间= c 1 * n + c 2 * 2 n ,其中c 1 &gt;&gt; ; ç<子> 2 。即,它开始线性并以指数结束。

Link to article appearing in ACM's SIGCSE Inroads magazine (September 2012)

答案 3 :(得分:1)

河内问题的标准塔有3个钉子。

但是,如果我们有k-pegs,时间复杂度将为O(2 ^(n /(k-2)))。

我用4个钉子和5个钉子解决了这个问题,时间复杂度分别为O(2 ^(n / 2))和O(2 ^(n / 3))

答案 4 :(得分:-1)

这一步比递归快约7%。它将移动内容存储在列表中,以便您以后可以使用它,否则,可以根据需要放入打印件并删除容器。

```
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]);
  }
}
```