マトリックスアクセラレーションアルゴリズムについて話す

序文

(この記事を見て、最初にマトリックスとは何かを理解してください)

マトリックスアクセラレーションは非常に魔法のアルゴリズムであり、極端なデータの下で複雑な問題を短時間で解決でき、非常にシンプルです。例から始めましょう:

例1:フィボナッチアイテムn

フィボナッチ数列はf(1)= f(2)= 1、f(i)= f(i-1)+ f(i-2)、i> 2、を満たしますf(n)(N <= 1e9)

このデータは大きく、再帰によって日常的に解決することはできません。次に、迂回せずに、式の導出を開始します。

f_ {n} = f_ {n-1} + f_ {n-2}(キーの繰り返し)のためf_ {n-1} * 1 + f_ {n-2} * 1、行列の乗算(n * mおよびm * q行列)に入れることができます2項×2項であるため、m = 2であり、通常の累積乗算を実現するには、結果もn * 2の行列、つまりq = 2であることを確認する必要があります。他に必要な数がないため、n = 1です。行列は、乗算後、(1,1)の位置はf_ {n}です。ただしf_ {n + 1}、左行列はである必要がある\ begin {bmatrix} f_ {n}&f_ {n-1} \ end {bmatrix}ため、(2,2)の位置f_ {n-1}f_ {n-2}と他の2つの数値の「加算と乗算」演算によって取得する必要がありますf_ {n-1}明らかに、f_ {n-1} = f_ {n-1} * 1 + f_ {n-2} * 0したがって、全体の乗算は\ begin {bmatrix} f_ {n-1}&f_ {n-2} \ end {bmatrix} * \ begin {bmatrix} 1&1 \\ 1&0 \ end {bmatrix}、を取得でき\ begin {bmatrix} f_ {n}&f_ {n-1} \ end {bmatrix}、次に1を乗算してを\ begin {bmatrix} 1&1 \\ 1&0 \ end {bmatrix}取得でき\ begin {bmatrix} f_ {n + 1}&f_ {n} \ end {bmatrix}ます。したがって\ 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}、f2とf1は既知であり、行列を使用してそれらをすばやく指数化します。

(コード自身の水)

例2:TRのシーケンス

TRは数学が大好きで、奇妙な数学の問題を研究するためにドラフトペーパーをよく取り出します。最近、彼は突然数字のシーケンスに興味を持ち、フィボナッチに似た数字のシーケンス、つまりTn = 1 * f1 + 2 *を見つけました。 f2 + 3 * f3 +……+ n * fn(fnはフィボナッチのn番目の項の値)、TRはTn%m(1≤n、m≤2^ 31)の値を見つけるのを手伝ってくれるように頼みます-1)。

とするとV_ {n} = n * f_ {n}

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}

したがってT_ {n} = T_ {n-1} * 1 + V_ {n-2} * 1 + V_ {n-1} * 1 + f_ {n-2} * 2 + f_ {n-1} * 1(キー再帰)、左側の行列はに設定され\ begin {bmatrix} T_ {n-1}&V_ {n-2}&V_ {n-1}&f_ {n-2}&f_ {n-1} \ end {bmatrix}、次に

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

だから\ 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}

わかりますか?最後に\ 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}、行列の高速べき乗を使用します。

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

例3:[山東省が選択]再帰シーケンス(バージョン2)

総括する

(たとえば、質問1と2、1つの解決策を理解するだけです)行列加速法は、最初に主要な漸化式をリストし(右側は1回のm項でなければなりません)、次に1 * mとm *をリストします。式に従ってm行列、そしてそれはとても簡単です。

(update2021.3.13)拡張:一般化モーメント乗算

上記の方法は、プラス記号とマイナス記号で構成される再帰式のみを解くようです。実際、最大記号と最小記号を使用して接続された式は、行列で最適化することもできます。このとき、一般化された行列乗算が使用されます。

C = A * B、C(i、j)= max / min(A(i、k)+ B(k、j))、(これはそのうちの1つにすぎません)

一般化された行列の乗算は結合法則を満たしますが、分布率を満たさないことが証明できますが、行列の加速には結合法則のみが必要なため、これで十分です。

プラス記号とマイナス記号の制約を取り除くため、行列アクセラレーションは、一見最適化されていない多くの問題を最適化できます。動的DPは、一般化モーメント乗算に基づいて確立されたアルゴリズムです。

おすすめ

転載: blog.csdn.net/weixin_43960287/article/details/88993370