【发布时间】:2016-04-26 04:52:50
【问题描述】:
简介及源码
我正在尝试计算两个维度为 169647 的稀疏向量之间的余弦相似度。作为输入,这两个向量表示为 <index, value> 形式的字符串。只有向量的非零元素被赋予索引。
x = "1:0.1 43:0.4 100:0.43 10000:0.9"
y = "200:0.5 500:0.34 501:0.34"
首先,我们使用函数splitVector 将x 和y 分别转换为两个vectors<float>.。然后我们使用函数cosine_similarity 计算距离。没关系split 函数。我正在使用它以防您希望运行代码。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
void split(const string& s, char c,vector<string>& v) {
string::size_type i = 0;
string::size_type j = s.find(c);
while (j != string::npos) {
v.push_back(s.substr(i, j-i));
i = ++j;
j = s.find(c, j);
if (j == string::npos)
v.push_back(s.substr(i, s.length()));
}
}
float cosine_similarity(const std::vector<float> & A,const std::vector<float> & B)
{
float dot = 0.0, denom_a = 0.0, denom_b = 0.0 ;
for(unsigned int i = 0; i < A.size(); ++i)
{
dot += A[i] * B[i] ;
denom_a += A[i] * A[i] ;
denom_b += B[i] * B[i] ;
}
return dot / (sqrt(denom_a) * sqrt(denom_b)) ;
}
void splitVector(const vector<string> & v, vector<float> & values)
{
vector<string> tmpv;
string parsed;
for(unsigned int i = 0; i < v.size(); i++)
{
split(v[i], ':', tmpv);
int idx = atoi(tmpv[0].c_str());
float val = atof(tmpv[1].c_str());
tmpv.clear();
values[idx] = val;
}//end for;
}//end function
int main()
{
//INPUT VECTORS.
vector<string> x {"1:0.1","43:0.4","50:0.43","90:0.9"};
vector<string> y {"20:0.5","40:0.34","50:0.34"};
//STEP 1: Initialize vectors
int dimension = 169647;
vector<float> X;
X.resize(dimension, 0.0);
vector<float> Y;
Y.resize(dimension, 0.0);
//STEP 2: CREATE FLOAT VECTORS
splitVector(x, X);
splitVector(y, Y);
//STEP 3: COMPUTE COSINE SIMILARITY
cout << cosine_similarity(X,Y) << endl;
}
问题和建议的解决方案
初始化和填充vector<float> 是个问题。它真的需要很多执行时间。我正在考虑在 C++ 中使用 std::map<int,float> 结构。其中 X 和 Y 将由 :
std::map<int,float> x_m{ make_pair(1,0.1), make_pair(43,0.4), make_pair(50,0.43), make_pair(90,0.9)};
std::map<int,float> y_m{ make_pair(20,0.5), make_pair(40,0.34), make_pair(50,0.34)};
为此,我使用了以下函数:
float cosine_similarity(const std::map<int,float> & A,const std::map<int,float> & B)
{
float dot = 0.0, denom_a = 0.0, denom_b = 0.0 ;
for(auto &a:A)
{
denom_a += a.second * a.second ;
}
for(auto &b:B)
{
denom_b += b.second * b.second ;
}
for(auto &a:A)
{
if(B.find(a.first) != B.end())
{
dot += a.second * B.find(a.first)->second ;
}
}
return dot / (sqrt(denom_a) * sqrt(denom_b)) ;
}
问题
- 您能帮我计算一下复杂性吗?
- 使用地图的第二个提议函数会降低复杂性吗?
- 您认为解决方案如何?
【问题讨论】:
-
您是否考虑过仅使用现有库来计算它?
-
我认为使用向量计算它可能比地图更快。向量有什么问题?
-
如果使用matlab会更好:)
-
@HaniGoc
map有一个不必要的O(nlogn)构造时间如果你的索引已经有序(在大多数稀疏向量表示中它们是)所以它将主导距离比较,即@987654334 @。然后是遍历两个映射而不是增加两个索引(或四个指针)的开销。 -
@HaniGoc 我想重点是,如果实现和维护更快的数组版本一样容易或更容易,为什么还要麻烦地图?
标签: c++ dictionary vector distance cosine-similarity