我正在解决一个问题,我应该找到nth
矩阵的4x4
幂,其中n
可以与10^15
一样大,并且因为答案中的值可以非常大我可以使用modulo 10^9+7
。
鉴于Matrix是 -
2 1 -2 -1
A= 1 0 0 0
0 1 0 0
0 0 1 0
我为此目的编写了代码,但其运行时间超过了预期的时间。所以 有人请帮我减少时间复杂度。
#define FOR(k,a,b) for(typeof(a) k=(a); k < (b); ++k)
typedef long long ll;
#define dim 4
struct matrix {
long long a[dim][dim];
};
#define MOD 1000000007
matrix mul(matrix x, matrix y)
{
matrix res;
FOR(a, 0, dim) FOR(b, 0, dim) res.a[a][b] = 0;
FOR(a, 0, dim) FOR(b, 0, dim) FOR(c, 0, dim) {
ll temp = x.a[a][b] * y.a[b][c];
if (temp <= -MOD || temp >= MOD)
temp %= MOD;
res.a[a][c] += temp;
if (res.a[a][c] <= -MOD || res.a[a][c] >= MOD)
res.a[a][c] %= MOD;
}
return res;
}
matrix power(matrix m, ll n)
{
if (n == 1)
return m;
matrix u = mul(m, m);
u = power(u, n / 2);
if (n & 1)
u = mul(u, m);
return u;
}
matrix M, RP;
int main()
{
FOR(a, 0, dim) FOR(b, 0, dim) M.a[a][b] = 0;
M.a[0][0] = 2;
M.a[0][1] = 1;
M.a[0][2] = -2;
M.a[0][3] = -1;
M.a[1][0] = 1;
M.a[2][1] = 1;
M.a[3][2] = 1;
int nt;
scanf("%d", &nt);
while (nt--) {
ll n;
scanf("%lld", &n);
RP = power(M, n);
FOR(a, 0, dim)
FOR(b, 0, dim)
printf("%lld\n", RP.a[a][b]);
}
return 0;
}
答案 0 :(得分:2)
[评论者表示这个答案是不完整的。答案保留在这里以供参考,但不想再投票。评论者是否会根据自己的判断添加更完整的答案?]
是。一个很好的方法来做你想要的事情是众所周知的。你必须对角化矩阵。
对角化需要一些编程。在教派中解释了here,的理论。 14.6。 幸运的是,像LAPACK这样的现有矩阵代数库已经包含了对角化例程。
@Haile正确且有趣地观察到并非所有矩阵都是可对角化的,存在退化情况。我对这些案件没有多少实际经验。存在Schur分解(参见先前链接源的第14.10节),但我通常看到Schur仅用于制作理论点,而不是用于实际计算。不过,我相信舒尔会工作。我怀疑,实施它需要付出很多努力,但即使在严格不可对角化的矩阵的情况下,它也会起作用。
答案 1 :(得分:2)
您可以利用多个测试用例来减少总计算量。
请注意,每次拨打电源时,您都会重新计算原始矩阵2的所有幂。因此,对于像10 ^ 15(大约2 ^ 50)这样的数字,您将最终将矩阵平方50次,并且还计算该数字中每个非零位的乘数(可能是25次)。
如果你只是预先计算2的50次幂,那么每个测试用例只需要平均25次乘法而不是75次。
您可以进一步采用这个想法,并使用不同的基数进行求幂。这将导致更多的预计算,但每个测试值的最终矩阵乘法更少。
例如,您可以预先计算[M ^ 1,M ^ 2,M ^ 3],[M ^ 4,M ^ 8,而不是预先计算M ^ 2,M ^ 4,M ^ 8,M ^ 16, M ^ 12],[M ^ 16,M ^ 32,M ^ 48]等M ^ 51将是(M ^ 3)*(M ^ 48)而不是M * M ^ 2 * M ^ 16 * M ^ 32
答案 2 :(得分:0)
这不是一个关于更快地对指标进行取幂的想法,而是关于加速整个程序的想法。
如果要求您执行10 ^ 4个指数,这并不意味着它们应该独立完成。您可以对请求进行排序,并为每次下一次计算重复使用先前的结果。
您还可以存储先前计算的中间结果。