数字华容道的数学原理

144 篇文章 18 订阅
53 篇文章 1 订阅

给儿子买了一套数字华容道的棋盘,结果毫不意外地成了我打发时间的工具,玩儿多了后就有了一些心得,结合网上的资料,总结如下图所示。

这是一个NPC问题,目前还没有通用解法。更深刻的数学原理涉及到群论了,群论在大学数学里面有着完全不一样的画风,没有深入学习过。

以三阶为例,可以验证,每个复原步骤均满足上述条件:


共计48步,这个步数必定是个偶数,因为黑框从最后一个位置再回到最后的位置,必定为偶数,根据群的基本理论,所以必须保证初始状态的逆序数也为偶数。这样完成后才满足偶置换的要求,如上的所有条件都是为了满足这个要求

写了一个深搜程序,计算出来四种方案,步骤都非常多,可见如果想得到最优补数需要用到广搜,深搜一头走到黑,得到的一般不是最优结果,如果限制路径堆栈的深度,可以得到接近最优的搜索路径:

我们按照程序计算出来的顺序实际操作一遍:

up->left->left->up->right->right->down->left->left->up->right->right->down->left->left->up->right->right->down->down->left->up->up->right->down->left->down->right->up->up->left->down->left->up->right->right->down->down->left->up->left->down->right->up->right->down->left->up->right->down

按照程序给出的操作步骤,顺利完成了3阶华容道的还原。

程序在限定步数前后,搜索步数会出现较大差异,是由于深度搜索过程中,首次出现的某个可解状态并非以最优次序出现,导致即便后续最优序列出现的时候,由于状态重复而被剪枝。

限定最大不超过30步的情况下的搜索结果:

left->left->up->right->right->up->left->down->right->up->left->down->left->down->right->up->right->down->left->left->up->right->up->left->down->right->down->left->up->right->right->down

空格和空格行数

空格水平位置的变化不影响逆序数,但是垂直方向上的变化却可能影响逆序数的奇偶性变化,并且这种影响和华容道的阶数有关,对于三阶来说,空格上下移动,相当于数字向前/向后连续置换了两次,次数为偶数,所以逆序数仍然是偶数保持不变,可以不用考虑空格行数的变化。

但是对于四阶来说,上下移动后,相当于一个数字向前/向后移动了三个块,发生三次置换,次数为奇数,所以逆序数的奇偶性一定发生了变化,需要加上空格行数作为补偿。是偶数才能复原。

进一步引申,奇数阶的数字华容道是否有解,只和当前数字排列的奇偶性有关,逆序数为偶数才有解。偶数阶的数字华容道,除了要考虑数字排列逆序数之外,还需要考虑到空格所在的行数。


结束!

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
数字华容道是一个经典的拼图游戏,也被称为“拼图游戏中的魔方”。在这个游戏中,有一个 $4\times 4$ 的方格,其中有 $15$ 个数字方块和一个空格,玩家需要通过移动数字方块,使得它们按照从 $1$ 到 $15$ 的顺序排列,空格则是用来移动数字方块的。 数字华容道的实现可以使用 C++ 的面向对象特性,将方格和方块抽象为对象,实现它们的移动和交换操作。以下是一个简单的实现示例。 ```c++ #include <iostream> using namespace std; // 数字方块类 class NumberBlock { private: int value; // 方块上的数字 int row; // 方块所在的行号 int col; // 方块所在的列号 public: NumberBlock(int value, int row, int col) { this->value = value; this->row = row; this->col = col; } // 获取方块上的数字 int getValue() { return value; } // 获取方块所在的行号 int getRow() { return row; } // 获取方块所在的列号 int getCol() { return col; } // 设置方块所在的行号和列号 void setPosition(int row, int col) { this->row = row; this->col = col; } }; // 数字华容道类 class DigitalHuRongDao { private: NumberBlock* blocks[4][4]; // 数字方块的二维数组 int emptyRow; // 空格所在的行号 int emptyCol; // 空格所在的列号 public: DigitalHuRongDao() { // 初始化数字方块 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int value = i * 4 + j + 1; if (value == 16) { blocks[i][j] = nullptr; emptyRow = i; emptyCol = j; } else { blocks[i][j] = new NumberBlock(value, i, j); } } } } // 输出数字华容道的当前状态 void print() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (blocks[i][j] == nullptr) { cout << " "; } else { cout << blocks[i][j]->getValue() << " "; } } cout << endl; } cout << endl; } // 移动数字方块 bool move(int row, int col) { if (row < 0 || row > 3 || col < 0 || col > 3) { return false; } if (blocks[row][col] != nullptr) { return false; } if (row == emptyRow && col == emptyCol) { return false; } NumberBlock* block = blocks[emptyRow][emptyCol]; blocks[emptyRow][emptyCol] = blocks[row][col]; blocks[row][col] = block; if (blocks[emptyRow][emptyCol] != nullptr) { blocks[emptyRow][emptyCol]->setPosition(emptyRow, emptyCol); } emptyRow = row; emptyCol = col; return true; } // 判断数字华容道是否已经完成 bool isCompleted() { int value = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (blocks[i][j] != nullptr) { if (blocks[i][j]->getValue() != value + 1) { return false; } value++; } } } return true; } }; int main() { DigitalHuRongDao dh; dh.print(); while (!dh.isCompleted()) { int row, col; cout << "请输入要移动的数字方块的行号和列号(用空格分隔):"; cin >> row >> col; if (dh.move(row, col)) { dh.print(); } else { cout << "无效的移动,请重新输入!" << endl; } } cout << "恭喜你,完成了数字华容道!" << endl; return 0; } ``` 在这个示例代码中,`NumberBlock` 类表示数字方块,它有一个数字和一个位置(行号和列号)属性,并且可以获取和设置这些属性。`DigitalHuRongDao` 类表示数字华容道,它有一个 $4\times 4$ 的数字方块二维数组和一个空格位置属性,可以初始化数字方块、输出当前状态、移动数字方块和判断是否已经完成。在 `main` 函数中,首先创建一个数字华容道对象,然后进入一个循环,不断接受用户输入的移动指令,直到数字华容道被完成为止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值