我正在寻找关于存储条纹模式数据库的所有可能排列的一些建议。
所以十五个瓷砖问题有16个!可能的排列,但是存储fringe
的值,所以0(空白区块),3,7,11,12,13,14,15是16!/(16-8)! = 518,918,400个排列。
我希望将所有这些排列存储在数据结构中以及启发式函数的值(每次迭代首次搜索时都会增加),到目前为止,我这样做但非常缓慢并且采取了我5分钟存储60,000,这是我没有的时间!
目前我的结构看起来像这样。
Value Pos0 Pos3 Pos7 Pos11 Pos12 Pos13 Pos14 Pos15
我存储给定数字的位置。我必须使用这些位置作为ID,当我计算启发式值时,我可以快速浏览到给定的组合并检索值。
我对此非常不确定。拼图的状态由数组示例表示:
int[] goalState = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
我的问题是存储这些值的最佳数据结构是什么?以及检索它们的最佳方法。
(这个问题最初基于存储在数据库中,但现在我想将它们存储在某种形式的本地数据结构中 - 因为从数据库中检索速度很慢)
答案 0 :(得分:10)
我无法真正掌握,0,3,7,11,12,13,14,15在你的情况下有什么特殊的意义。他们的立场是不变的吗?他们的立场是否足以识别整个谜题状态?
无论如何,这是一种通用方法,您可以随时缩小范围:
由于你有16种可能的最大状态,我会尝试使用十六进制数来表示你的排列。因此状态{1,2,3,6,5,4,7,8,9,10,11,12,13,14,15,0}
看起来像0x123654789ABCDEF0 = 1312329218393956080
。可能的最大数字是0xFEDCBA9876543210
,它仍然可以存储在无符号长(仅限Java 8)或BigInteger中(有很多例子,我更喜欢这个)。这个数字对于每个排列都是唯一的,并且可以用作主键,如果你有整个状态,从数据库中检索它将非常快。
//saving your permutation
String state = "0xFEDCBA9876543210";
BigInteger permutationForDatabase = new BigInteger(state, 16);
//and then you can insert it into database as a number
//reading your permutation
char searchedCharacter = 'A';//lets say you look for tile 10
BigInteger permutation = ...;//here you read the number from the database
int tilePosition = permutation.toString(16).indexOf(searchedCharacter);
可能有一个更优雅/高性能的解决方案来获得平铺位置(可能有点操作魔法)。
答案 1 :(得分:6)
每个数字0-15
是一个4位数字。您必须表示7个这样的数字,最低要求为28位,这完全在int
的31个有符号位空间内。因此,可以从int
分配所有排列并从中导出所有排列。
要计算此数字,请给定变量a
到g
:
int key = a | (b << 4) | (c << 8) | (d << 12) | (e << 16) | (f << 20) | (g << 24);
解码(如果需要):
int a = key & 0xF;
int b = key & 0xF0;
int c = key & 0xF00; // etc
将ints
存储在数据库中非常有效,并且占用的磁盘空间最小:
create table heuristics (
key_value int not null,
heuristic varchar(32) not null -- as small as you can, char(n) if all the same length
);
插入所有行后,为超快速查找创建covering index:
create unique index heuristics_covering heuristics(key_value, heuristic);
如果在插入之前创建此索引,插入将非常非常慢。
创建数据并插入数据是相对简单的编码。
答案 2 :(得分:0)
因此,我的理解是正确的,您正在为每个可能的谜题状态计算启发式值,并且您希望以后能够根据给定的谜题状态查找它?这样你就不必动态计算它?大概是因为计算启发式值所花费的时间。
因此,您将迭代所有可能的谜题状态,计算启发式,然后存储该结果。它花了很长时间才能做到这一点。您的假设似乎是需要花费很长时间才能存储价值 - 但是,如果您看到的时间滞后不是存储价值所花费的时间,那该怎么办呢?在数据存储中,而是它生成启发式值的时间?这对我来说似乎更有可能。
在这种情况下,如果您想加快生成和存储值的过程,我可能会建议将任务分成几个部分,并一次使用多个线程。
我认为禁用的数据结构将是一个内存中的哈希表,哈希键是你的拼图状态,值是你的启发式值。其他人已经提出了生成拼图状态哈希键的合理方法。每个线程都可以访问相同的哈希表结构,这些线程为拼图状态域的各个部分生成和存储启发式值。
一旦填充了哈希表,您只需将其序列化并将其存储在文件系统中的二进制文件中即可。然后让你的启发式值服务器在启动时加载到内存中(并将其反序列化到内存中的哈希表中)。
如果我的前提是错误的,它需要花费很长时间来生成启发式值,那么当你去存储它们时,你似乎做了一些非常不理想的事情。例如,每次存储值时重新连接到远程数据库。这可能解释了5分钟。如果你每次去查找一个值时都重新连接,那么这也可以解释为什么这样做太长了。
根据启发式值的大小,内存中的哈希表可能不实用。记录的随机访问二进制文件(每个记录只包含启发式值)可能会完成同样的事情,但是您需要某种方法将哈希键域数学地映射到记录索引域(包括顺序整数)。如果你重复所有可能的谜题状态,似乎你已经有了一种将谜题状态映射到连续整数的方法;你只需要弄清楚数学。
使用本地数据库表,每行只有一个键和一个值是不合理的。您绝对应该能够在几分钟的时间内插入5.18亿行 - 您只需要在数据加载过程中保持连接,并在数据加载完成后构建索引。一旦您在密钥上构建了索引,只要您不必为每次查找重新连接,使用(群集主键整数)索引进行查找应该非常快。
此外,如果您要将行提交到数据库中,您不想在每行之后提交,那么您将要提交每1,000或10,000行。如果您在插入每一行后提交,那么这将大大降低您的数据加载性能。