对于课程中的一项作业,我们必须使用图表为可能的朋友找到建议。我们可以自由地实现这一点。我选择根据潜在朋友与人的距离为每个朋友(图表的顶点)赋予权重。
我编写了两个版本的实现,一个使用哈希表(无序地图)来跟踪每个人拥有的点数。比它获取数据并将其转换为矢量然后按它累积的点对其进行排序。
因此,在此示例图中,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;
}