Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何使这个匹配算法运行得更快?_C++_Performance_C++11_Performance Testing_Matching - Fatal编程技术网

C++ 如何使这个匹配算法运行得更快?

C++ 如何使这个匹配算法运行得更快?,c++,performance,c++11,performance-testing,matching,C++,Performance,C++11,Performance Testing,Matching,我有两个指向数据结构X的指针列表,算法非常简单: 它在第一个列表A上循环,并尝试在列表B中找到第一个匹配元素。要求每个列表中至少有50k个元素: #include <iostream> #include <memory> #include <chrono> #include <vector> #include <algorithm> #include <string> struct X { std::string

我有两个指向数据结构X的指针列表,算法非常简单:

它在第一个列表A上循环,并尝试在列表B中找到第一个匹配元素。要求每个列表中至少有50k个元素:

#include <iostream>
#include <memory>
#include <chrono>
#include <vector>
#include <algorithm>
#include <string>

struct X {
    std::string field_1;
    std::string field_2;
    std::string field_3;
    std::string field_4;

    X(std::string f1, std::string f2, std::string f3, std::string f4)
        : field_1(f1)
        , field_2(f2)
        , field_3(f3)
        , field_4(f4)
    {};

    bool equal(const std::shared_ptr<X>& x) {
        return  (x->field_1 == field_1) &&
                (x->field_2 == field_2) &&
                (x->field_3 == field_3) &&
                (x->field_4 == field_4);
    };

    X *match = nullptr;
};
typedef std::shared_ptr<X> X_ptr;


class Timer
{
public:
    Timer(std::string name) : beg_(clock_::now()), name_(name) {}

    ~Timer() {
        std::cout << "Elapsed(" << name_ << "): " << elapsed() << std::endl;
    }

    void reset() { beg_ = clock_::now(); }
    double elapsed() const {
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count();
    }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;

    std::string name_;
};


std::string random_string(size_t length)
{
    auto randchar = []() -> char
    {
        const char charset[] =
            "0123456789"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        const size_t max_index = (sizeof(charset) - 1);
        return charset[rand() % max_index];
    };
    std::string str(length, 0);
    std::generate_n(str.begin(), length, randchar);
    return str;
}

int main()
{
    Timer t("main");

    std::vector <X_ptr> list_A;
    std::vector <X_ptr> list_B;

    const int MAX_ELEM = 50000;

    list_A.reserve(MAX_ELEM);
    list_B.reserve(MAX_ELEM);

    {
        Timer t("insert");

        for (int i = 0; i < MAX_ELEM; i++) {
            list_A.push_back(X_ptr(new X{ random_string(2), random_string(2), random_string(2), random_string(2) }));
            list_B.push_back(X_ptr(new X{ random_string(2), random_string(2), random_string(2), random_string(2) }));
        }
    }

    {
        Timer t("match");

        std::for_each(list_A.begin(), list_A.end(), [list_B](X_ptr& a) {
            auto found_b = std::find_if(list_B.begin(), list_B.end(), [a](const X_ptr& b) {
                return a->equal(b);
            });

            if (found_b != list_B.end()) {
                a->match = found_b->get();

                std::cout << "match OK \n";
            }
        });
    }
}

如果您能想出任何其他方法来优化它以使其运行更快,我们将不胜感激。

您可以使用以下方法:

std::sort(list_B.begin(), list_B.end(), deref_less<X>);
{
    Timer t("match");

    for (const auto& a : list_A) {
        auto it = std::lower_bound(list_B.begin(), list_B.end(), a, deref_less<X>);

        if (it != list_B.end() && **it == *a) {
            a->match = it->get();

            std::cout << "match OK \n";
        }
    }
}

.

您使用的是向量,因此每次对列表B的查找都会进行,其中n是B中的元素数。这意味着如果m是列表A中的元素数,则总算法为Om*n。因此,如果m和n A的大小相似,则有一个On^2算法。这对于任何大的n来说都太慢了。要解决此问题,请将列表B转换为无序的映射,您可以将此作为此算法的一部分进行转换,因为映射键中的元素是列表B中的一个元素,其值为任意值,例如0。然后,您可以使用“在地图上查找”在O1时间内对地图执行查找。因此,您的算法将继续运行,比^2上的算法要好得多

比如说

       std::unordered_map< X_ptr, int > value_map;
       Time r t("match");

        std::for_each(list_B.begin(), list_B.end(), [&](X_ptr& b) {
                value_map[b] = 0;
        });

       std::for_each(list_A.begin(), list_A.end(), [value_map](X_ptr& a) {
            auto found_b = value_map.find( a );

            if ( found_b != value_map.end() )
            {
                a->match = found_b->first.get();

                std::cout << "match OK \n";
            }
        });
    }
新版本:

Elapsed(insert): 0.0719907
Elapsed(match): 0.0388562
Elapsed(main): 0.130884

列表顺序是固定的还是可以在使用前对其进行排序?请在使用前对列表进行稳定排序。然后您可以在列表b上使用二进制搜索,而不是“如果”查找。@MatthiasB,是的,它可以排序。但是哪个字段更适合codereview.stackexchange.com在这种情况下,最大的好处是不必遍历整个列表进行搜索,一种方法是以可以排序的方式对其进行结构化,然后使用二进制搜索。通过优化字符串比较,可以获得进一步的收益,例如,在分配字符串时生成哈希代码,然后只比较哈希。使用哈希表键作为查找表对于编码工具箱来说是一个有用的技巧。
Elapsed(insert): 0.0758608
Elapsed(match): 182.899
Elapsed(main): 182.991
Elapsed(insert): 0.0719907
Elapsed(match): 0.0388562
Elapsed(main): 0.130884