コンテンツにスキップ
メインメニュー
メインメニュー
サイドバーに移動
非表示
案内
メインページ
最近の更新
未作成ページ
おまかせ表示
ヘルプ
MonoBook
検索
検索
ログイン
個人用ツール
ログイン
ログアウトした編集者のページ
もっと詳しく
投稿記録
トーク
「
定数倍高速化
」を編集中
ページ
議論
日本語
閲覧
編集
ソースを編集
履歴表示
ツール
ツール
サイドバーに移動
非表示
操作
閲覧
編集
ソースを編集
履歴表示
全般
リンク元
関連ページの更新状況
特別ページ
ページ情報
警告:
ログインしていません。編集を行うと、あなたの IP アドレスが公開されます。
ログイン
または
アカウントを作成
すれば、あなたの編集はその利用者名とともに表示されるほか、その他の利点もあります。
スパム攻撃防止用のチェックです。 けっして、ここには、値の入力は
しない
でください!
'''定数倍高速化'''とは、[[アルゴリズム]]の改善による[[高速化]]とは違い、計算処理の方法を改善することにより[[計算量]]の[[オーダー]]を変えずに処理を高速化することである。 ==概要== [[競技プログラミング]]においてはほとんどの場合意味がないが、たまに定数倍高速化で無理矢理通せる場合や、[[想定解]]の[[アルゴリズム]]だけど自分の[[実装]]が悪いので定数倍高速化しないと通らない場合など、定数倍高速化が役に立つ場面がある。 ==例== ===なるべく除算をしない=== <source lang="c"> unsigned long long repeat_add( unsigned long long a,unsigned long long b,unsigned long long c) { unsigned long long result=0; while(b>0) { if(b&1)result=(result+a)%c; a=(a<<1)%c; b>>=1; } return result; } </source> このコードは、a,bがULLONG_MAX/2以下、cがULLONG_MAX/2+1以下のとき、 (a*b) mod cを[[オーバーフロー]]しないように気をつけながら求める[[プログラム]]である。 しかし、よく考えると、最初に与えられるaがc未満のとき、常にresult+a<2*c,(a<<1)<2*cが成り立つことがわかる。 よって、次のように書くことができ、重い[[除算]]が比較的軽い減算になるため高速化できる。 <source lang="c"> /* suppose that a<c */ unsigned long long repeat_add( unsigned long long a,unsigned long long b,unsigned long long c) { unsigned long long result=0; while(b>0) { if(b&1) { result+=a; if(result>=c)result-=c; } a+=a; if(a>=c)a-=c; b>>=1; } return result; } </source> ===なるべく再帰を使わない=== [[再帰]]を使った[[プログラム]]は、再帰を使わない処理に比べて、 [[関数]]の呼び出しによる[[スタック]]の確保・復帰処理が入るため低速になることが多い。 ===コンパイラに最適化してもらう=== <source lang="c"> #include <stdio.h> #include <string.h> #define MOD_BY 100000007ULL typedef unsigned long long ull; void matrix_mul(ull in1[50][50],ull in2[50][50],ull out[50][50],int n) { ull result[50][50]; int i,j,k; for(i=0;i<n;i++) { for(j=0;j<n;j++) { result[i][j]=0; for(k=0;k<n;k++) { result[i][j]+=(in1[i][k]*in2[k][j])%MOD_BY; } result[i][j]%=MOD_BY; } } memcpy(out,result,sizeof(result)); } void matvec_mul(ull mat[50][50],ull vec[50],ull out[50],int n) { ull result[50]; int i,j; for(i=0;i<n;i++) { result[i]=0; for(j=0;j<n;j++) { result[i]+=(mat[i][j]*vec[j])%MOD_BY; } result[i]%=MOD_BY; } memcpy(out,result,sizeof(result)); } void make_matrix(ull mat[50][50],int n) { int i,j; for(i=0;i<n;i++) { for(j=0;j<n;j++)mat[i][j]=0; mat[0][i]=1; if(i>0)mat[i][i-1]=1; } } void matrix_kaizyo(ull out[50][50],int n,ull kai) { ull matrix[50][50]; int i,j; for(i=0;i<n;i++) { for(j=0;j<n;j++)out[i][j]=0; out[i][i]=1; } make_matrix(matrix,n); for(i=0;i<64 && kai;i++,kai>>=1) { if(kai & 1)matrix_mul(matrix,out,out,n); matrix_mul(matrix,matrix,matrix,n); } } int main(void) { int k; ull n; ull ans_matrix[50][50]; ull init_vector[50]={1}; ull ans_vector[50]; while(1) { scanf("%d%I64u",&k,&n); if(k==0 && n==0ULL)break; matrix_kaizyo(ans_matrix,k,n-1); matvec_mul(ans_matrix,init_vector,ans_vector,k); printf("%I64u\n",ans_vector[0]); } return 0; } </source> この[[ソースコード]]は、直前のk項の数の[[和]]を次の項の値とする[[数列]](ただし第1項は1、第n(n≦0)項は0とみなす)の第n項の値を[[100000007]]で割った余りを求める[[プログラム]]である。 この[[コード]]を[[gcc]]4.7.2で[[コンパイル]]した場合、-O2の[[最適化]]をかけた[[バイナリ]]は、 同じ入力に対し、最適化オプションをつけずに[[コンパイル]]した[[バイナリ]]の約43%の実行時間で答えを出すことができた。 ===OpenMPを使う=== [[OpenMP]]を使うと、数行のおまじないとコンパイルオプションの追加により、 実行中の[[CPU]]使用率を上げる代わりに実行時間を短くすることができる。 詳しくは、[[OpenMP]]のページを見ていただきたい。 [[category: アルゴリズム]]
編集内容の要約:
MonoBookへの投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。 自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。
また、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細は
MonoBook:著作権
を参照)。
著作権保護されている作品は、許諾なしに投稿しないでください!
このページを編集するには、下記の確認用の質問に回答してください (
詳細
):
1たす1は?(全角で入力してください)
キャンセル
編集の仕方
(新しいウィンドウで開きます)
本文の横幅制限を有効化/無効化