Java 基于集合的三元组匹配算法

Java 基于集合的三元组匹配算法,java,performance,algorithm,data-structures,scalability,Java,Performance,Algorithm,Data Structures,Scalability,我正在寻找一种有效的方法来解决以下问题 列表1是由原始三元组标识的记录列表: X | Y | Z 列表2是由三组记录标识的记录列表。一个X,一个Y,一个Z。X、Y、Z与列表1中的“类型”相同,因此它们之间可以直接比较 Set(X) | Set(Y) | Set(Z) 对于列表1中的项目,我需要找到列表2中的所有项目,其中列表1中的X、Y、Z都出现在列表2中相应的集合中。这可以通过一个例子得到最好的证明: 清单1: 清单2: 在上面的例子中,列表1中的项目将与列表2中的前两个项目相匹配。第三项

我正在寻找一种有效的方法来解决以下问题

列表1是由原始三元组标识的记录列表:

X | Y | Z
列表2是由三组记录标识的记录列表。一个X,一个Y,一个Z。X、Y、Z与列表1中的“类型”相同,因此它们之间可以直接比较

Set(X) | Set(Y) | Set(Z)
对于列表1中的项目,我需要找到列表2中的所有项目,其中列表1中的X、Y、Z都出现在列表2中相应的集合中。这可以通过一个例子得到最好的证明:

清单1:

清单2:

在上面的例子中,列表1中的项目将与列表2中的前两个项目相匹配。第三项不匹配,因为X1不出现在X集中,Z1不出现在Z集中

我已经编写了一个功能正确的算法版本,但担心在更大的数据集上的性能。这两个列表都非常大,所以在列表1上进行迭代,然后对每个项目在列表2上执行迭代将非常低效

我试图通过将列表2中的每个项反规范化为一个映射来构建索引,但是每个项的索引中的索引项的数量与该项子集的大小成正比。因此,这将使用非常高的内存级别,并且还需要一些重要的资源来构建


有人能给我建议一个解决这个问题的最佳方法吗。我很乐意考虑内存和CPU的最佳解决方案,但打破平衡将是美好的! 有很多方法可以做到这一点。哪个是正确的取决于数据和可用内存的多少

一种简单的技术是从list2构建一个表,以加速来自list1的查询

from collections import defaultdict

# Build "hits".  hits[0] is a table of, for each x,
# which items in list2 contain it. Likewise hits[1]
# is for y and hits[2] is for z.
hits = [defaultdict(set) for i in range(3)]
for rowid, row in enumerate(list2):
    for i in range(3):
        for v in row[i]:
            hits[i][v].add(rowid)

# For each row, query the database to find which
# items in list2 contain all three values.
for x, y, z in list1:
    print hits[0][x].intersection(hits[1][y], hits[2][z])

对于列表2,使用
HashSet
(或
HashSet
s)如何?这样,您只需在列表1上迭代,如果集合的总大小不太大,您可以尝试将列表2建模为位字段。不过,这个结构可能会非常零碎——也许维基百科文章(Judy arrays,Trys,Bloom filter)中引用的结构可以帮助您解决规范化方法中的内存问题。

您可以从列表2中构建一个树;树的第一级是集合X中出现的(X1..Xn)中的第一级。第二级是第二项的值,加上一个叶节点,其中包含仅包含X1的列表集。下一个级别包含下一个可能的值,依此类推

Root --+--X1--+--EOF--> List of pointers to list2 lines containing only "X1"
       |      |
       |      +--X2---+--EOF--> List of pointers to list2 lines containing only "X1,X2"
       |      |       |
       |      |       +--X3--+--etc--
       |      |       
       |      +--X3---+--EOF--> "X1,X3"
       |             
       +--X2--+--EOF--> "X2"
       |      |
       |      +--X3---+--EOF--> "X2,X3"
       |      |       |
       ...
这在内存消耗方面是昂贵的(我认为是N^2 log K,其中N=X的值,K=列表2中的行),但会导致快速检索时间。如果可能的X数量很大,那么这种方法将崩溃

显然,您可以为元组的所有3个部分建立此索引,然后将搜索每个树的结果合并在一起。

如果您使用,有一种高级方法来实现此目的,它不一定是最优的,但不会做任何疯狂的事:

List<SomeType> list1 = ...;
List<Set<SomeType>> candidateFromList2 = ...;
if (Sets.cartesianProduct(candidateFromList2).contains(list1)) { ... }
列表列表1=。。。;
名单候选名单2=。。。;
if(Sets.cartesianProduct(candidateFromList2).contains(list1)){…}

