给定对称实矩阵,有一种算法可计算总和在所有1 \ leq i <j <k \ leq n上,时间复杂度优于O(n ^ 3)?
给定对称实矩阵,有一种算法可计算总和在所有1 \ leq i <j <k \ leq n上,时间复杂度优于O(n ^ 3)?
Answers:
有一种相当实用的方法可以在时间内工作,其中是处理器字中的位数。主要思想是您按递增顺序(任意断开关系)逐个迭代矩阵的元素,然后“打开它们”。考虑一下某个三元组的最大元素打开的时刻。为了简单起见,让我们假设所述元素是。当最后一个元素打开时,很自然地将三元组的值添加到答案中。因此,我们必须计算可能的的数量,例如和已经打开(这是三元组的数量,这里是最大的元素,因此它们才刚刚完全打开)。在这里,我们可以通过使用位优化来加快天真的实现。
有关详细信息,您可以参考以下C ++ 11中的实现,该实现应适用于, (它不是非常优化的;但是,至少在我的机器上,它对于的天真求和仍然远远超过了)
// code is not very elegant,
// but should be understandable
// here the matrix a has dimensions n x n
// a has to be symmetric!
int64_t solve (int n, const vector<vector<int32_t>> &a)
{
std::vector<boost::dynamic_bitset<int64_t>> mat
(n, boost::dynamic_bitset<int64_t>(n));
vector<pair<int, int>> order;
for (int j = 1; j < n; j++)
for (int i = 0; i < j; i++)
order.emplace_back(i, j);
sort(order.begin(), order.end(),
[&] (const pair<int, int> &l, const pair<int, int> &r)
{return a[l.first][l.second] < a[r.first][r.second];});
int64_t ans = 0;
for (const auto &position : order)
{
int i, j;
tie (i, j) = position;
mat[i][j] = mat[j][i] = 1;
// here it is important that conditions
// mat[i][i] = 0 and mat[j][j] = 0 always hold
ans += (mat[i] & mat[j]).count() * int64_t(a[i][j]);
}
return ans;
}
如果考虑使用位优化作弊,则可以在此处使用四种俄语方法获得相同的结果,从而产生算法,该算法应不太实用(因为在大多数现代硬件上都很大)但理论上更好。实际上,让我们选择并将矩阵的每一行都保留为整数从到数组,其中第个数字在该阵列对应于行的范围从位包容独家在-索引。我们可以在时间内预先计算出每两个这样的块的标量积。更新矩阵中的位置很快,因为我们只更改一个整数。要查找行和的标量积,只需迭代与该行相对应的数组,就可以在表中查找相应块的标量积,并对所得乘积求和。
上面的段落假设整数运算花费时间。这是一个很常见的假设,因为它通常实际上并不会改变算法的比较速度(例如,如果我们不使用该假设,则蛮力方法实际上会在时间起作用 (此处我们以位操作来衡量时间),如果取一个绝对值至少为整数值(对于某个常数则为其他值) (否则我们可以用矩阵乘法);但是上面建议的四种俄语方法使用在这种情况下,操作的大小为;因此它可以执行位操作,尽管更改了模型,但仍然比蛮力好)。
但是,关于方法存在的问题仍然很有趣。
此答案中提出的技术(位优化和四俄语方法)绝不是原创的,此处为了阐述完整而提出。但是,找到一种应用这些方法的方法并非易事。
mat[i]
mat[j]
mat
似乎很重要的代码。我知道如何定义它,但是我想知道它是否(mat[i] & mat[j]).count()
可以与任何STL容器一起使用。
mat
-我想我们必须使用std::vector<boost::dynamic_bitset<int64_t>>
。
mat
:是的,我实际上已经想到了标准位集,但是boost::dynamic_bitset
在这种情况下甚至更好,因为它的大小不必是编译时常数。将编辑答案以添加此详细信息并阐明四位俄罗斯人的做法。