たとえば、次のコードがあるとします。
for(int i=1;i<n;i++)
for(int j=1; j < sqrt(i); j++)
foo(); //foo takes constant time
この種のプログラムの計算の複雑さ(「ビッグO」)を計算する方法を誰かに説明できますか?
これは基本的に数学の質問です。「コンピュータサイエンス」のスタック交換またはSOの方が適している場合があります。しかし、プログラマーもそれを知っていることを期待できると思います。プログラマーは「ソフト」な質問だけに対応しているわけではないので、おそらくここに配置されます。
いずれかの方法。
これまでの回答に基づくと、フィリップケンドールはあなたの質問を誤解していると思います。彼はあなたが「この合計を評価する計算の複雑さは何ですか」と尋ねていると思っています。あなたが求めています。 「この合計の成長率はどれくらいですか」。
シリーズの成長率を推定するには、いくつかの手法があります。非常に一般的なものは積分法であり、基本的にそれを微積分問題に変えます。 f(n) = n
やf(n) = n^{0.5}
などの分析関数に従って値をとる系列をとることは、「ステップ」関数の積分をとることと同じであるという考え方です。 (連続)範囲は_1
_からn
です。ステップ関数は上下からsmooth関数によって制限されており、smooth関数は積分が簡単です(または少なくともwhen積分は簡単です)。これは明確な答えを与えます。
たとえば、_x dx
_の不定積分は_1/2 x^2
_であるため、合計_1 + 2 + ... + n
_はO(n^2)
です。
あなたの場合、あなたはsqrt(n)
までの合計を気にします。 sqrt(x) dx
の不定積分は_2/3 x^{3/2}
_です。したがって、合計1 + sqrt(2) + ... + sqrt(n)
はO(n^{3/2})
です。
しかし、多くの人は計算を忘れていました。多くの場合、これらのことを推定するための単純なヒューリスティックを使用することで回避できます。
たとえば、合計1 + sqrt(2) + ... + sqrt(n)
では、sqrt(n)
が合計の最大数であることがわかります。そして、合計にはn
の数があります。とても簡単に、合計がn * sqrt(n)
を超えないことがわかります。
さて、あなたは合計がはるかに少なくなることができないのを見たいと思います。最初のエントリが小さいため、それを確認するのはより困難です。ただし、typicalエントリの値は何ですか。後半の数字をすべて考えてみましょう。それらの最後はsqrt(n)
で、真ん中のエントリはsqrt(n/2)
です。ただし、sqrt(n/2)
はsqrt(n) / sqrt(2)
に書き換えることもできます。言い換えると、シーケンスの後半のすべてのエントリは、Big Oに関する限り、「同じ」である限り、相互の因数sqrt(2)
内にあります。
具体的には、合計の後半の数値はすべてsqrt(n) / sqrt(2)
です。そして、それらの数の_n/2
_があります。したがって、合計は少なくともn * sqrt(n) / (2 sqrt(2))
であり、その関数もO(n^{3/2})
です。
したがって、計算なしで、成長率は_n^{3/2}
_の定数係数内にあることがわかります。
このアプローチは、優れたエンジニアリング思考を促進するため、この方法で気に入っています。問題を直接解決するのが難しい場合は、典型的なケースに焦点を当てるとよいでしょう。通常、正常に機能するものを作成すると、実際にはそれで十分です。科学においても同様です。場合によっては、一部の式の正確な定数が重要であり、微積分などの強力なツールを使用してそれを見つける必要があります。しかし、より一般的には、人々は全体像だけを気にし、微積分は閉形式の解と正確な定数に焦点を当てることでそれを曖昧にするかもしれません。多くの場合、典型的なケースを理解できれば、全体的な問題を解決するのに十分な情報が得られます。
複雑さは、明らかに、sqrt(i)の合計です(i = 1〜n)。そのような合計は、通常、閉じた形式で計算するのが非常に困難です。概算として、合計は計算しませんが、幅nの間隔でのsqrt(i)の積分(nの値を合計していたため)。別のものを選択する正当な理由がない場合は、 0.5からn + 0.5までのxに対するsqrt(x)の積分。
今、あなたはあなたの数学を覚えておく必要があります。 x ^ kの積分はx ^(k + 1)/(k + 1)であり、sqrt(x)= x ^(1/2)の積分はx ^(3/2)/(1.5)、したがって、0.5からn + 0.5までの積分は約(n + 0.5)^(3/2)/(1.5)であり、O(n ^ 3/2)になります。
(これは実際には証明ではありませんが、経験を積めば、証明しなければならない場合に証明できることを知っています。また、O(n)だけでなく、foo()が呼び出される頻度についても適切な見積もりを提供します。 ^ 1.5/1.5回)。