简体   繁体   中英

Is there any way to reduce time complexity to find this matrix to the power n?

I am working on a problem where I am supposed to find nth power of a 4x4 matrix where n can be as large as 10^15 and since values in answer can be very large I can use modulo 10^9+7 . Given Matrix is-

   2  1 -2 -1
A= 1  0  0  0
   0  1  0  0
   0  0  1  0 

I have written a code for this purpose but its running time is more than desired time. So anyone please help me in reducing time complexity.

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

[Commenters have shown that this answer is incomplete. The answer is retained here for reference, but wants no more upvotes. Would the commenters add more complete answers, at their discretion?]

Yes. An excellent way to do exactly what you want is known. You must diagonalize the matrix.

Diagonalization will require some programming. The theory is explained here, in sect. 14.6. Fortunately, existing matrix-algebra libraries like LAPACK already include diagonalization routines.

@Haile correctly and interestingly observes that not all matrices are diagonalizable, that there exist degenerate cases. I do not have much practical experience with such cases. There is the Schur decomposition (see sect. 14.10 of the previously linked source), but I have normally seen Schur used only to make theoretical points, not to do practical calculations. Still, I believe that Schur would work. It would take a lot of effort to implement it, I suspect, but it would work, even in the case of the strictly nondiagonalizable matrix.

You could take advantage of the multiple test cases to reduce the total computation.

Note that every time you call power you are recomputing all the powers of 2 of your original matrix. So for a number like 10^15 (roughly 2^50) you will end up squaring a matrix 50 times, and also calculating a multiply for each nonzero bit in the number (perhaps 25 times).

If you simply precompute the 50 powers of 2, then each test case would only require on average 25 multiplications instead of 75.

You can take this idea a little further and use a different base for your exponentiation. This would result in more precomputation, but fewer final matrix multiplications for each test value.

For example, instead of precomputing M^2, M^4, M^8, M^16 you could precompute [M^1,M^2,M^3],[M^4,M^8,M^12],[M^16,M^32,M^48] and so M^51 would be (M^3)*(M^48) instead of M*M^2*M^16*M^32

This is not really an idea about exponentiating matrices faster, but about speeding up the entire program.

If you are asked to perform 10^4 exponentiations, this doesn't mean they should be done independently. You can sort requests and reuse previous result for each next computation.

Also you can store intermediate results from previous computations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM