找到最佳间隔匹配结果

时间:2011-10-20 14:32:24

标签: c# java c++ algorithm

我有两种形式的数据:

x   |  y  |  z        x1   |   y1   |  z1
ab1 |  1  |  2        ab1  |   1    |  2
ab1 |  2  |  3        ab1  |   1.8  |  2
ab2 |  2  |  3        ab1  |   1.8  |  2

列数可以在1到30之间变化。两组的行数可能不同。 每组的平均行数可以在几百到几百万之间变化。 对于每列,将应用不同的匹配规则,例如:

x: perfect match
y: +/- 0.1
z: +/- 0.5

当满足所有标准时,两行是等效的。 我的最终目标是在第二组中找到第二组中没有匹配的行。

天真的算法可能是:

foreach a in SetA
{
    foreach b in SetB
    {
        if (a == b)
        {
            remove b from SetB
            process the next element in SetA
        }
    }
    log a is not in SetB
}

在这个阶段,我对算法的效率不是很感兴趣。我相信我可以做得更好,我可以降低复杂性。 我更关心结果的正确性。让我们试试一个非常简单的例子。 两组数字:

A       B
1.6    1.55
1.5    1.45
4      3.2

如果符合以下两个要素:

b + 0.1 >= a >= b - 0.1

现在,如果我运行朴素算法,我将找到2个匹配项。 然而,算法的结果取决于两组的顺序。例如:

A       B
1.5    1.55
1.6    1.45
4      3.2

该算法只能找到一个匹配。

我想找到最大匹配行数。

我认为在真实世界中,其中一列会存储一个id,因此可能的多个匹配的数量将是原始集的一个小得多的子集。 我知道我可以尝试在第一次扫描后通过后处理来解决这个问题。 但是,我不想重新发明轮子,我想知道我的问题是否等同于一些着名的,众所周知的,已经解决的问题。

PS:我已将问题标记为C ++,C#和Java,因为我将使用其中一种语言来实现它。

6 个答案:

答案 0 :(得分:1)

它可以被视为图论问题。设X是一个包含第一组中每行的一个节点的集合。设Y为另一组,其中包含第二组中每行的一个节点。

图中的边界定义为:对于X中的给定x和Y中的给定y,如果对应于x的行与对应于y的行匹配,则存在边(x,y)。

构建此图表后,您可以在其上运行“maximum-bipartite-matching”算法,您将完成。

答案 1 :(得分:1)

据我所知,您希望第一组中的行与第二组中的任何行匹配(在错误范围内)。通过解析第一组中的元素并将它们与第二组中的元素进行比较,可以使用O(n ^ 2)复杂度算法来实现这种清晰度。 优化可能是这样的:

  • 对两个集合进行排序 - O(n * ln(n))

  • 从头开始消除第一组中的元素太小或太大(在错误内) - O(n)

  • 使用二分搜索(在错误内)查看第一组元素的第二组 - O(n * lg(2))并消除那些不合适的

  • comlexity O(n * ln(n))

答案 2 :(得分:0)

范围树? http://en.wikipedia.org/wiki/Range_tree 我真的不知道,只是把想法扔出去

答案 3 :(得分:0)

从声明“我的最终目标是找到第一组中的第二组中没有匹配的行”。据我所知,第一组中可能有多行与第二组中的同一行匹配。在这种情况下,解决方案是从您的朴素算法中删除“从SetB中删除b”这一行。

但是,如果你真的需要在两组元素之间进行一对一的匹配,那么Corey Kosak提供的“最大二分匹配”答案就适用了。

答案 4 :(得分:0)

Two rows are equivalent when all the criteria are satisfied. My final goal is to find the rows in the first set with no match in second set.

foreach a in SetA
{
    foreach b in SetB
    {
        if (a == b) //why would you alter SetB at all
           go to next A
    }
    remove a from SetA  //log a is not in SetB
}

但是,你是对的,is equivalent to some famous, well known and already solved problem。它被称为“设置差异”。它是集合论的一个主要部分。由于所有这些语言都有集合,因此它们也具有该算法。 C ++甚至还有一个专用的功能。所有这些的近似复杂度为O(2(A+B)-1)

C ++标准算法函数:http://www.cplusplus.com/reference/algorithm/set_difference/

vector<row> Result(A.rows());
end = std::set_difference(A.begin(), A.end(), 
                          B.begin(), B.end(), 
                          Result.begin());
Result.resize(end-Result.begin());
可以使用

或std :: unordered_set执行此操作:http://msdn.microsoft.com/en-us/library/bb982739.aspx

std::unordered_set<row> Result(A.begin(), A.end());
for(auto i=B.begin(); i!=B.end(); ++i) {
    auto f = Result.find(*i);
    if (f != A.end())
        A.erase(f);
}

Java也是如此:http://download.oracle.com/javase/tutorial/collections/interfaces/set.html

Set<row> Result = new Set<row>(A);
A.removeAll(B);

和C#:http://msdn.microsoft.com/en-us/library/bb299875.aspx

HashSet<row> Result = new HashSet<row>(A);
A.ExceptWith(B);

答案 5 :(得分:0)

考虑到你的约束,我没有看到一种方法在低于O(n ^ 2)的情况下做到这一点。我可能会修改你的朴素算法,为表A中的每一行包含bool或count字段,然后如果它与表B中的行匹配则标记它。 然后基于指标使用std :: partition对其进行处理,以将所有唯一行和非唯一行组合在一起。如果你计算,你可以获得“最不唯一”的行。因为你可以在第一场比赛中突破B圈,所以bool会更有效率。