Hablando sobre el algoritmo de aceleración matricial

Prefacio

(Mire este artículo, primero comprenda qué es una matriz)

La aceleración matricial es un algoritmo muy mágico, puede resolver problemas complejos en poco tiempo con datos extremos y también es muy simple. Comencemos con ejemplos:

Ejemplo 1: elemento n de Fibonacci

La secuencia de Fibonacci satisface f (1) = f (2) = 1, f (i) = f (i-1) + f (i-2), i> 2, pregunte f (n). (N <= 1e9)

Estos datos son grandes y no se pueden resolver de forma rutinaria mediante recursividad. Entonces no demos un rodeo, simplemente iniciemos la derivación de la fórmula:

Debido a f_ {n} = f_ {n-1} + f_ {n-2}(repetición de clave), se puede poner f_ {n-1} * 1 + f_ {n-2} * 1en una multiplicación de matriz (matriz n * my m * q). Debido a que son 2 términos por 2 términos, entonces m = 2, para lograr una multiplicación acumulativa regular, es necesario asegurarse de que el resultado también sea una matriz de n * 2, entonces q = 2. Debido a que no hay otros números obligatorios, n = 1. La matriz es , después de multiplicar, la posición de (1,1) es f_ {n}. Sin embargo f_ {n + 1}, se requiere que la matriz de la izquierda sea \ begin {bmatrix} f_ {n} & f_ {n-1} \ end {bmatrix}así, por lo que la posición de (2,2) debe obtenerse mediante la operación de "suma y multiplicación" de f_ {n-1}, f_ {n-2}y los otros dos números f_ {n-1}. Obviamente, f_ {n-1} = f_ {n-1} * 1 + f_ {n-2} * 0entonces la multiplicación completa es \ begin {bmatrix} f_ {n-1} & f_ {n-2} \ end {bmatrix} * \ begin {bmatrix} 1 & 1 \\ 1 & 0 \ end {bmatrix}, se puede obtener \ begin {bmatrix} f_ {n} & f_ {n-1} \ end {bmatrix}y luego multiplicar por uno para \ begin {bmatrix} 1 & 1 \\ 1 & 0 \ end {bmatrix}obtener \ begin {bmatrix} f_ {n + 1} & f_ {n} \ end {bmatrix}, y así sucesivamente. Por lo tanto \ begin {bmatrix} f_ {2} & f_ {1} \ end {bmatrix} * \ begin {bmatrix} 1 & 1 \\ 1 & 0 \ end {bmatrix} ^ {n-2} = \ begin {bmatrix} f_ {n} & f_ {n-1} \ end {bmatrix}, se conocen f2 y f1 y se usa una matriz para exponenciarlos rápidamente.

(Código agua propia)

Ejemplo 2: La secuencia de TR

A TR le gustan mucho las matemáticas y, a menudo, saca borradores para estudiar problemas matemáticos extraños. Recientemente, de repente se interesó en la secuencia de números. Encontró una secuencia de números similar a Fibonacci, a saber: Tn = 1 * f1 + 2 * f2 + 3 * f3 + …… + n * fn (fn es el valor del n-ésimo término de Fibonacci), ahora TR quiere pedirle que ayude a encontrar el valor de Tn% m (1≤n, m≤2 ^ 31 - 1).

Supongamos V_ {n} = n * f_ {n}, entonces

V_ {n} = n * f_ {n-1} + n * f_ {n-2}

V_ {n} = (n-1) * f_ {n-1} + f_ {n-1} + (n-2) * f_ {n-2} + 2 * f_ {n-2}

V_ {n} = V_ {n-2} + V_ {n-1} + 2 * f_ {n-2} + f_ {n-1}

Entonces T_ {n} = T_ {n-1} * 1 + V_ {n-2} * 1 + V_ {n-1} * 1 + f_ {n-2} * 2 + f_ {n-1} * 1(recursividad de clave), la matriz de la izquierda se establece en \ begin {bmatrix} T_ {n-1} & V_ {n-2} & V_ {n-1} & f_ {n-2} & f_ {n-1} \ end {bmatrix}, y luego la

V_ {n-1} = T_ {n-1} * 0 + V_ {n-2} * 0 + V_ {n-1} * 1 + f_ {n-2} * 0 + f_ {n-1} * 0

V_ {n} = T_ {n-1} * 0 + V_ {n-2} * 1 + V_ {n-1} * 1 + f_ {n-2} * 2 + f_ {n-1} * 1

