有效地存储8个不同数字和8个零的所有排列

时间:2015-11-13 15:47:11

标签: database algorithm optimization

我为Loyd的15 Puzzle工作了最佳求解器,我遇到了一种使用7-8不相交模式数据库的技术(Korf,Richard E.和Felner,Ariel 2001,Disjoint模式数据库启发式,人工智能 2002年1月。)

描述该技术的论文指出,每个条目,包括拼图的修改版本的独特状态和短整数启发式,花费一个字节或更少来存储在数据库中

这引出了我的问题:如何在16个可能的位置存储8个数字的每个唯一排列,并在一个字节中存储一个短整数?或者我是在错误的轨道上并且应该存储不同的东西?

提前谢谢!

2 个答案:

答案 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]