c ++连接许多std :: vectors并删除重复项

时间:2017-09-17 08:41:49

标签: c++ c++11 vector

我从外部API(std :: vector)接收一些整数。

API通常需要多次调用,因此我需要将连续API调用中的所有整数累加到本地向量。最后,数组的每个元素都必须是唯一的(不需要排序)。

我的代码如下(使用getNextVector来"模拟"数据并模拟API调用)。

代码有效,但是我希望此操作具有最高性能。我的方法是正确的吗?

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

std::vector<int> getNextVector(int i) {
    if ( i == 0 ) {
        std::vector<int> v = { 1,2,3 };
        return v; 
    } else if ( i == 1 ) {
        std::vector<int> v = { 3,4,5 };
        return v; 
    } else if ( i == 2 ) {
        std::vector<int> v = { 5,6,7 };
        return v; 
    } else if ( i == 3 ) {
        std::vector<int> v = { 7,8,9 };
        return v; 
    }
}

int count() { return 4; } //we have four vectors

int main(int argc, char** argv) {

    std::vector<int> dest;
    dest.reserve(20); // we can find this, for simplicity hardcode...

    for( int i = 0; i < count(); i++ ) {
        std::vector<int> src = getNextVector(i);
        dest.insert(
            dest.end(),
            std::make_move_iterator(src.begin()),
            std::make_move_iterator(src.end())
        );
    }

    std::sort(dest.begin(), dest.end());
    dest.erase(unique(dest.begin(), dest.end()), dest.end());
/*
    std::copy(
      dest.begin(),
      dest.end(),
      std::ostream_iterator<int>(std::cout, "\n")
    );
*/
    return 0;
}

3 个答案:

答案 0 :(得分:1)

我认为你可以将矢量的元素存储在一个集合中。如果不需要排序,您可以使用unordered_set。只需执行以下操作 -

std::unordered_set<int> integers;

for (int i = 0; i < count; i++) {
    std::vector<int> src = getNextVector(i);

    for (int j = 0; j < src.size(); j++) {
        integers.insert(src[i]);
    }
}

或者如@StoryTeller建议的那样,您可以使用适当的函数而不是循环。例如 -

std::unordered_set<int> integers;

for (int i = 0; i < count; i++) {
    std::vector<int> src = getNextVector(i);
    integers.insert(src.begin(), src.end());
}

答案 1 :(得分:0)

我的第一个想法是“可以使用unordered_set快速轻松地完成”,后来我意识到它对int的帮助不大(int的哈希仍然是int,所以我看不到这里业绩增长)。所以,最后我决定对它进行基准测试,结果如下:

N = 4 Set implementation 304703 miliseconds
N = 4 Unordered set implementation 404469 miliseconds
N = 4 Vect implementation 91769 miliseconds

N = 20 Set implementation 563320 miliseconds
N = 20 Unordered set implementation 398049 miliseconds
N = 20 Vect implementation 176558 miliseconds

N = 40 Set implementation 569628 miliseconds
N = 40 Unordered set implementation 420496 miliseconds
N = 40 Vect implementation 207368 miliseconds

N = 200 Set implementation 639829 miliseconds
N = 200 Unordered set implementation 456763 miliseconds
N = 200 Vect implementation 245343 miliseconds

N = 2000 Set implementation 728753 miliseconds
N = 2000 Unordered set implementation 499716 miliseconds
N = 2000 Vect implementation 303813 miliseconds

N = 20000 Set implementation 760176 miliseconds
N = 20000 Unordered set implementation 480219 miliseconds
N = 20000 Vect implementation 331941 miliseconds

所以,很显然,对于你在这里给我们的样品,你实施的是最快的。当您的API仅返回几个可能的矢量组合且迭代次数很少时就是这种情况。我决定通过rand()为N&gt;提供更多不同的值,以验证发生了什么。 4(*)。它保持这种方式。无序集是最慢的(哈希计算成本)。 所以,回答你的问题:自己对你的情况进行基准测试 - 这是确定哪一个是最快的最佳方法。

(*)rand()的不良随机性不是错误,而是此处的一项功能。

编辑: 我的答案并没有提供没有说没有更快的算法 - 我已经对STL进行了基准测试,乍一看似乎表现得与结果提供的不同。但肯定有一种方法可以更快地进行独特的连接,也许是一组矢量或不同容器的组合,我希望有人会提供一个。

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <set>
#include <unordered_set>
#include <chrono>