但是,检查这个“直接”也不难。

有一种相当有效的方法,可以通过单次传递列表2来实现这一点。首先,构建列表1中项目的索引

from collections import defaultdict

# index is HashMap<X, HashMap<Y, HashMap<Z, Integer>>>
index = defaultdict(lambda: defaultdict(dict))
for rowid, (x, y, z) in enumerate(list1):
    index[x][y][z] = rowid

for rowid2, (xs, ys, zs) in enumerate(list2):
    xhits = defaultdict(list)
    for x in xs:
        if x in index:
            for y, zmap in index[x].iteritems():
                xhits[y].append(zmap)

    yhits = defaultdict(list)
    for y in ys:
        if y in xhits:
            for z, rowid1 in xhits[y].iteritems():
                yhits[z].append(rowid1)

    for z in zs:
        if z in yhits:
            for rowid1 in yhits[z]:
                print "list1[%d] matches list2[%d]" % (hit[z], rowid2)
从集合导入defaultdict
#索引是HashMap
索引=defaultdict(lambda:defaultdict(dict))
对于枚举(列表1)中的rowid(x,y,z):
索引[x][y][z]=rowid
对于枚举(列表2)中的行ID2(xs、ys、zs):
xhits=defaultdict(列表)
对于xs中的x:
如果索引中有x:
对于y,索引[x]中的zmap。iteritems():
xhits[y].追加(zmap)
yhis=defaultdict(列表)
对于y在y中:
如果y在xhits中:
对于z,xhits[y]中的rowid1。iteritems():
yhits[z]。追加(rowid1)
对于zs中的z:
如果z在YHIT中:
对于yhits[z]中的rowid1:
打印“列表1[%d]与列表2[%d]匹配”%(点击[z],行ID2)
这里额外的簿记可能会使它比索引列表2慢。但由于在您的情况下,list1通常比list2小得多,因此这将使用更少的内存。如果您正在从磁盘读取list2,使用此算法,您不需要将其任何部分保留在内存中


内存访问可能是一件大事,所以我不能肯定在实践中哪个会更快。必须测量。在这两种情况下,除非哈希表出现故障,否则最坏情况下的时间复杂度为O(len(list1)*len(list2))。

列表2的集合中的项目是否有任何排序?(例如,这些项目是否有逻辑顺序?)。通常“|”表示“或”而不是“,(X1,X2)表示两个元素的元组,而不是X1 | X2。我读这些东西时头晕目眩。每组元素的典型数量是多少?是像你的例子中那样只有几个,还是有几百个?你真的应该给出更多的细节。清单1和清单2的大致尺寸是多少?在列表2中,每组大约有多少个值?这些集合中的值是什么,整数?列表2中的值是否多次重复使用,或者它们几乎都是唯一的(只有几个唯一的值,数千个唯一的值)?@Dav:列表2中的项在集合中没有任何顺序。如果列表1和列表2中的项是直接可比的,这将起作用。但是,我们需要针对列表1中的每个事件测试列表2中的每个项目,因为这些项目是不可直接比较的,而列表2是驱动因素。不可比较是因为列表2包含一组基本体,而列表1包含基本体?在这种情况下,将List2转换为单个哈希集(例如,{X1,X2,Y1,Z1,Z3}),然后通过迭代List1继续,并对每个成员在哈希集上执行一个quick contains()。创建哈希集不是免费的,但是你可以考虑权衡。David,我明白你的意思,这可能会奏效。我应该澄清X,Y,Zs
List<SomeType> list1 = ...;
List<Set<SomeType>> candidateFromList2 = ...;
if (Sets.cartesianProduct(candidateFromList2).contains(list1)) { ... }
from collections import defaultdict

# index is HashMap<X, HashMap<Y, HashMap<Z, Integer>>>
index = defaultdict(lambda: defaultdict(dict))
for rowid, (x, y, z) in enumerate(list1):
    index[x][y][z] = rowid

for rowid2, (xs, ys, zs) in enumerate(list2):
    xhits = defaultdict(list)
    for x in xs:
        if x in index:
            for y, zmap in index[x].iteritems():
                xhits[y].append(zmap)

    yhits = defaultdict(list)
    for y in ys:
        if y in xhits:
            for z, rowid1 in xhits[y].iteritems():
                yhits[z].append(rowid1)

    for z in zs:
        if z in yhits:
            for rowid1 in yhits[z]:
                print "list1[%d] matches list2[%d]" % (hit[z], rowid2)