【问题标题】:Idea for keep information about visited states保留有关访问状态的信息的想法
【发布时间】:2011-08-05 09:44:06
【问题描述】:

我现在正在制作 15 谜题求解器(在 C++ 中),但我的程序不仅必须解决 15 谜题,还必须解决 3x4 谜题、8x8 谜题等... - > X x Y 谜题。我必须以某种方式保留有关已访问状态的信息,我的第一个想法是制作树,例如:
谜题:

状态 1
1 2
3 0

状态 2
1 3
0 2

我留在我的树上:

根->1->2->3->0
\_
\->3->0->2

这也适用于 5x3、6x6 等拼图,适用于所有拼图

这个想法行得通,但是很浪费内存,而且添加节点需要一些时间:/所以效率很低。

下一个想法是在 stl 的 std::map 中保持访问状态,但我不知道如何制作好的散列函数 - 从拼图状态制作快捷方式(因为我不必存储拼图状态,我只需要已访问过的信息。您有什么想法,对于 std::map 的密钥,或其他保持已访问状态信息的想法吗?

【问题讨论】:

  • std::map 是红黑树,而不是哈希。
  • 我现在但是,我想例如: std::map ,然后我可以将状态 4x4“压缩”到一个 int,但对于更大的状态它将不起作用(例如 8x8,那么我的数字最大为 64!:()

标签: c++ algorithm data-structures map hash-function


【解决方案1】:

Zobrist hashing 在玩抽象游戏的程序中几乎无处不在。

【讨论】:

  • 顾名思义是一个有冲突的哈希函数,不能用来存储实际状态
  • 对,这就是为什么你将它用作哈希表中的哈希函数
  • 另外,如果你将哈希计算到 160 位,那么碰撞的概率小于宇宙射线以其他方式破坏计算的概率。
  • 好吧,如果你正在解决一个 8x8 的谜题,它有 2^296 个不同的状态,所以一个 160 位的哈希函数会有很多冲突。对于 4x4,您可以将整个状态简单地存储在 2^128 位中(参见 A.S. 的回答),那么使用大于实际状态表示的哈希值有什么意义呢?
  • @antti.huima 显然存在冲突,但由于 Zobrist 哈希是 2-universal 并且求解器忽略了哈希向量,因此它们极不可能在合理的时间内发生。
【解决方案2】:

假设您只对解决难题 X*Y 感兴趣,其中 X, Y

template<unsigned X, unsigned Y>
class PuzzleState {
    unsigned char state[X*Y];
    public:
    void set(unsigned x, unsigned y, unsigned char v) {
        state[x+X*y] = v;
    }
    unsigned get(unsigned x, unsigned y) {
        return state[x+X*y];
    }
    bool operator<(const PuzzleState<X, Y>& other) const {
        return memcmp(state, other.state, X*Y) < 0;
    }
};

然后只需将std::set&lt;PuzzleState&lt;8,8 &gt;insertfind 方法一起使用。测试后如果性能仍然不合适,你可以抛出一个哈希表而不是std::set,用一个简单的哈希函数,比如Rolling hash

【讨论】:

    【解决方案3】:

    我会将单个状态表示为 BigInteger 数字(可用的 c++ 实现 here,或者如果您想实现自己的 here 也是一个线程)。

    假设您有一个具有 (X,Y) 维度的拼图,您将使用 base = X*Y 创建一个数字,该数字的数字将表示拼图拼成一维。

    例如:

    State 1
    1 2
    3 0
    

    这将被展平为

    1 2 3 0
    

    然后转换为以 4 为底的数字,如:

    state = (1 * (4^3)) + (2 * (4^2)) + (3 * 4) + 0;
    

    这将唯一标识任何谜题的任何给定状态。

    【讨论】:

    • 嗯,再考虑一下,这种方法在 15x15 甚至更大的拼图上的内存效率不会很高。我很好奇其他人会提出什么建议。
    • 它比将其存储在向量中(相当于使用base 2^32)更节省内存,甚至没有提及列表。事实上,这是存储状态的最小方式。这取决于大整数实现的开销(例如,与普通 new 等相比,从池中分配将节省相当多的开销)。
    • 它不是最小的,因为状态是数字 1..XY 的排列(使用数字 XY 表示空槽),所以有 (XY)!状态(虽然只有 1/2 是可达的)但是功率编码允许 (XY)^(XY) 状态,这更多。例如。 15-puzzle 有 2^44 种可能的状态(并非全部可达),但 16^16(幂编码)等于 2^128,所以这种编码浪费了 80 多位。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-03
    • 2020-04-15
    • 2021-08-07
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 2014-09-30
    相关资源
    最近更新 更多