【问题标题】:Why does the unordered_map perform about 100 times slower than a 2d vector for look-ups?为什么 unordered_map 的查找速度比 2d 向量慢 100 倍?
【发布时间】:2016-02-06 06:12:50
【问题描述】:
  • 我尝试为大型数据集实现背包算法。

  • 2d 矢量解决方案非常适用于大约 100 个项目的中等数据集。

  • 由于二维向量不适用于涉及大约 1000 个项目的大型数据集,因此我决定使用哈希表并根据需要缓存值。

  • 我已经使用 hash_value() 从 boost 将 std::pair 散列到 unordered_map 中。

  • 但我不明白为什么这个解决方案的运行速度比 二维向量解。哈希表不是用于超快速查找吗?

  • 两种实现都无法在有限时间内处理大型数据集。

  • 我已附上代码以及“中型”和“大型”数据集。

  • 代码有 unordered_map 和 2d 矢量实现,后者被注释掉了。

  • 如果有人能指出性能缓慢的原因并提出一些优化建议以便能够处理大型数据集,那将非常有帮助。

  • 输入文件的格式为。 (例如):

6 4 //重量,物品数量 3 4 2 3 4 2 4 3

最优解是 8。

Download Link for Large dataset (1000 items)

Download Link for Medium dataset (100 items)

Download Link for Source Code

//代码如下:

//标题、宏和全局变量:

#include<iostream>
#include<vector>
#include<algorithm>
#include<fstream>
#include<string>
#include<sstream>
#include<unordered_map>
#include<boost/functional/hash.hpp>
using namespace std;

typedef vector< vector<long long> > vii;
typedef vector< pair<int,int> > vp;
typedef pair<int,int> myPair;
typedef unordered_map< myPair, long long, boost::hash<myPair> > myMap;

vp elmnts;
//vii arr2d;
myMap arr;

//背包函数:

long long knapsack(int n, int w)
{
//arr2d.resize(n+1, vector<long long>(w+1));
int vi,wi;

for(int j=0; j<=w; ++j)
//  arr2d[0][j] = 0;
    arr.emplace(make_pair(0,j), 0);

for(int i=1; i<=n; ++i)
{
    vi = elmnts[i-1].first;
    wi = elmnts[i-1].second;

    for(int j=0; j<=w; ++j)
    //  arr2d[i][j] = (wi > j) ? arr2d[i-1][j] : max(arr2d[i-1][j], arr2d[i-1][j-wi] + vi);
        arr.emplace(make_pair(i,j), (wi > j) ? arr[make_pair(i-1,j)] : max(arr[make_pair(i-1,j)], arr[make_pair(i-1,j-wi)]+ vi));
}

//return arr2d[n][w];
return arr[make_pair(n,w)];
}

//主要功能

int main()
{
ifstream file("/home/tauseef/Desktop/DAA2/knapsack1.txt");
int n,w;
string line;
pair<int,int> elmnt;

getline(file, line);
stringstream ss(line);
ss >> w;
ss >> n;

while(getline(file, line))
{
    stringstream ss1(line);
    ss1 >> elmnt.first;
    ss1 >> elmnt.second;
    elmnts.push_back(elmnt);
}

cout<<"\nThe optimal solution is: "<<knapsack(n,w)<<endl;
file.close();
}

【问题讨论】:

  • O(1) 查找并不意味着它比O(n) 查找快!这仅意味着对于某个值n 大于某个值n0 在某个假想机器上O(1)O(n) 快。
  • O(1) 表示固定时间,可以是 1 小时。 O(n) 可以表示 n 秒。
  • 好吧,您能指出任何可以帮助此代码处理包含 2000 个项目的大型数据集的优化吗? @UlrichEckhardt
  • 从来没有那样看它@BoPersson,谢谢 :) 但是你能帮我弄清楚为什么这段代码不能处理大数据集吗?
  • 学习使用分析器。

标签: c++ algorithm stl unordered-map knapsack-problem


【解决方案1】:

没想到差异如此之大:在我的机器上,数组版本比 hash_map 版本快 100 倍。但是想了想……

你应该预料到地图会变慢 - 有很多开销:调用 make_pair,创建一个配对对象,计算哈希,在地图中搜索它,构造返回值,来回复制对象而不是仅仅查看 -升值!

另一方面,您不会从切换到地图中获益根本,因为最后,正如现在编码的那样,您的地图中的元素与大批。如果您将一些元素从地图中删除,那么您的更改将是有意义的,但您不这样做。

但你的代码中更大的问题是你使用了来自维基百科的伪多项式算法,它需要O(n*W) 内存。这意味着您将需要 32GB 内存来处理更大的测试用例,这可能意味着将内存与硬盘交换,具体取决于您的系统有多大并且变得非常慢。

解决办法是取需要O(W)内存的算法版本:

long long knapsack(const vector<myPair> &objects, int W)
{
    vector<long long> bests(W+1, -1L);//-1 = this value is not reachable
    //the only possible configuration at start: an empty set, value is also 0
    bests[0]=0;


    for(const myPair &object:objects){//try out all objects
        int v = object.first;
        int w = object.second;
        //update all possible configurations:
        for(int cur=W;cur>=0;--cur){//backwards->object at most once in every configuration!
          int next=cur+w;
          if(bests[cur]!=-1 && next<=W)//consider only reachable configurations and discard too big weights
            bests[next]=std::max(bests[next], bests[cur]+v);
       }
    }

    return *max_element(bests.begin(), bests.end());
}

最重要的部分是我们回溯可能的配置,因此可以就地更新配置(更新的配置是当前扫描中已经进行的配置)。

我猜这个版本对于更大的情况应该需要不到 1 分钟的时间(考虑到输入的大小,这是相当合理的)。我不保证这是没有错误的,但希望你能明白它的要点。

【讨论】:

  • 非常感谢@ead :) 这正是我所需要的。抱歉回复晚了,正在思考为什么代码会产生不正确的结果。请批准编辑,以便我接受您的回答:)
猜你喜欢
  • 2012-10-07
  • 2011-09-02
  • 1970-01-01
  • 2021-05-30
  • 2013-01-10
  • 1970-01-01
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多