对于以下问题,是否存在亚三次算法?


11

给定对称实矩阵,有一种算法可计算总和在所有1 \ leq i <j <k \ leq n上,时间复杂度优于O(n ^ 3)n×nA=(aij)

i,j,kmax(aij,aik,ajk)
1i<j<knO(n3)

3
请注意,这至少与计算给定图中的三角形数量一样困难。如果您的输入矩阵对图形进行编码,使得“ 0”表示边,而“ 1”表示缺失边,则max(aij,aik,ajk)=0当且仅当存在是由节点ijk形成的三角形k,否则为1
Jukka Suomela

1
我认为唯一已知的用于三角形计数的显着次立方算法是基于快速矩阵乘法的?在此问题中将这些技术应用于此处可能很棘手。另外,如果您正在寻找实用的东西,那么基于快速矩阵乘法的任何东西都将无济于事。
Jukka Suomela

Answers:


3

有一种相当实用的方法可以在时间内工作,其中是处理器字中的位数。主要思想是您按递增顺序(任意断开关系)逐个迭代矩阵的元素,然后“打开它们”。考虑一下某个三元组的最大元素打开的时刻。为了简单起见,让我们假设所述元素是。当最后一个元素打开时,很自然地将三元组的值添加到答案中。因此,我们必须计算可能的的数量,例如和O(n3/w)waij,aik,ajkaijkaikajk已经打开(这是三元组的数量,这里是最大的元素,因此它们才刚刚完全打开)。在这里,我们可以通过使用位优化来加快天真的实现。aijO(n)

有关详细信息,您可以参考以下C ++ 11中的实现,该实现应适用于, (它不是非常优化的;但是,至少在我的机器上,它对于的天真求和仍然远远超过了)n5000|aij|109n=5000

// 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;
}

如果考虑使用位优化作弊,则可以在此处使用四种俄语方法获得相同的结果,从而产生算法,该算法应不太实用(因为在大多数现代硬件上都很大)但理论上更好。实际上,让我们选择并将矩阵的每一行都保留为整数从到数组,其中第个数字在该阵列对应于行的范围从位包容独家在O(n3/logn)wblog2nnb02b1iibmin(n,(i+1)b)0-索引。我们可以在时间内预先计算出每两个这样的块的标量积。更新矩阵中的位置很快,因为我们只更改一个整数。要查找行和的标量积,只需迭代与该行相对应的数组,就可以在表中查找相应块的标量积,并对所得乘积求和。O(22bb)ij

上面的段落假设整数运算花费时间。这是一个很常见的假设,因为它通常实际上并不会改变算法的比较速度(例如,如果我们不使用该假设,则蛮力方法实际上会在时间起作用 (此处我们以位操作来衡量时间),如果取一个绝对值至少为整数值(对于某个常数则为其他值) (否则我们可以用矩阵乘法);但是上面建议的四种俄语方法使用nO(1)O(n3logn)aijnεε>0O(nε)O(n3/logn)在这种情况下,操作的大小为;因此它可以执行位操作,尽管更改了模型,但仍然比蛮力好)。O(logn)O(n3)

但是,关于方法存在的问题仍然很有趣。O(n3ε)

此答案中提出的技术(位优化和四俄语方法)绝不是原创的,此处为了阐述完整而提出。但是,找到一种应用这些方法的方法并非易事。


首先,您的建议确实在实践上很有帮助,我可以在用例中尝试一下。谢谢!其次,对于任何固定宽度的数值类型,您的算法的计算复杂度仍然为。您能否详细介绍方法?我不知道如何找到并比更快的标量积(如果我们访问它们的所有元素,这将是必需的)。O(n3)O(n3/logn)mat[i]mat[j]O(n)
user89217 '18

另外,您的代码未定义mat似乎很重要的代码。我知道如何定义它,但是我想知道它是否(mat[i] & mat[j]).count()可以与任何STL容器一起使用。
user89217 '18

1
关于mat-我想我们必须使用std::vector<boost::dynamic_bitset<int64_t>>
user89217 '18

关于mat:是的,我实际上已经想到了标准位集,但是boost::dynamic_bitset在这种情况下甚至更好,因为它的大小不必是编译时常数。将编辑答案以添加此详细信息并阐明四位俄罗斯人的做法。
卡班5

1
太好了,在我看来,这很可靠。一点要点:由于跨二分法模型假设我们可以对机器单词执行运算,因此无需预先计算任何标量积。实际上,该模型假设,因此至少与。而且,正如您所说,预先计算标量乘积没有实际意义(数组查找将比二进制运算慢)。O(1)wlog2nO(n3/w)O(n3/logn)
user89217 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.