如何在O(m + n)中得到两个稀疏矢量的点积,其中m和n是两个向量中的元素个数

时间:2015-09-24 04:20:57

标签: algorithm big-o linear-algebra

我有两个稀疏矢量X和Y,想要得到O(m + n)中的点积,其中m和n是X和Y中非零元素的数量。我能想到的唯一方法是从向量X中选取每个元素并遍历向量Y以查找是否存在具有相同索引的元素。但这需要O(m * n)。我将矢量实现为链表,每个节点都有一个元素。

3 个答案:

答案 0 :(得分:6)

如果您的向量存储为元组的链表,并且每个元组包含索引和非零元素的值并按索引排序,则可以执行此操作。

通过从矢量中选择下一个元素来迭代这两个矢量,您在较低的索引处。如果索引相同,则将元素相乘并存储结果。

重复直到一个列表到达结尾。

由于每个列表中每个非零元素都有一步,因此复杂度为O(m + n)。

脚注:数据结构不必是链表,但必须提供O(1)方式来访问下一个非0元素及其索引。

答案 1 :(得分:0)

排序列表

假设您的非零元素在两个向量中按坐标索引排序,则通过 merge 算法实现。这是计算机科学中的标准算法,它将两个排序的序列合并为一个排序的序列,并且它在 O(M + N)中工作。

有两种方法可以做到这一点。第一个是检查合并中的相同元素。这确实是最好的方式。

第二种方法是首先合并,然后检查等于(它们必须连续):

std::pair<int, double> vecA[n], vecB[m], vecBoth[n+m];
std::merge(vecA, vecA+n, vecB, vecB+m, vecBoth);
double dotP = 0.0;
for (int i = 0; i+1 < n+m; i++)
  if (vecBoth[i].first == vecBoth[i+1].first)
    dotP += vecBoth[i].second * vecBoth[i+1].second;

std::merge的复杂性是 O(M + N)

上面的例子假设数据存储在数组中(这是稀疏向量和矩阵的最佳选择)。如果您想使用链接列表,还可以在 O(M + N)时间内执行合并,请参阅this question

未排序的列表

即使您的列表未排序,您仍然可以在 O(M + N)时间内执行点积。我们的想法是首先将 A 的所有元素放入哈希表中,然后遍历 B 的元素,看看是否存在具有相同索引的哈希中的元素。

如果索引非常大(例如超过百万),那么也许您应该使用非平凡的哈希函数。但是,如果您的索引相当小,那么您可以避免使用哈希函数。只需使用大于矢量维度的数组。为了快速清除这个数组,你可以使用“代数”这个技巧。

//global data! must be threadlocal in case of concurrent access
double elemsTable[1<<20];
int whenUsed[1<<20] = {0};
int usedGeneration = 0;

double CalcDotProduct(std::pair<int, double> vecA[n], vecB[m]) {
  usedGeneration++;   //clear used array in O(1)
  for (int i = 0; i < n; i++) {
    elemsTable[vecA[i].first] = vecA[i].second;
    whenUsed[vecA[i].first] = usedGeneration;
  }
  double dotP = 0.0;
  for (int i = 0; i < m; i++)
    if (whenUsed[vecB[i].first] == usedGeneration)
      dotP += elemsTable[vecB[i].first] * vecB[i].second;
  return dotP;
}

请注意,您可能需要清除每十亿点产品whenUsed一次。

答案 2 :(得分:0)

$(document).ready(function(){
var dataSet = '{"status":"success","data":[{"key":"Open","values":[[1512432000,55.65],[1512518400,54.45],[1512604800,53.05],[1512691200,56.4],[1512950400,54.65],[1513036800,55],[1513123200,53.9],[1513209600,56],[1513296000,55.9],[1513555200,56.25]]},{"key":"High","values":[[1512432000,55.65],[1512518400,55],[1512604800,57.95],[1512691200,57],[1512950400,55.5],[1513036800,55.4],[1513123200,58.9],[1513209600,56.5],[1513296000,58.5],[1513555200,57.75]]},{"key":"Low","values":[[1512432000,53.65],[1512518400,53.1],[1512604800,53.05],[1512691200,54.1],[1512950400,54.65],[1513036800,53.7],[1513123200,53],[1513209600,54],[1513296000,55.1],[1513555200,52.5]]},{"key":"Close","values":[[1512432000,54.55],[1512518400,53.6],[1512604800,55.9],[1512691200,54.65],[1512950400,54.9],[1513036800,54.1],[1513123200,55.65],[1513209600,54.45],[1513296000,56.5],[1513555200,55.65]]}]}';

 var n = nv.addGraph(function () {
            var chart = nv.models.cumulativeLineChart()
                .x(function (d) {
                    return d[0]
                })
                .y(function (d) {
                    return d[1]
                })
                .color(d3.scale.category10().range())
                .useInteractiveGuideline(true)
            ;

            chart.xAxis
                .tickFormat(function (d) {
                    return d3.time.format('%x')(new Date(d))
                })

            chart.yAxis
                .tickFormat(d3.format(''));



            d3.select('#chart1 svg')
                .datum(JSON.parse(dataSet)['data'])
                .call(chart);


            return chart;
        });
});