当前位置: 首页 > 知识库问答 >
问题:

计算排序向量的向量中唯一值的计数

羊慈
2023-03-14

我有一个类型为std::vector 的对象,称为V,其中每个子向量(每个std::vector )都是排序的。 我想计算出v的每个唯一size_t被找到的次数。 我在考虑使用std::map 执行如下操作

int main()
{
    const std::vector<std::vector<size_t>> v = {
        {4, 10, 12, 18, 20, 28, 34},
        {4, 12, 18, 20, 28},
        {4, 17, 18, 20, 28},
        {4, 17, 18, 20, 28, 37}
    };


    std::map<size_t, size_t> counts;
    for (const auto& a : v)
    {
        for (const auto& b : a)
        {
            auto it = counts.lower_bound(b);
            if (it != counts.end() && !(counts.key_comp()(b, it->first)))
            {
                // mut already exist
                ++(it->second);
            } else
            {
                // mut is new
                counts.insert(it, std::map<size_t, size_t>::value_type(b, 1));
            }
        }   
    }

    for (auto it = counts.begin() ; it != counts.end() ; ++it)
        std::cout << it->first << ": " << it->second << "\n";
}

,输出

4: 4
10: 1
12: 2
17: 2
18: 4
20: 4
28: 4
34: 1
37: 1

不出所料。

实际上,这些值在0和4e9之间均匀分布,因此我不得不使用std::map而不是std::vector。 如果一个值存在于一个向量中,则增加了该值在连续向量中被一次又一次发现的可能性,因此与已插入值的增量相比,插入相对较少。 而且,向量的子部分往往是相同的。

有没有更好的技术? 例如,在计算lower_bound时,在对元素进行排序时使用前一个元素的插入点会更快。 像是,

    for (const auto& a : v)
    {
        MapType::iterator it = a.begin();
        for (const auto& b : a)
        {
            auto it = counts.lower_bound(it, b); // Use `it` to avoid searching in elements that precedes its position
            
            // etc..
        }   
    }

,但是我认为std::map::lower_bound不能使用from迭代器。

共有2个答案

霍财
2023-03-14

这需要进行性能测试,但假设向量的数量明显小于其中的数字,这种方法可能工作得更快:

using szvec = std::vector<size_t>;
using range = std::pair<szvec::const_iterator,szvec::const_iterator>;

const std::vector<szvec> v = {
    {4, 10, 12, 18, 20, 28, 34},
    {4, 12, 18, 20, 28},
    {4, 17, 18, 20, 28},
    {4, 17, 18, 20, 28, 37}
};

// we use greater so iterator with smallest value will be on top of queue
auto sort_range = []( const range &r1, const range &r2 ) { 
    return *(r1.first) > *(r2.first);
};
std::priority_queue<range,std::vector<range>,decltype(sort_range)> q( sort_range );

// we assume all vectors are not empty on start
// otherwise we need to check for empty range before pushing
for( const auto &vec : v ) q.push( std::make_pair( vec.cbegin(), vec.cend() ) );
std::vector<std::pair<size_t,size_t>> counters;
while( !q.empty() ) {
    auto r = q.top();
    q.pop();
    if( counters.empty() || counters.back().first != *(r.first) ) 
        counters.emplace_back( *(r.first), 1 );
    else 
        counters.back().second++;
    if( ++r.first != r.second ) q.push( r );
}

for( const auto &p : counters ) 
    std::cout << p.first << ":" << p.second << std::endl;

因此,我们的想法是让迭代器对不同的向量进行排序,按它们所指向的值进行排序,并对通过迭代器传递的相同值进行计数,而不是分别处理每个向量。

活生生的例子

秦永望
2023-03-14

我给出了一种重用插入点的方法。 我是利用插入很少的事实。

我将使用对的排序向量作为MapType。

typedef std::vector<std::pair<size_t, size_t>> MapType;

假设向量根据KEY_COMP函子进行排序。 然后您可以为您的MapType构建一个比较函子(这里我使用lambda表达式来完成)。

auto comp = [&](std::pair<size_t, size_t>& p1, std::pair<size_t, size_t> const& p2)
    {
        return key_comp(p1.first,p2.first);
    };

现在,对于v中的每个向量,您可以重用您过去的插入点,因为您知道元素是排序的。

这是完整的代码

#include <map>
#include <vector>
#include <iostream>
#include <algorithm>

typedef std::vector<std::pair<size_t, size_t>> MapType;