std::vector<int> getNextVector(int i) {
    if (i == 0) {
        std::vector<int> v = { 1,2,3 };
        return v;
    }
    else if (i == 1) {
        std::vector<int> v = { 3,4,5 };
        return v;
    }
    else if (i == 2) {
        std::vector<int> v = { 5,6,7 };
        return v;
    }
    else if (i == 3) {
        std::vector<int> v = { 7,8,9 };
        return v;
    }
    return {rand() % 10000,rand() % 10000,rand() % 10000 };
}


void set_impl(std::set<int>& dest, int N)
{
    // dest.reserve(20); // we can find this, for simplicity hardcode...

    for (int i = 0; i < N; i++) {
        std::vector<int> src = getNextVector(i);
        dest.insert(
            std::make_move_iterator(src.begin()),
            std::make_move_iterator(src.end())
        );
    }
}

void uset_impl(std::unordered_set<int>& dest, int N)
{
    // dest.reserve(20); // we can find this, for simplicity hardcode...

    for (int i = 0; i < N; i++) {
        std::vector<int> src = getNextVector(i);
        dest.insert(
            std::make_move_iterator(src.begin()),
            std::make_move_iterator(src.end())
        );
    }
}

void vect_impl(std::vector<int>& dest, int N)
{
    for (int i = 0; i < N; i++) {
        std::vector<int> src = getNextVector(i);
        dest.insert(
            dest.end(),
            std::make_move_iterator(src.begin()),
            std::make_move_iterator(src.end())
        );
    }

    std::sort(dest.begin(), dest.end());
    dest.erase(unique(dest.begin(), dest.end()), dest.end());
}

int main(int argc, char** argv) {


    for (int N : { 4, 20, 40, 200, 2000, 20000 })
    {

        const int K = 1000000 / N;

        using clock = std::chrono::high_resolution_clock;

        std::set<int> sdest;
        auto start = clock::now();
        for (int i = 0; i < K; i++)
        {
            sdest.clear();
            set_impl(sdest, N);
        }
        auto set_ms = std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start).count();


        std::unordered_set<int> usdest;
        start = clock::now();
        for (int i = 0; i < K; i++)
        {
            usdest.clear();
            uset_impl(usdest, N);
        }
        auto uset_ms = std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start).count();

        std::vector<int> dest;
        dest.reserve(N); // we can find this, for simplicity hardcode...
        start = clock::now();
        for (int i = 0; i < K; i++)
        {
            dest.clear();
            vect_impl(dest, N);
        }
        auto vect_ms = std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start).count();

        std::cout << "N = " << N << " Set implementation " << set_ms << " miliseconds\n";
        std::cout << "N = " << N << " Unordered set implementation " << uset_ms << " miliseconds\n";
        std::cout << "N = " << N << " Vect implementation " << vect_ms << " miliseconds\n";

    }
    return 0;
}

答案 2 :(得分:0)

如果你想保留从外部API收到的元素的顺序并且它们没有排序,那么我建议你创建一个你保持排序的第二个向量。然后对已排序的向量执行lower_bound,如果返回的迭代器不是目标和已排序向量中的值插入(使用返回的迭代器作为已排序向量中的插入位置)。对整数使用set或unordered set可能会非常慢(可能要慢几个数量级)。如果您不关心订单,请使用单个排序的矢量。

vector<int> sorted;
....
vector<int> src = getNextVector(i);
for( int i : src ) {
  auto itr = std::lower_bound( sorted.begin(), sorted.end(), i );
  if( *itr != i ) {
     sorted.insert(itr, i);
     integers.push_back(i);
  }
}

如果您知道每次调用getNextVector的值都是唯一的,那么您可以执行以下操作(可能更快。)

vector<int> sorted;
....
vector<int> src = getNextVector(i);
vector<int> usrc;
for( int i : src ) {
  auto itr = std::lower_bound( sorted.begin(), sorted.end(), i );
  if( *itr != i ) {
     usrc.push_back(i);
     integers.push_back(i);
  }
}
sorted.insert(sorted.end(), usrc.begin(), usrc.end());
std::sort( sorted.begin(), sorted.end() );