【问题标题】:Pattern Databases Storing all permutations存储所有排列的模式数据库
【发布时间】:2017-03-10 01:46:04
【问题描述】:

我正在寻找一些关于存储条纹图案数据库的所有可能排列的建议。

所以十五个瓷砖问题有 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}

我的问题是存储这些值的最佳数据结构是什么?以及检索它们的最佳方法。

(这个问题最初是基于存储在数据库中,但现在我想将它们存储在某种形式的本地数据结构中——因为从数据库中检索速度很慢)

【问题讨论】:

  • “我花了 5 分钟来存储 60,000 个” – 你当时是如何存储它们的?
  • 哪个数据库,用什么代码?
  • 我认为如果您提供您正在解决的难题的一些详细信息会有所帮助,这样我们就可以了解您要保存哪些排列,哪些不保存。 fringe 到底是什么?
  • 为什么要存储可以生成的内容?似乎是解决回溯问题的一种过度设计的方法。

标签: java database


【解决方案1】:

我无法理解,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);

可能有更优雅/高性能的解决方案来获取磁贴位置(可能是一些位操作魔法)。

【讨论】:

  • 所以它是一种模式数据库的方法,所以本质上存储与当前状态的距离以达到0,3,7,11,12,13,14,15的正确位置给出了一个下限启发式估计,比如说我们解决了它们,那么我们只需要处理一个 3x3 的谜题而不是 4x4
【解决方案2】:

每个数字0-15 是一个 4 位数字。您必须表示 7 个这样的数字,最低要求为 28 位,这正好在 int 的 31 个有符号位空间内。因此,所有排列都可以从int 分配和派生。

要计算这个数字,给定变量ag

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);

如果你在插入之前创建这个索引,插入会非常非常慢。

创建数据并插入数据是相对简单的编码。

【讨论】:

    【解决方案3】:

    那么我的理解是否正确,您正在为每个可能的谜题状态计算启发式值,并且您希望稍后能够根据给定的谜题状态查找它?这样您就不必即时计算?大概是因为计算启发式值需要时间。

    因此,您正在迭代所有可能的谜题状态,计算启发式,然后存储该结果。这样做需要很长时间。您的假设似乎是存储值需要很长时间 - 但是如果您看到的时间延迟不是将值存储在数据存储中所花费的时间,而是它所花费的时间生成启发式值?这对我来说似乎更有可能。

    在这种情况下,如果您想加快生成和存储值的过程,我可能会建议将任务拆分为多个部分,并一次使用多个线程。

    我相信禁食的数据结构将是一个内存中的哈希表,哈希键是你的谜题状态,值是你的启发式值。其他人已经提出了生成拼图状态哈希键的合理方法。为拼图状态域的各个部分生成和存储启发式值的每个线程都可以访问相同的哈希表结构。

    填充哈希表后,您可以简单地对其进行序列化并将其存储在文件系统中的二进制文件中。然后让您的启发式值服务器在启动时将其加载到内存中(并将其反序列化到内存中的哈希表中)。

    如果我的假设是不正确的,即生成启发式值需要很长时间,那么当你去存储它们时,你似乎在做一些非常次优的事情。例如,每次存储值时重新连接到远程数据库。这可能解释了 5 分钟。而且,如果您每次查找值时都重新连接,这也可以解释为什么花费的时间太长。

    根据您的启发式值有多大,内存中的哈希表可能不实用。记录的随机访问二进制文件(每个记录仅包含启发式值)可能会完成相同的事情,但是您需要某种方式将哈希键域在数学上映射到记录索引域(由顺序整数)。如果您正在迭代所有可能的谜题状态,那么您似乎已经有了一种将谜题状态映射到顺序整数的方法;你只需要弄清楚数学。

    使用本地数据库表,每行仅具有一个键和一个值并不是不合理的。您绝对应该能够在几分钟内插入 5.18 亿行 - 您只需要在数据加载过程中保持连接,并在数据加载完成后构建索引。在您的键上建立索引后,只要您不必每次查找都重新连接,使用(聚集主键整数)索引进行查找应该很快。

    此外,如果您将行提交到数据库中,您不想在每行之后提交,而是希望每 1,000 或 10,000 行提交一次。如果您在插入每一行后提交,那将大大降低您的数据加载性能。

    【讨论】:

      猜你喜欢
      • 2019-09-22
      • 1970-01-01
      • 2018-08-21
      • 1970-01-01
      • 1970-01-01
      • 2011-02-23
      • 1970-01-01
      • 2013-08-10
      • 2010-10-03
      相关资源
      最近更新 更多