int main()
{
    const std::vector<std::vector<size_t>> v = {
        {4, 10, 12, 18, 20, 28, 34},
        {4, 12, 18, 20, 28},
        {4, 17, 18, 20, 28},
        {4, 17, 18, 20, 28, 37}
    };

    auto key_comp = [](std::size_t v1, std::size_t v2) {
        return v1 < v2;
    };

    auto comp = [&](std::pair<size_t, size_t>& p1, std::pair<size_t, size_t> const& p2)
    {
        return key_comp(p1.first,p2.first);
    };


    MapType counts;
    for (const auto& a : v)
    {
        auto it = counts.begin();
        for (const auto& b : a)
        {
            // You can start from it instead of counts.begin() because vector a is sorted
            it = std::lower_bound(it, counts.end(), MapType::value_type(b, 1), comp);
            if (it != counts.end() && !(key_comp(b, it->first)))
            {
                // mut already exist
                ++(it->second);
            } else
            {
                // mut is new
                // Insertion may invalidate iterators so you need to reassign it
                it = counts.insert(it, MapType::value_type(b, 1));
            }
        }   
    }

    for (auto it = counts.begin() ; it != counts.end() ; ++it)
        std::cout << it->first << ": " << it->second << "\n";
}

输出:

4: 4
10: 1
12: 2
17: 2
18: 4
20: 4
28: 4
34: 1
37: 1

编译器资源管理器链接:https://godbolt.org/z/zoy7kg

 类似资料:
  • 问题内容: 如果我有三列: 我想计算一下表格中有多少唯一的电子邮件,我该怎么做? 如下语句: 给我总数。 我试过了 但这似乎并没有给我期望的数字。 问题答案: 采用 提供唯一的电子邮件ID,然后简单地对其进行计数。

  • 我需要计算和的中位数。但是,要计算每个中位数,我必须包括具有相同面和相同类别的所有行。例如,要计算第二行的中位数,我必须包括行 2 和 3,因为我在第 2 行和第 3 行中具有相同的面和 。我正在尝试使用循环函数,但我不知道如何包含此条件。 这就像一个条件中位数。 非常感谢您的关注。 这里,就是例子:

  • 假设我有数据表(如上所述): 如何有效地计算唯一列?在这种情况下,只有3个。 请假设一般情况下: 始终是数据表而不是矩阵;尽管列始终是数字 是否可以在不制作额外数据副本的情况下执行此操作? 我目前的方法是使用

  • 设一个向量,长度为,随机包含0或1。 获取向量的有效方法是什么,该向量指示每个0段或1段中交替有多少个0或1? 例子: 请注意,始终先计算0,如果向量以1开头,则结果向量的第一个条目,即计数(1),应为0。

  • 问题内容: 我正在寻找一种类似于R函数的高效方法来计算Python中列表的秩向量。在一个简单的列表与所述元件之间没有联系,元件 我 的列表的秩矢量的应该是 X 当且仅当是 X 个在排序的列表元素。到目前为止,这很简单,以下代码片段可以解决问题: 但是,如果原始列表具有联系(即,多个具有相同值的元素),事情就会变得复杂。在这种情况下,所有具有相同值的元素都应具有相同的等级,这是使用上述朴素方法获得的

  • 问题内容: 我正在尝试使用Haversine公式来计算由纬度和经度标识的一长串位置的距离矩阵,该公式采用两个坐标对的元组来产生距离: 我可以使用嵌套的for循环计算所有点之间的距离,如下所示: 使用一个简单的函数: 但是考虑到时间的复杂性,这需要花费相当长的时间,大约需要20秒才能获得500点,而且我的清单要长得多。这让我着眼于矢量化,并且遇到了((docs)),但无法弄清楚如何在这种情况下应用它

  • 问题内容: 假设我们有以下pandas DataFrame: 如何以 向量化的方式计算 大熊猫的连续数量?我想要这样的结果: 类似于矢量化求和运算的操作,它会在特定条件下重置。 问题答案: 您可以执行以下操作(贷方:如何使用系列/数据框模拟itertools.groupby):

  • 问题内容: 我已经在处理以下代码,但是似乎找不到一种方法来计算字谜列表中唯一值的数量。如果我只是打印出:我会得到列表的总价值,但其中包括重复项。 我试图将列表转换为集合,然后再删除掉重复项,但是还没有任何运气。 谢谢! 问题答案: 使用。仅包含唯一值: