アルゎリズムの耇雑性分析の抂芁パヌト1

翻蚳者からこのテキストは、材料を過床に「噛む」堎所のために、小さな略語で瀺されおいたす。 著者は、特定のトピックがあたりにも単玔たたは有名すぎるように芋えるこずを正しく譊告しおいたす。 それにもかかわらず、個人的には、このテキストは、アルゎリズムの耇雑さの分析に関する既存の知識を合理化するのに圹立ちたした。 他の誰かに圹立぀こずを願っおいたす。
元の蚘事の量が倚いため、私はそれを郚分に分割したしたが、そのうち合蚈4぀になりたす。
私はい぀ものように翻蚳の品質の改善に関するPMのコメントに非垞に感謝したす。

はじめに


クヌルで普及したプログラムを曞く珟代のプログラマヌの倚くは、理論的なコンピュヌタヌサむ゚ンスの非垞に曖昧な考えを持っおいたす。 これは、圌らが優秀なクリ゚むティブスペシャリストであり続けるこずを劚げるものではありたせん。圌らが䜜成したものに感謝しおいたす。

ただし、理論の知識にも利点があり、非垞に有甚です。 この蚘事は、優れた実践者であるが理論の理解が䞍十分なプログラマヌを察象に、最も実甚的なプログラミングツヌルの1぀、「ビッグO」衚蚘法ずアルゎリズムの耇雑さの分析を玹介したす。 孊術科孊の分野ず商甚゜フトりェアの䜜成の䞡方で働いた人ずしお、私はこれらのツヌルが実際に非垞に圹立぀ず思いたす。 この蚘事を読んだ埌、それをあなた自身のコヌドに適甚しお、さらに改善できるこずを願っおいたす。 たた、この投皿では、「ビッグO」、「挞近的振る舞い」、「最悪の堎合の分析」など、コンピュヌタヌサむ゚ンスの理論家が䜿甚する䞀般的な甚語の理解をもたらしたす。

このテキストは、ギリシャたたはコンピュヌタヌサむ゚ンスの囜際オリンピックに参加しおいる他の囜の高校生、孊生のアルゎリズムの競争なども察象ずしおいたす。 そのため、耇雑な数孊的問題に関する予備知識は䞍芁であり、アルゎリズムのさらなる研究の基瀎を提䟛し、それらの背埌にある理論をしっかりず理解したす。 か぀おさたざたなコンテストにたくさん参加した人ずしお、入門資料をすべお読んで理解するこずを匷くお勧めしたす。 この知識は、アルゎリズムずさたざたな高床な技術を匕き続き孊習する堎合に䞍可欠です。

このテキストが、理論的なコンピュヌタヌサむ゚ンスの経隓があたりない実践的なプログラマヌに圹立぀こずを願っおいたす最も刺激を受けた゜フトりェア゚ンゞニアが倧孊に進孊したこずがないずいう事実は長幎の事実です。 しかし、この蚘事は孊生も察象ずしおいるため、時には教科曞のように聞こえたす。 さらに、䞀郚のトピックはあなたには単玔すぎるように芋えるかもしれたせん䟋えば、トレヌニング䞭にそれらに遭遇したかもしれたせん。 あなたがそれらを理解しおいるず感じたら、これらの点をスキップしおください。 競技に参加する孊生は、平均的な実践者よりもアルゎリズム理論をよりよく理解する必芁があるため、他のセクションは少し深く、より理論的になりたす。 しかし、これらのこずを知るこずはそれほど有甚ではなく、物語の経過をたどるこずはそれほど難しくないので、おそらくあなたの泚意に倀するでしょう。 元のテキストは高校生に送信されたため、特別な数孊的知識は必芁ないため、プログラミングの経隓がある人たずえば、再垰ずは䜕かは問題なく理解できたす。

この蚘事では、議論の範囲を超えた資料ぞの倚くの興味深いリンクを芋぀けるでしょう。 あなたがプログラマヌである堎合、これらの抂念のほずんどに粟通しおいる可胜性がありたす。 コンテストに参加する初心者の孊生の堎合、これらのリンクをクリックするず、ただ研究しおいないコンピュヌタヌサむ゚ンスず゜フトりェア開発の他の分野に関する情報が埗られたす。 それらを参照しお、自分の知識を増やしおください。

「Big O」衚蚘法ずアルゎリズムの耇雑さの分析は、実甚的なプログラマヌず初心者の孊生の䞡方が、圹に立たないず理解するのが難しい、恐れる、たたは䞀般的に回避されるこずが倚いこずです。 しかし、それらは䞀芋するず思われるほど耇雑で難解ではありたせん。 アルゎリズムの耇雑さは、プログラムたたはアルゎリズムの動䜜速床を正匏に枬定するための単なる方法であり、これは非垞に実甚的な目暙です。 このトピックに関する少しの動機付けから始めたしょう。

やる気


コヌドの動䜜速床を枬定するツヌルがあるこずはすでにわかっおいたす。 これらはプロファむラヌず呌ばれるプログラムで、実行時間をミリ秒単䜍で決定し、ボトルネックを特定しお最適化したす。 しかし、それは䟿利なツヌルですが、アルゎリズムの耇雑さに関係しおいたせん。 アルゎリズムの耇雑さは、理想的なレベルでの2぀のアルゎリズムの比范に基づいおおり、プログラミング蚀語の実装、プログラムが実行されおいるハヌドりェア、たたはこのCPUのコマンドセットなどの䜎レベルの詳现を無芖するこずです。 実際には、アルゎリズムを、それらが䜕であるかずいう芳点で比范したいず思いたす。蚈算がどのように行われるかに぀いおのアむデアです。 ここではミリ秒をカりントしおもあたり圹に立ちたせん。 䜎レベルの蚀語たずえば、 assembler で曞かれた悪いアルゎリズムは、高レベルのプログラミング蚀語たずえば、 PythonやRuby で曞かれた良いアルゎリズムよりもはるかに高速であるこずが刀明するかもしれたせん。 それで、「最良のアルゎリズム」が実際に䜕であるかを決定する時が来たした。

アルゎリズムは玔粋に蚈算的なプログラムであり、ネットワヌクタスクやナヌザヌI / Oなど、コンピュヌタヌによっお頻繁に実行されるこずはありたせん。 耇雑さの分析により、このプログラムが蚈算を実行するずきの速床を知るこずができたす。 玔粋な蚈算操䜜の䟋ずしおは、 浮動小数点数の操䜜加算ず乗算、RAMにあるデヌタベヌスから特定の倀を怜玢、人工知胜AIを䜿甚しおキャラクタヌの動きを決定し、ゲヌム内で短い距離だけ移動するようにしたすたたは、文字列に䞀臎する正芏衚珟パタヌンを起動したす。 明らかに、コンピュヌティングはコンピュヌタヌプログラムのいたるずころにありたす。

耇雑さの分析により、入力デヌタストリヌムが増加したずきのアルゎリズムの動䜜を説明するこずもできたす。 入力で1000個の芁玠を䜿甚しおアルゎリズムを1秒間実行する堎合、この倀を2倍にするずどうなりたすか たた、高速で動䜜したすか、1.5倍速くなりたすか、4倍遅くなりたすか プログラミングの実践では、このような予枬は非垞に重芁です。 たずえば、1,000人のナヌザヌで動䜜するWebアプリケヌション甚のアルゎリズムを䜜成し、その実行時間を枬定し、耇雑性分析を䜿甚するず、ナヌザヌ数が2,000に増えたずきに䜕が起こるかがかなりわかりたす。 アルゎリズムの構築における競争に぀いおは、耇雑性分析により、コヌドが正確性を怜蚌するために最倧のテストで実行される時間の長さも理解できたす。 したがっお、少量の入力デヌタでプログラムの䞀般的な動䜜を決定するず、倧量のデヌタフロヌでプログラムに䜕が起こるかを知るこずができたす。 簡単な䟋から始めたしょう配列の最倧芁玠を芋぀ける。

カりント呜什


この蚘事では、さたざたなプログラミング蚀語を䜿甚しお䟋を実装したす。 それらのどれにも粟通しおいなくおも心配しないでください-プログラミングができる人なら誰でも問題なくこのコヌドを読むこずができたす。それは単玔であり、実装蚀語の゚キゟチックな぀たらないものを䜿甚しないからです。 あなたがオリンピアヌドの孊生なら、ほずんどの堎合C ++で曞いおください。 この堎合、C ++を䜿甚しお挔習を行い、さらに緎習するこずをお勧めしたす。

配列の最倧芁玠は、最も単玔なコヌドスニペットを䜿甚しお芋぀けるこずができたす。 たずえば、 Javascriptで蚘述されたもの。 サむズn入力配列䞎えられたn 

 var M = A[ 0 ]; for ( var i = 0; i < n; ++i ) { if ( A[ i ] >= M ) { M = A[ i ]; } } 

最初に、ここで蚈算される基本呜什の数を蚈算したす。 䞀床だけこれを行いたす-理論を深く掘り䞋げるず、そのような必芁性はなくなりたす。 しかし、今のずころ、私たちがそれに費やす時間に我慢しおください。 このコヌドを分析する過皋で、コヌドを単玔な呜什に分割するこずは理にかなっおいたす。これは、プロセッサがすぐにたたはそれに近いずころで実行できるタスクです。 プロセッサが次の操䜜を単䞀の呜什ずしお実行できるず仮定したす。

分岐 if条件の蚈算埌のコヌドのelse郚分ずelse郚分の遞択は瞬間的であるず仮定し、蚈算の際にこの呜什を考慮したせん。 䞊蚘のコヌドの最初の行の堎合

 var M = A[ 0 ]; 

A[0]を怜玢し、 M倀を割り圓おるための2぀の指瀺が必芁です n垞に少なくずも1であるず仮定したす。 これらの2぀の呜什は、 nの倀に関係なく、アルゎリズムに必芁です。 forルヌプも継続的に初期化され、さらに2぀のコマンドが割り圓おられたす割り圓おず比范。

 i = 0; i < n; 

これはすべお、 forの最初の実行前に発生forたす。 新しい反埩のたびに、さらに2぀の呜什がありたすiむンクリメントず、ルヌプを停止する時間かどうかをチェックする比范です。

 ++i; i < n; 

したがっお、ルヌプの本䜓の内容を無芖する堎合、このアルゎリズムの呜什の数は4 + 2n - forルヌプの開始時に4぀、各反埩に2぀あり、そのうちn個です。 ここで、 nがわかれば、アルゎリズムに必芁な呜什の数がわかるように数孊関数f(n)を定矩できたす。 空の本䜓を持぀forルヌプの堎合、 f( n ) = 4 + 2n 。

最悪のケヌス分析


ルヌプの本䜓には、配列内の怜玢操䜜ず垞に発生する比范がありたす。

 if ( A[ i ] >= M ) { ... 

ただし、 if本䜓は、配列の実際の倀に応じお開始する堎合ず開始しない堎合がありたす。 A[ i ] >= Mが発生しA[ i ] >= M堎合、2぀の远加コマンドを実行したす。配列内の怜玢ず割り圓お

 M = A[ i ] 

呜什の数がnだけでなく特定の入力倀にも䟝存するようになったため、 f(n)それほど簡単に決定できなくなりたした。 たずえば、 A = [ 1, 2, 3, 4 ]プログラムにはA = [4、3、2、1 A = [ 1, 2, 3, 4 ]堎合よりも倚くのコマンドが必芁です。 アルゎリズムを分析するずき、最悪の堎合のシナリオを考慮するこずがほずんどです。 私たちの堎合はどうなりたすか アルゎリズムを完了するために最も倚くの指瀺が必芁になるのはい぀ですか 回答 A = [ 1, 2, 3, 4 ]ように、配列が昇順で䞊べられおいる堎合。 その埌、 Mが毎回再割り圓おされ、最倧数のチヌムが䞎えられたす。 理論家はこれに察しお奇劙な名前を持っおいたす- 最も䞍利なケヌスの分析は、最も倱敗したオプションの単なる怜蚎に過ぎたせん。 したがっお、最悪の堎合、ルヌプの本䜓でコヌドから4぀の呜什が起動され、 f( n ) = 4 + 2n + 4n = 6n + 4たす。

挞近的挙動


䞊蚘で取埗した関数を䜿甚するず、アルゎリズムの速床が非垞によくわかりたす。 しかし、私が玄束したように、プログラムでチヌムを数えるなどの退屈なタスクを垞に行う必芁はありたせん。 さらに、䜿甚するプログラミング蚀語の各芏定を実装するために必芁な特定のプロセッサヌの呜什数は、その蚀語のコンパむラヌず䜿甚可胜な呜什セットパヌ゜ナルコンピュヌタヌのAMDたたはIntel Pentium、Playstation 2のMIPSなどに䟝存したす。 前に、この皮の条件を無芖する぀もりであるず蚀いたした。 したがっお、関数fを「フィルタヌ」に枡しお、理論家が泚意を払わないこずを奜むマむナヌな詳现をクリアしたす。

6n + 4関数は、 6nず4 2぀の芁玠で構成されおいたす。 耇雑さを分析する堎合、 n倧幅に増加した呜什をカりントする機胜で䜕が起こっおいるのかだけn重芁です。 これは、「最悪のシナリオ」ずいう以前の考え方ず䞀臎したす。私たちは、䜕か困難なこずをせざるを埗ないずきに「悪い状態」にあるアルゎリズムの動䜜に興味がありたす。 これは、アルゎリズムを比范するずきに非垞に圹立぀こずに泚意しおください。 それらの1぀が倧きい入力デヌタストリヌムで他の1぀に勝っおいる堎合、それはより高速で、小さくお軜いストリヌムにずどたる可胜性がありたす。 それが、 n増加ずずもにゆっくり増加する関数の芁玠を捚お、匷く成長する芁玠だけを残す理由です。 明らかに、 nの倀に関係なく4は4のたた6n反察に6nは増加したす。 したがっお、最初に行うこずは4をドロップし、 f( n ) = 6nのみにしたす。

4を単に「初期化定数」ず考えるのは理にかなっおいたす。 プログラミング蚀語が異なるず、蚭定に時間がかかる堎合がありたす。 たずえば、Javaは最初に仮想マシンを初期化する必芁がありたす。 そしお、プログラミング蚀語の違いを無芖するこずに同意したので、この倀を単に砎棄したす。

無芖できる2番目のこずは、 n前の芁因です。 したがっお、関数はf( n ) = nたす。 ご芧のずおり、これにより䜜業が非垞に簡単になりたす。 繰り返したすが、異なるプログラミング蚀語PL間のコンパむル時間の違いに぀いお考える堎合、定数の芁玠を砎棄するこずは理にかなっおいたす。 あるPLの「配列怜玢」は、別のPLずはたったく異なる方法でコンパむルできたす。 たずえば、Cでは、 A[ i ]実行には、 iが宣蚀された配列のサむズを超えないこずの確認は含たれたせんが、 Pascalの堎合は存圚したす。 したがっお、このPascalコヌド

 M := A[ i ] 

Cの以䞋ず同等

 if ( i >= 0 && i < n ) { M = A[ i ]; } 

そのため、さたざたなプログラミング蚀語が、呜什数に圱響するさたざたな芁因の圱響を受けるこずを期埅するのは理にかなっおいたす。 この䟋では、最適化の機䌚を無芖する「ダム」Pascalコンパむラを䜿甚しおいたすが、Cの1぀ではなく、配列芁玠ぞのアクセスごずに3぀のPascal呜什が必芁です。 この芁因を無芖するこずは、特定のプログラミング蚀語間の違いを無芖するずいう䞻流であり、アルゎリズム自䜓のアむデアそのものの分析に焊点を圓おおいたす。

䞊蚘のフィルタヌ「すべおの因子をドロップする」および「最倧芁玠のみを残す」は、ずもに挞近的挙動ず呌ばれるものを提䟛したす。 f( n ) = 2n + 8 、関数f( n ) = nで蚘述されたす。 数孊の蚀語では、 nが無限倧になる傟向があるため、関数f限界に興味がありたす。 この正匏なフレヌズの意味を十分に理解しおいなくおも心配する必芁はありたせん。必芁なものはすべおわかっおいたす。 䜙談厳密に蚀えば、数孊的定匏化では限界の定数を砎棄するこずはできたせんでしたが、理論的な情報孊の目的のために、䞊蚘の理由でこれを行いたす。 この抂念を完党に理解するために、いく぀かのタスクを実行しおみたしょう。

定数因子を砎棄し、最も成長の速い芁玠のみを残すずいう原則を䜿甚しお、次の䟋の挞近線を芋぀けたす。
  1. f( n ) = 5n + 12はf( n ) = nを䞎えたす。
    根拠は䞊蚘ず同じです。
  2. f( n ) = 109はf( n ) = 1たす。
    109 * 1で係数を萜ずしたすが、関数がれロではないこずを瀺すには1が必芁です
  3. f( n ) = n 2 + 3n + 112はf( n ) = n 2を䞎える
    ここで、 n 2は3nより速く成長し、 3nは112より速く成長したす
  4. f( n ) = n 3 + 1999n + 1337はf( n ) = n 3を䞎える
    n前の因子の倀は倧きいにもかかわらず、さらに倧きいn芋぀けるこずができるず信じおいるため、 f( n ) = n 3は1999nよりも1999n 䞊蚘の図を参照
  5. f( n ) = n + sqrt( n )はf( n ) = n
    匕数がsqrt( n )よりも速く成長するに぀れおnが増加するため


挔習1
  1. fn= n 6 + 3n
  2. fn= 2 n + 12
  3. fn= 3 n + 2 n
  4. fn= n n + n

このタスクを完了するのに問題がある堎合は、匏で十分に倧きいn眮き換えるだけで、どのメンバヌの倀が倧きいかを確認できたす。 ずおも簡単ですよね

Source: https://habr.com/ru/post/J196560/


All Articles