合并对象的有效方法

Efficient way to merge objects

本文关键字:方法 有效 对象 合并      更新时间:2023-10-16

我有一个比较大的"站点"列表,大约有350,000个。这些电台来自五个不同的来源,每个都有特定的信息片段。例如,它们都至少具有五种不同类型标识符中的一种。目标是合并相等的站点(如果站点有匹配的标识符,则站点是相等的)。

示例:如果站点a有identifierA: 01234、identifierB: NULL和identifierC: KAKW,站点b有identiferA: NULL、identifierB: USA00012和identifierC: KAKW,我想将它们合并在一起,作为一个具有identifierA: 01234、identifierB: USA00012和identifierC: KAKW的新站点。

现在我把所有的站都放在一个大向量上。我每次移动一个站点到一个新的向量中:1)如果它与向量中已有的任何站点不匹配,则将其推回去,或者2)如果有匹配,则将其合并。

这太花时间了。从理论上讲,是否有一个有效的算法或概念,我可以用来加速这个过程?上一次我花了将近3天的时间。

创建3个数据副本,每个副本用不同的标识符排序。

然后选择一个,迭代,并在已排序的容器中查找匹配。构建组合对象并将其保存到"processed"向量中。

看起来应该是一个哈希表类型的问题,但也许不是,因为有不止一种方法可以使两个站点相等。

我建议类似阿米特的答案,如果你可以节省内存。只是有5个std::map容器的标识符作为索引进入主容器(如数据库),并做你的搜索方式。将标识符放入已排序的容器中需要做一些前期工作,但最终可能会更快。

如果你不关心哪个标识符匹配哪个,或者如果你的标识符数据集不相交,你可以只使用一个容器,将所有五个标识符组合在一起(可能是散列的,但可能不包括null),并在上面搜索。

下面的代码对站点进行传递,有效地生成该站点具有的标识符的位掩码。它翻转这些位以创建一个位掩码,这个位掩码是另一个只有缺失标识符的站点所具有的,如果这样的站点已经已知,则与它合并,将合并的站点添加到稍后将被擦除的列表中。否则,它将不匹配的站点添加到具有其特定位掩码的站点列表中,以便稍后可以快速找到它。(只做了很少的测试)

#include <iostream>
#include <string>
#include <vector>
struct Station
{
    int idn_map() const { return 16 * bool(ia_[0]) +
                                  8 * bool(ib_[0]) +
                                  4 * bool(ic_[0]) +
                                  2 * bool(id_[0]) +
                                      bool(ie_[0]); }
    void merge(const Station& rhs)
    {
        if (ia_.empty()) ia_ = rhs.ia_;
        if (ib_.empty()) ib_ = rhs.ib_;
        if (ic_.empty()) ic_ = rhs.ic_;
        if (id_.empty()) id_ = rhs.id_;
        if (ie_.empty()) ie_ = rhs.ie_;
    }
    std::string ia_, ib_, ic_, id_, ie_;
};
std::ostream& operator<<(std::ostream& os, const Station& s)
{
    return os << s.ia_ << ':' << s.ib_ << ':' << s.ic_ << ':' << s.id_ << ':' << s.ie_;
}
int main()
{
    std::vector<Station> stations = {
        { "s0a", "",    "s0c", "",    "s0e" },
        { "",    "",    "s1c", "s1d", "s1e" },
        { "",    "s2b", "",    "s2d", "" },
        { "s3a", "s3b", "s3c", "s3d", "" },
        { "s4a", "s4b", "",    "",    "" }
    };
    std::vector<Station*> unmatched[32];
    std::vector<Station*> to_erase;
    for (Station& s : stations)
    {
        int idn_map = s.idn_map();
        if (unmatched[idn_map ^ 31].empty())
            unmatched[idn_map].push_back(&s);
        else
        {
            // merge from current element so to_erase kept sorted
            // for fast compact/erase later...
            Station* p_merge_to = unmatched[idn_map ^ 31].back();
            p_merge_to->merge(s);
            to_erase.push_back(&s);
            unmatched[idn_map ^ 31].pop_back();
        }
    }
    for (const auto& p : to_erase)
        std::cout << "will remove element at " << p << 'n';
    // now compact over the deferred erasures...
    auto erase_it = to_erase.begin();
    auto to = stations.begin();
    for (auto from = to; from != stations.end(); ++from)
        if (erase_it != to_erase.end() && *erase_it == &*from)
            ++erase_it;
        else
            *to++ = *from;
    stations.erase(to, stations.end());
    std::cout << "results:n";
    for (const auto& station : stations)
        std::cout << station << 'n'; 
}

代码在这里可用/可运行。

(在我纠正循环逻辑时进行了几次编辑,从使用集合更改为矢量以提高性能)