纸牌游戏中快速匹配的数据结构

时间:2016-04-07 03:24:04

标签: algorithm data-structures computer-science

在播放trading card games时,我经常想知道处理以下问题的最有效数据结构是什么。

在这样的游戏中,我面对的是一个包含N卡(N~30..60..100)的牌组的对手,每个卡都是从可能的M卡类型中选择的(M~通常为1000 ... 10000) 。卡通常不需要是唯一的,即可以有重复的卡类型。在比赛开始之前,对手牌组的内容是未知的。

随着游戏的开始和进展,我慢慢地逐卡学习,这是对手使用的牌。有一个数据集包括之前看到的甲板的K(K~通常为100000..100000s)的全部内容。我想使用在特定游戏中获得的逐步增加的样本I来查询该数据集,以制作对手使用的可能套牌的排序列表。

在合理的现代硬件(即几千兆字节的RAM可用)上提到限制时,进行此类查询的最有效数据结构是什么?

一个非常小的例子

  • 可能的卡片类型= [1..10]
  • 已知的K甲板:

    d1 = [1, 4, 6, 3, 4]
    d2 = [5, 3, 3, 9, 5]
    d3 = [5, 10, 4, 10, 1]
    d4 = [3, 7, 1, 8, 5]
    
  • 在第1轮,我透露对手使用卡牌#5;因此,我的候选人名单缩减为:

    d2 = [5, 3, 3, 9, 5] - score 2
    d3 = [5, 10, 4, 10, 1] - score 1
    d4 = [3, 7, 1, 8, 5] - score 1
    

    d2在结果中排​​名高于其余部分,因为该套牌中有双5,所以它可能更有可能

  • 在第2轮,我透露对手使用卡#1;候选人名单缩减为:

    d3 = [5, 10, 4, 10, 1]
    d4 = [3, 7, 1, 8, 5]
    

我对解决方案的看法

当然,简单的解决方案是将K卡片存储为 N 整数的数组。获得针对一个套牌显示的 p 卡的给定查询的匹配分数因此需要进行O(N * p)检查。每次我们看到匹配时,我们只是将得分增加1.因此,根据 p 卡的查询检查所有 K 已知套牌将需要O(K N p),在最坏情况下约为100000 * 100 * 100次操作=> 1e9,那是很多工作。

我们可以设置一个索引,该索引将包含针对每种已知卡类型遇到卡片的指针列表 - 但是,它并没有解决所有这些列表相交的问题(并且它们正在进行中如果是巨大的,可能会有90%到95%已知甲板上的卡片。对于给定的 p 卡片查找,它归结为与 K 甲板指针的 p 列相交,计算过程中的交叉点分数。粗略地说,这是O(Kp),但具有相当大的常数。它还处于后期阶段的1e7操作。

但是,如果我们使用的事实是每次下一轮实际上都会进一步限制我们的数据集,我们可以重新应用过滤到之前查询中出现的任何内容。这样,每转一圈就是O(K)=> 1e5操作。

有没有一种方法可以更好地执行,理想情况下,不依赖于K的值?

2 个答案:

答案 0 :(得分:2)

你可以做两件事来加快速度。首先,创建一个倒排索引,告诉您哪些卡片包含每张卡片。所以在上面的示例中:

d1 = [1, 4, 6, 3, 4]
d2 = [5, 3, 3, 9, 5]
d3 = [5, 10, 4, 10, 1]
d4 = [3, 7, 1, 8, 5]

您的索引是:

1: d1, d3, d4
3: d1, d2, d4
4: d1(2), d3
5: d2(2), d3, d4
6: d1
7: d4
8: d4
9: d2
10: d3(2)

应该很清楚,这需要与甲板本身大致相同的内存量。也就是说,你可以拥有多达M张牌,而不是拥有N张K牌,每张牌最多可以有N张牌。

当用户翻过第一张卡片5时,您可以在索引中快速查找5并获得候选列表[d2,d3,d4]

这是第二个优化:你保留了候选人名单。你不再对其他的甲板感兴趣了;他们已被从候选人名单中删除。当显示下一张卡片1时,您在索引中查找1并获得[d1,d3,d4]。您将其与第一个候选列表相交以生成[d3,d4]

在最糟糕的情况下,你最终会在每个K项目中进行N个交叉点(每个卡一个)(如果甲板都非常相似)。但在大多数情况下,卡所在的套牌数量将远远小于K,因此您的候选列表长度可能会很快缩小。

最后,如果您将卡片引用存储为哈希映射,那么交集会非常快速,因为您只需要从下一张卡片的大项目列表中的(通常很小的)现有候选列表中查找项目。那些查找是O(1)。

这是搜索引擎如何运作的基本思路。你有一个单词列表,每个单词都包含对单词出现的文档的引用。你可以很快地将文档列表从数亿个缩小到只有少数几个。

答案 1 :(得分:1)

你对甲板指针的交叉p列表的想法是好的,但是你缺少一些优化。

按照某些标准(即甲板索引)对甲板进行排序,并使用二进制搜索在列表中前进(使用堆获取最小甲板ID并使其前进以匹配或超过当前最大甲板ID)。这样你就可以更快地完成它们,特别是如果交叉路口没有很多套牌。

同时存储上一个交点,以便下次移动时只需要交叉2个列表(上一个结果和新卡)。

最后,您可以简单地忽略过于流行的卡片,并在最终结果中检查它们。

我建议您实施这样的解决方案并运行一些基准测试。它会比O(K)快。

相关问题