在未排序的对数组中找到K个UNIQUE最大元素

时间:2019-09-13 23:36:14

标签: c++ arrays algorithm optimization max

这是场景。我有一个未分类的数组(非常大),称为Gallery,其中包含成对的模板(std::vector<uint8_t>)及其关联的ID(std::string)。

我有一个向我提供模板的函数,并且必须返回我画廊中最相似的k模板的ID(我使用余弦相似度在模板之间生成相似度得分)。

我考虑使用this post中讨论的堆。 但是,问题在于图库可以包含属于一个ID的多个不同模板。在我的函数中,我必须返回k 唯一 ID。

对于上下文,我正在做一个面部识别应用程序。我可以在画廊中拥有属于一个人的多个不同模板(该人使用多个不同的图像注册了画廊,因此,多个模板属于他们的ID)。搜索功能应将k个最相似的人返回给提供的模板(因此,一次返回相同的ID不得超过一次)。

将赞赏使用C ++的高效算法。

编辑:为我建议的带有堆的解决方案剪掉了代码(无法正确处理重复项)

    std::priority_queue<std::pair<double, std::string>, std::vector<std::pair<double, std::string> >, std::greater<> > queue;


    for(const auto& templPair : m_gallery) {
        try{
            double similairty = computeSimilarityScore(templPair.templ, idTemplateDeserial);

            if (queue.size() < candidateListLength) {
                queue.push(std::pair<double, std::string>(similairty, templPair.id));
            } else if (queue.top().first < similairty) {
                queue.pop();
                queue.push(std::pair<double, std::string>(similairty, templPair.id));
            }
        } catch(...) {
            std::cout << "Unable to compute similarity\n";
            continue;
        }
    }
// CandidateListLength number of IDs with the highest scores will be in queue

这里是一个希望有所帮助的例子。为了简单起见,我将假定已经为模板计算了相似度得分。

模板1:相似性得分:0.4,ID:赛勒斯

模板2:相似度得分:0.5,ID:詹姆斯

模板3:相似性得分:0.9,ID:鲍勃

模板4:相似度评分:0.8,ID:赛勒斯

模板5:相似度评分:0.7,ID:Vanessa

模板6:相似度评分:0.3,ID:Ariana

获取前3个得分模板的ID将返回[Bob,Cyrus,Vanessa]

2 个答案:

答案 0 :(得分:2)

使用std :: set结构(平衡的BST)代替堆。它还可以按顺序排列元素,让您找到插入的最大和最小元素。此外,使用插入功能时,它会自动检测到重复项并将其忽略,因此内部的每个元素将始终是唯一的。复杂度是完全一样的(尽管由于常数较大,所以速度稍慢)。

编辑:我可能不正确地理解了这个问题。据我所知,您可以具有多个具有不同值的元素,这些元素应被视为重复项。

我会做什么:

  1. 将一组配对(模板值,ID)
  2. 制作一个地图,其中键是ID,值是集合中当前模板的模板值。

  3. 如果要添加新模板:

    • 如果它的ID在地图上-您已找到一个重复项。如果其值比映射中与ID配对的值差,则不执行任何操作,否则从集中删除一对(旧值,ID)并插入(新值,ID),将映射中的值更改为新值。
    • 如果它不在地图中,只需将其添加到地图中并设置即可。
  4. 如果集合中有太多项目,只需从集合和地图中删除最差的一个。

答案 1 :(得分:1)

实施了Maras的答案纲要。 似乎可以完成工作。

#include <iostream>
#include <vector>
#include <map>
#include <utility>
#include <string>
#include <set>

int main() {
    int K = 3;

    std::vector<std::pair<double, std::string>> data {
        {0.4, "Cyrus"},
        {0.5, "James"},
        {0.9, "Bob"},
        {0.8, "Cyrus"},
        {0.7, "Vanessa"},
        {0.3, "Ariana"},
    };

    std::set<std::pair<double, std::string>> mySet;
    std::map<std::string, double> myMap;

    for (const auto& pair: data) {
        if (myMap.find( pair.second ) == myMap.end()) {
            // The ID is unique
            if (mySet.size() < K) {
                // The size of the set is less than the size of search candidates
                // Add the result to the map and the set
                mySet.insert(pair);
                myMap[pair.second] = pair.first;
            } else {
                // Check to see if the current score is larger than the worst performer in the set
                auto worstPairPtr = mySet.begin();

                if (pair.first > (*worstPairPtr).first) {
                    // The contender performed better than the worst in the set
                    // Remove the worst item from the set, and add the contender
                    // Remove the corresponding item from the map, and add the new contender
                    mySet.erase(worstPairPtr);
                    myMap.erase((*worstPairPtr).second);
                    mySet.insert(pair);
                    myMap[pair.second] = pair.first;
                }
            }

        } else {
            // The ID already exists
            // Compare the contender score to the score of the existing ID.
            // If the contender score is better, replace the existing item score with the new score
            // Remove the old item from the set
            if (pair.first > myMap[pair.second]) {
                mySet.erase({myMap[pair.second], pair.second});
                mySet.insert(pair);
                myMap[pair.second] = pair.first;
            }

        }
    }

    for (auto it = mySet.rbegin(); it != mySet.rend(); ++it) {
        std::cout << (*it).second << std::endl;
    }

}

输出为

Bob
Cyrus
Vanessa