我为Loyd的15 Puzzle工作了最佳求解器,我遇到了一种使用7-8不相交模式数据库的技术(Korf,Richard E.和Felner,Ariel 2001,Disjoint模式数据库启发式,人工智能 2002年1月。)
描述该技术的论文指出,每个条目,包括拼图的修改版本的独特状态和短整数启发式,花费一个字节或更少来存储在数据库中。
这引出了我的问题:如何在16个可能的位置存储8个数字的每个唯一排列,并在一个字节中存储一个短整数?或者我是在错误的轨道上并且应该存储不同的东西?
提前谢谢!
答案 0 :(得分:0)
根据洋泾洞的原则,你显然是在错误的轨道上。
pidgin-hole原则说,如果没有在一个洞中放置多个pidgins,你就不能把更多的pidgins放入孔中。 (还有一个无限版本,在有限数量的孔中有无数个pidgins意味着一个洞会随着无数个pidgins而结束。)
在这种情况下,在16个可能的位置有518918400个8个数字的唯一排列。一个字节只能存储256个唯一的东西。它不合适。
您需要以某种方式存储更少的信息或欺骗它的存储方式。例如,您可以存储具有4 GB地址的数组。只需读取基数为16的数组元素的位置即可获得它的排列,并且数组元素的内容会告诉您与该排列相关的信息。
答案 1 :(得分:0)
文章不是很明确,但技术相当清楚。两个不相交的数据库由所有位置组成,分别是7个和8个瓦片(分别)与一个小整数相关联。由于存储了所有位置,因此不必在数据库中实际存储密钥。能够计算所有键的Universe中的特定键的索引就足够了。然后“数据库”只是一个小整数的向量,其大小是可能的键的总数(分别为16!/ 9!和16!/ 8!)。密钥根本不存储,因为它是隐含的。
存储在数据库中的值总是适合一个字节(显然),但在本文后面有一个建议,可以减少数据的最大幅度。例如,如果可以将最大值减小到15,则可以在一个字节中存储两个条目,从而将数据库的大小减小2倍。
这是一种相当快速的算法,用于为[0, n)
中k个值子集的给定排列产生唯一索引。它生成的索引不是基于排列的词典排序,而是基于使用Fisher-Yates shuffle算法生成排列。
FY shuffle产生其输入向量的均匀分布的随机置换;通过简单地在k步之后停止shuffle并将输出截断为k个元素,可以修改它以产生其输入向量的k子集的均匀分布的随机置换。 (提前停止洗牌只是“优化”。)
FY shuffle需要一系列随机数:rnd(n), rnd(n-1), ..., rnd(1)
,其中rnd(x)
在(0, x]
范围内产生均匀分布的随机数。每个不同的序列产生不同的排列,因此可以通过列举可能的序列来列举排列。可以使用factorial base system将给定序列映射到唯一整数。
同样,我们通过截断到长度为k的数字来枚举k长度排列;而不是使用n个权重n!
,(n-1)!
,...,1!
,我们使用第一个k
权重n!
... {{1}通过将每个权重除以(n-k+1)!
来重新规范化。在实践中,不需要除法,因为数字是使用Horner's method计算的,因此对应于d 0 ,d 1 ,...,d的索引 k-1 计算如下:
(n - k + 1)!
为了从排列中推导出索引,我们需要通过观察排列中每个值的最终位置来推导出FY算法的序列。下面的代码基于从左到右的FY,这与链接的维基百科文章中的算法相反。 (从左到右使截断结果更容易。)因此,生成随机排列的算法将是:
(…((d0*n + d1)*(n-1) + d2)*(n-2) + …) + dk-1
该算法的重要特征是步骤Shuffle(n, k):
Create Vec[n] with initial values 0,1,...,n-1
for i from 0 to k-1:
generate random j in the range [i, n-1]
swap Vec[i], Vec[j]
Result is the first k elements of Vec.
肯定产生i
的最终值;后续步骤不会改变Vec [i]。因此,为了反转算法,我们可以通过找到Vec[i]
处上一步的值来推导出步骤i中j的值。如果我们还跟踪每个值的位置,那么这很容易做到。
这是C99中的实际代码,它可能比上面的解释更容易理解:
Vec[i]