f_ {n-1} = T_ {n-1} * 0 + V_ {n-2} * 0 + V_ {n-1} * 0 + f_ {n-2} * 0 + f_ {n-1} * 1

f_ {n} = T_ {n-1} * 0 + V_ {n-2} * 0 + V_ {n-1} * 0 + f_ {n-2} * 1 + f_ {n-1} * 1

Entonces \ begin {bmatrix} T_ {n-1} & V_ {n-2} & V_ {n-1} & f_ {n-2} & f_ {n-1} \ end {bmatrix} * \ begin {bmatrix} 1 & 0 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 & 0 \\ 1 & 1 & 1 & 0 & 0 \\ 2 & 0 & 2 & 0 & 1 \\ 1 & 0 & 1 & 1 & 1 \ end {bmatrix} = \ begin {bmatrix} T_ {n} & V_ {n-1} & V_ {n} & f_ {n-1} & f_ {n} \ end {bmatrix}.

¿Lo entiendes? Finalmente \ begin {bmatrix} T_ {2} & V_ {1} & V_ {2} & f_ {1} & f_ {2} \ end {bmatrix} * \ begin {bmatrix} 1 & 0 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 & 0 \\ 1 & 1 & 1 & 0 & 0 \\ 2 & 0 & 2 & 0 & 1 \\ 1 & 0 & 1 & 1 & 1 \ end {bmatrix} ^ {n-2} = \ begin {bmatrix} T_ {n} & V_ {n-1} & V_ {n} & f_ {n-1} & f_ {n} \ end {bmatrix}, use la exponenciación rápida de matriz.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,mod;
struct matrix{
    int n,m;
    long long c[105][105];
    matrix(){memset(c,0,sizeof(c));}
    matrix operator*(const matrix&a){
        matrix r;r.n=n,r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j])%mod;
        return r;
    }
}a,b;
matrix mpow(matrix a,long long b){
    matrix res;res.n=res.m=a.n;
    for(int i=1;i<=res.n;i++)res.c[i][i]=1;
    for(;b;b>>=1){
        if(b&1)res=res*a;
        a=a*a;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&n,&mod);
    if(n==1){
        printf("%lld",1%mod);
        putchar('\n');
        return 0;
    }
    a.n=1,a.m=5;
    a.c[1][1]=3,a.c[1][2]=a.c[1][4]=a.c[1][5]=1,a.c[1][3]=2;
    b.n=b.m=5;
    b.c[1][1]=1;
    b.c[2][1]=b.c[2][3]=b.c[3][1]=b.c[3][2]=b.c[3][3]=1;
    b.c[4][1]=b.c[4][3]=2,b.c[4][5]=1;
    b.c[5][1]=b.c[5][3]=b.c[5][4]=b.c[5][5]=1;
    a=a*mpow(b,n-2);
    printf("%lld",a.c[1][1]%mod);
    putchar('\n');
    return 0;
}

Ejemplo 3: [Seleccionado por la provincia de Shandong] Secuencia recursiva (versión 2)

para resumir

(Por ejemplo, las preguntas uno y dos, solo comprenda una solución) El método de aceleración matricial es primero enumerar la fórmula de recurrencia clave (el lado derecho debe ser un término m de una sola vez), y luego enumerar 1 * my m * m matrices de acuerdo con la fórmula, y luego es así de simple.

(update2021.3.13) Extensión: Multiplicación de momento generalizada

El método anterior parece resolver solo la fórmula recursiva compuesta por signos más y menos. De hecho, la fórmula conectada al tomar los símbolos máximo y mínimo también se puede optimizar con una matriz. En este momento, se usa la multiplicación de matrices generalizada:

C = A * B, C (i, j) = máximo / mínimo (A (i, k) + B (k, j)), (Este es solo uno de ellos)

Se puede demostrar que la multiplicación de matrices generalizada todavía satisface la ley asociativa, pero no satisface la tasa de distribución, pero esto es suficiente, porque la aceleración de la matriz solo necesita la ley asociativa.

Debido a que se eliminan las restricciones de los signos más y menos, la aceleración matricial puede optimizar muchos problemas aparentemente no optimizados El DP dinámico es un algoritmo establecido sobre la base de la multiplicación de momentos generalizada.

Supongo que te gusta

Origin blog.csdn.net/weixin_43960287/article/details/88993370
Recomendado
Clasificación