为什么这个版本的代码速度较慢?

时间:2017-12-09 19:09:46

标签: c++ algorithm social-networking

对于课程中的一项作业,我们必须使用图表为可能的朋友找到建议。我们可以自由地实现这一点。我选择根据潜在朋友与人的距离为每个朋友(图表的顶点)赋予权重。

我编写了两个版本的实现,一个使用哈希表(无序地图)来跟踪每个人拥有的点数。比它获取数据并将其转换为矢量然后按它累积的点对其进行排序。

例如,如果我们有这样的朋友图 Sample Graph

因此,在此示例图中,1和2是0的朋友,3是1的唯一朋友,依此类推。

我的算法从距离2(距离1表示顶点已经是朋友)开始,并根据顶点的距离给出点。

例如,0的可能朋友可以通过以下方式计算:

At a distance of 2 (points: 1/2)
  3 : 0.5 (0 - 1 - 3)
  4 : 0.5 (0 - 2 - 4)
At a distance of 3 (points: 1/9)
  4 : 0.111111 (0 - 1 - 3 - 4)
  5 : 0.111111 (0 - 1 - 3 - 5)
  5 : 0.111111 (0 - 2 - 4 - 5)
At a distance of 4 (points: 1/16)
  5 : 0.0625 (0 - 1 - 3 - 4 - 5)

添加这些权重后,我们得到:

  4 : 0.611111
  3 : 0.5
  5 : 0.284722

从这些信息我们可以看出,第4个人最有机会成为0的朋友

我的第一次尝试是使用散列表来存储所有人(顶点)及其点值,然后将其转换为矢量并最后按点对它们进行排序。

std::unordered_map<int, double> friends;

/// @todo make this recursive
for (auto e1 : vertex_list[vs]->edge_list) { // this is level 1 (already our friend))
    for (auto e2 : vertex_list[e1->target_vertex]->edge_list) { // this is level 2 (potential friends)
        friends[e2->target_vertex] += 0.5;

        for (auto e3 : vertex_list[e2->target_vertex]->edge_list) {
            friends[e3->target_vertex] += 1/9.0;

            for (auto e4 : vertex_list[e3->target_vertex]->edge_list) {
                friends[e4->target_vertex] += 1/16.0;
            }
        }
    }
}

std::vector<std::pair<int, double>> tmp{friends.begin(), friends.end()};
std::sort(tmp.begin(), tmp.end(), [](auto a, auto b) {return a.second > b.second;});

for (int i = 0; i < ((tmp.size() < 10)? tmp.size() : 10); ++i) {
    // cout << i << "\t\t";
    cout << tmp[i].first << '\t' << tmp[i].second << endl;
}

然后我认为不是所有这些数据复制,在数据结构之间进行转换,如果我创建了我的数据结构,给出了O(1)查找时间并且还保持元素排序,我的意思是这是针对数据的结构和算法类。我创造了这个:

该结构使用哈希表将迭代器保存到std :: list,以保持键值对的排序。哈希表将键映射到列表中的元素的迭代器。在向值中添加一些数据时,数据结构将上下移动节点以使其保持排序。

template <
    typename TKey,
    typename TValue //,
    //typename THash = std:: // we can add this functionality later
> class ordered_vecmap {
public:
    using key_type   = TKey;
    using value_type = TValue;
    using size_type  = size_t;
    using locator    = typename std::list<std::pair<key_type, value_type>>::iterator;

    ordered_vecmap(size_type sz) : mIndex{}, mData{} {
        mIndex.rehash(sz);
    }

    void add_to_value(const key_type& key, const value_type& val) { 
        // create valuu if not exists
        if (mIndex.find(key) == mIndex.end()) { // new key-value pair
            mData.emplace_back(key, 0.0); // insert new value
            mIndex.insert({key, --mData.end()}); // add the key so it now points to the last member of the vector
        }

        // add to the value
        locator idx = mIndex[key];
        idx->second += val;

        // fix it so its ordered
        // while the current value is out of ordered
        // and we havent reached the max value
        // also prevents iterator invalidation
        auto prev = std::prev(idx);
        while (idx != mData.begin() and idx->second > prev->second) {
            mData.splice(prev, mData, idx);
            --(--prev);
        }
    }

    std::list<std::pair<key_type, value_type>>& get_results() { return mData; }

private:

    std::unordered_map<key_type, locator> mIndex; // this containtains all the indexes
    std::list<std::pair<key_type, value_type>> mData; // this contains the actual data

};

这是找朋友的功能(几乎与原算法相同)

ari::ordered_vecmap<int, double> friends{vertex_list.size()};

/// @todo make this recursive
for (auto e1 : vertex_list[vs]->edge_list) { // this is level 1 (already our friend))
    for (auto e2 : vertex_list[e1->target_vertex]->edge_list) { // this is level 2 (potential friends)
        friends.add_to_value(e2->target_vertex, 0.5);

        for (auto e3 : vertex_list[e2->target_vertex]->edge_list) {
            friends.add_to_value(e3->target_vertex, 1/9.0);

            for (auto e4 : vertex_list[e3->target_vertex]->edge_list) {
                friends.add_to_value(e4->target_vertex, 1/16.0);
            }
        }
    }
}

auto& tmp = friends.get_results();
auto it = tmp.begin();
for (int i = 0; i < ((tmp.size() < 10)? tmp.size() : 10); ++i) {
    // cout << i << "\t\t";
    cout << it->first << '\t' << it->second << endl;
    ++it;
}

问题在于理论上,我创建的数据结构应该接近,如果不是比我原来的算法更好。然而,我计算了两种算法,我发现我的数据结构慢了5-7倍。第一个算法耗时93毫秒,自定义数据结构耗时468毫秒,有4,000个顶点

我很困惑为什么?

修改 因此,其中一条评论说,可能是因为列表中的缓存未命中,所以我将我的代码转换为使用std :: vector而不是std :: list,它似乎变得更糟。第一种算法执行相同的操作:93ms,自定义数据结构花费671ms。这是我使用向量修改的代码(只是add_to_value成员函数)

void add_to_value(const key_type& key, const value_type& val) { 
    // create valuu if not exists       
    if (mIndex.find(key) == mIndex.end()) { // new key-value pair
        mIndex.insert({key, mData.size()}); // add the key so it now points to the last member of the vector
        mData.emplace_back(key, 0.0); // insert new value
    }

    // add to the value
    locator idx = mIndex[key];
    mData[idx].second += val;

    // fix it so its ordered
    // while the current value is out of ordered
    // and we havent reached the max value
    // also prevents iterator invalidation
    while (idx != 0 and mData[idx].second > mData[idx - 1].second) {
        std::swap(mData[idx], mData[idx - 1]); // swap this an previous
        mIndex[mData[idx].first] = idx; // fix the previous locator
        --idx;
    }

    mIndex[key] = idx;
}

0 个答案:

没有答案