Sparse Similarity:给定一些整数集合,计算集合之间的稀疏相似度。稀疏相似度定义为交集大小和并集大小的比值。
假设有s个集合,每个集合中有n个整数。最简单的方式就是对于每一对集合,查找第一个集合中有多少元素在第二个集合中,这样就算出了交集的大小,最后根据容斥原理计算并集的大小即可。这种方法的时间复杂度为O(s ^ 2 * n ^ 2);如果查找过程用set,那么复杂度降为O(s ^ 2 * nlogn);如果查找过程用unordered_set,那么复杂度降为O(s ^ 2 * n)。根据题干s和n的输入规模都会达到500,3次方的算法应该是会超时的。
先用这种方法交了一下,然后就出现了评论区中说的四舍五入导致的精度问题,所以要在比值的的最后加上一个epsilon😦😦😦
再来降低一下复杂度,也就是优化一下O(s ^ 2)的部分。如果不想两两全部比较,那就争取只处理相似度不为0的集合对,假设有p个,最坏情况下每一对都有n个整数,则这一部分的时间复杂度为O(pn)。如果要使用这种方法,就要求我们提前能够用某种数据结构表示出相似的部分,亦即对于一个给定的整数x,有哪些集合包含x,这也就是倒排索引,整体的时间复杂度为O(sn + pn)。
这道题很难和正确结果进行比较,也不知道预期输出是怎么个算法得来的,最好是找个工具排个序最后再文本比较下。
class Solution {
private:
unordered_map<int, vector<size_t>> InvertedIndex;
void createInvertedIndex(const vector<vector<int>> &docs)
{
for(size_t i = 0; i < docs.size(); i++)
{
const vector<int> &doc = docs[i];
for(int n : doc)
{
InvertedIndex[n].push_back(i);
}
}
}
map<pair<size_t, size_t>, int> Pair2InterSize;
void computeIntersectionSize()
{
for(auto iter = InvertedIndex.begin(); iter != InvertedIndex.end(); iter++)
{
const vector<size_t> &DocList = iter->second;
if(DocList.size() == 1) continue;
for(size_t i = 0; i < DocList.size(); i++)
{
for(size_t j = i + 1; j < DocList.size(); j++)
{
Pair2InterSize[make_pair(DocList[i], DocList[j])]++;
}
}
}
}
vector<string> ans;
void adjustToSimilarities(const vector<vector<int>> &docs)
{
for(auto iter = Pair2InterSize.begin(); iter != Pair2InterSize.end(); iter++)
{
size_t IntersectionSize = iter->second;
size_t UnionSize = docs[iter->first.first].size() + docs[iter->first.second].size() - IntersectionSize;
ostringstream oss;
oss << iter->first.first << ',' << iter->first.second << ": ";
oss << fixed << setprecision(4) << static_cast<double>(IntersectionSize) / static_cast<double>(UnionSize) + 1e-9;
ans.push_back(oss.str());
}
}
public:
vector<string> computeSimilarities(vector<vector<int>>& docs) {
createInvertedIndex(docs);
computeIntersectionSize();
adjustToSimilarities(docs);
return ans;
}
};
书上还给出了另外一种方法,将所有的元素排序,这样相同的元素就会聚到一起,之后的处理方法就和上面的相同了,时间复杂度为O(sn * logsn + pn),和上面不用unordered_map的情况一样,但是运行时间没多大区别。
class Solution {
private:
struct Element
{
int ele;
size_t DocID;
Element(int n, size_t ID) : ele(n), DocID(ID){}
bool operator<(const Element &rhs)
{
if(ele < rhs.ele) return true;
else if(ele == rhs.ele) return DocID < rhs.DocID;
else return false;
}
};
vector<Element> Sorted;
void sortDocs(const vector<vector<int>> &docs)
{
for(size_t i = 0; i < docs.size(); i++)
{
const vector<int> &doc = docs[i];
for(int n : doc)
{
Sorted.push_back(Element(n, i));
}
}
sort(Sorted.begin(), Sorted.end());
}
map<pair<size_t, size_t>, int> Pair2InterSize;
void computeIntersectionSize()
{
for(size_t i = 0; i < Sorted.size(); i++)
{
size_t j = i + 1;
while(j < Sorted.size() && Sorted[j].ele == Sorted[i].ele){
Pair2InterSize[make_pair(Sorted[i].DocID, Sorted[j].DocID)]++;
j++;
}
}
}
vector<string> ans;
void adjustToSimilarities(const vector<vector<int>> &docs)
{
for(auto iter = Pair2InterSize.begin(); iter != Pair2InterSize.end(); iter++)
{
size_t IntersectionSize = iter->second;
size_t UnionSize = docs[iter->first.first].size() + docs[iter->first.second].size() - IntersectionSize;
ostringstream oss;
oss << iter->first.first << ',' << iter->first.second << ": ";
oss << fixed << setprecision(4) << static_cast<double>(IntersectionSize) / static_cast<double>(UnionSize) + 1e-9;
ans.push_back(oss.str());
}
}
public:
vector<string> computeSimilarities(vector<vector<int>>& docs) {
sortDocs(docs);
computeIntersectionSize();
adjustToSimilarities(docs);
return ans;
}
};
本文探讨了计算多个整数集合间稀疏相似度的方法,包括直接比较、使用倒排索引优化及元素排序策略,旨在减少时间复杂度,解决大规模数据集上的精度问题。
754

被折叠的 条评论
为什么被折叠?



