計算の複雑さに関する知識のないプログラマーになることは問題ですか?


30

私は大学で運動を割り当てられました。私はそれを家に持ち帰り、それを解決するためのアルゴリズムをプログラムしようとしました。

それから、私が思いついた最も些細なことを作り、講師に見せました。簡単な観察の後、彼は私のソリューションの実行時の複雑さは実現不可能であり、より効率的なものを示していると感じました。そして、計算の複雑さについて何も知らないプログラマーの伝統があります(私はそれらの1つでした)。


3
司会者の通知:拡張討論や簡潔な回答の投稿にコメントを使用しないでください。チャットルームを使用してこの質問について話し合うことができます。以前のコメントはそこに移動されました。
ジル 'SO-悪であるのをやめる'

4
タイトルはプログラマーですが、質問は学生です。一般的に「プログラマー」とは「プロのプログラマー」を意味します。計算の複雑さを知らずにプロのプログラマーになることは問題なのでしょうか?それとも、プログラミングの学生がその知識を持っていなくても大丈夫ですか?2つは異なる質問です。たとえ同じ答えが得られたとしてもです。
corsiKa

回答:


42

はい、計算の複雑さについて何かを知ることは、真面目なプログラマーにとって必須です。巨大なデータセットを扱っていない限り、複雑さを知らなくても問題ありませんが、深刻な問題に取り組むプログラムを作成したい場合は必要です。

特定のケースでは、接続されたコンポーネントを見つける例は、最大個のノードのグラフで機能した可能性があります。ただし、100.000ノードのグラフを試した場合、講師のアルゴリズムはおそらく1秒でそれを管理できたでしょうが、アルゴリズムは(複雑さの程度に応じて)1時間、1日、または場合によっては1永遠にかかっていました。100100.000

学生がアルゴリズムコースで行うよくある間違いは、次のような配列を繰り返すことです。

while array not empty
    examine first element of array
    remove first element from array

これは最も美しいコードではないかもしれませんが、複雑なプログラムでは、プログラマが気付かないうちにこのようなものが表示される可能性があります。さて、このプログラムの問題点は何ですか?

100.00050.000

while array not empty
    examine last element of array
    remove last element from array

50.000

O(1)O(n)n1

12n2nn=100.000

これは単なるおもちゃの例ですが、すでに2つのプログラムの違いを伝えるために複雑さの基本的な理解が必要です。実際に、この間違いのあるより複雑なプログラムをデバッグ/最適化しようとする場合、見つけるにはさらに大きな理解が必要ですバグがどこにあるか。というのも、この方法で配列から要素を削除するような間違いは、コードの抽象化によって非常によく隠れるからです。

複雑さをよく理解することは、2つのアプローチを比較して問題を解決するときにも役立ちます。接続コンポーネントの問題を自分で解決するための2つの異なるアプローチを考え出したと仮定します。それらを決定するために、それらの複雑さを(迅速に)推定し、より良いものを選択できれば非常に便利です。


10
"So long as you are not dealing with huge data sets you will be fine not knowing complexity"これはしばしば真実ですが、常にそうではありません。たとえばO(n!)、比較的小さなデータセットであっても、アルゴリズムは実行できません。プログラムをO(n!)使用できるアルゴリズムを使用するとO(n^2)10のデータサイズで実行するのに36,288倍の時間がかかります。データサイズが20の場合、2.4五十億操作を検討しています。
レイラブ

1
@reirabの例を回答に含めるべきだと思います。それはより劇的であり、あなたのポイントをより決定的に証明します。そして、計算の複雑さを学ぶ前に、私は個人的にそのようなアルゴリズムに噛まれました。
思源レン

2
プレイにはもっと大きな問題があると思います。単にわからない場合は、これが不要なタスクを自己選択してください。だから、Xが終わることを知る必要があるほぼすべての質問を言うことができます、それは役に立つかもしれません。だから、その重要性がまだ知っているのが良いか、最終的にあなたに噛み付くかもしれないかどうかに関係なく。
joojaa

「2つのプログラムの違いを理解するには、複雑性理論に関する基本的な知識が必要です」-この特定の例では、そうではないと思います。プロファイルを作成し、「要素の削除」に常に時間がかかることを観察し、最後の要素を削除する方が最初の要素を削除するよりも速いことを(複雑さの理論を理解せずに)知って、変更を加えて、プログラムを高速化します。複雑さの理論を理解する利点は、プロファイリングせずにそのような問題を大まかに数量化できるため、「時期尚早」に最適化できることです。
スティーブジェソップ

..そして一般的に、複雑性理論を参照せずに、すべてまたはほとんどすべての実用的な例を1つずつ解決できると思います。この場合、大量のデータをコピーすることは、そうしないことよりも遅いことを知ることは、「複雑性理論」ではありません。しかし、もちろん、プログラミング(およびあらゆる職業)で、一般的に出てくる原則の優れたメンタルモデルを使用することは依然として有用です。そのような問題は、アドホックな手段によって一度に1つずつではなく、原則ごとに日常的に分析、議論、解決できるためです。
スティーブジェソップ

26

これはTom van der Zandenの答えの反論であり、これは必須であると述べています。

問題は、ほとんどの場合、50.000倍遅いことは関係ありません(もちろん、Googleで働いている場合を除きます)。

実行する操作にマイクロ秒かかる場合、またはNが特定のしきい値(最近行われたコーディングの大部分)を超えない場合、それは重要ではありません。そのような場合、計算の複雑さについて考えることは、時間(そしておそらくお金)を無駄にするだけです。

計算の複雑さは、何かが遅くなったり、スケールが悪くなったりする理由と、それを改善する方法を理解するためのツールですが、ほとんどの場合、完全にやり過ぎです。

私はもう5年以上プロのプログラマーであり、ループO(M * N)内でループするときに計算の複雑さを考える必要性は一度もありません。小さい。

プログラミングジョブを実行する人にとっては、はるかに重要で一般的に使用され、理解しにくいものがあります(パフォーマンス領域では、スレッド化とプロファイリングが良い例です)。

もちろん、計算の複雑さを理解しないとできないことはいくつかあります(たとえば、辞書でアナグラムを見つける)が、ほとんどの場合は必要ありません。


3
あなたのポイントを拡大するために、計算の複雑さを強調しすぎると、あなたが道に迷うことがあります。たとえば、小さな入力に対して「より良い」アルゴリズムが実際に遅い状況があります。プロファイラーは、究極の真実の源です。
ケビンクルムウィーデ

2
@Kevin Krumwiede、些細なデータセットのソートを最適化するのはやり過ぎだということには完全に同意します。しかし、少なくとも複雑さを理解することが依然として重要であることも示しています。理解は、他のより複雑なアルゴリズムとは対照的に、バブルソートが適切であるという判断を下すきっかけとなります。
ケントA.

4
データセットがすべての場合に小さいことがわかっている場合は、この種のことを回避できます。ただし、ループ内で呼び出されるものの過剰な複雑さに非常に注意する必要があります。少し前に、この方法で実行時間を1秒に短縮しました。また、O(n ^ 8)の問題に1回遭遇しました(データ検証)。多くの注意を払って12時間になりました。
ローレンペクテル

7
ループO(M * N)内でループするときに計算の複雑さを考慮する必要性を発見したことはありません。これは、常に操作が本当に速いか、MとNが非常に小さいためです。–皮肉なことに、あなたが与える議論は、計算の複雑さについて考えたことを示しています。あなたはそれがあなたがしていることに関連する問題ではなく、おそらく正当であると判断しましたが、あなたはまだこの問題の存在を認識しており、それが問題を引き起こす場合は、深刻な結果が起こる前にそれに反応することができますユーザーレベル。
Wrzlprmft

4
時期尚早の最適化はすべての悪の根源ですが、時期尚早な悲観化は少なくとも多くの悩まされるユーザーの根源です。繰り返しの関係を解く必要はないかもしれませんが、少なくともO(1)、O(N)、O(N ^ 2)の違いを伝えることができない場合は、特にループをネストしているので、誰かが後で混乱をクリーンアップする必要があります。ソース:後でクリーンアップする必要があった混乱。50.000の係数は非常に大きいので、後で入力が増えたとき、まだ余裕があるかどうかをよく知っている必要があります。
Jeroen Mostert

14

私はソフトウェアの開発を約30年間行っており、請負業者と従業員の両方として働いており、かなり成功しています。私の最初の言語はBASICでしたが、私はすぐに機械語を学び、力不足のボックスから適切な速度を引き出すようにしました。私は長年プロファイラーで多くの時間を費やし、高速でメモリ効率の高い最適化されたコードの生成について多くを学びました。

言うまでもなく、私は独学です。数年前にインタビューを開始するまで、O表記に出会うことはありませんでした。インタビュー中を除いて、私のプロの仕事では決して出てきません。そのため、インタビューでその質問を処理するためだけに基本を学ばなければなりませんでした。

楽譜を読むことができないジャズミュージシャンのように感じます。私はまだうまくプレイできます。私はハッシュテーブル(つまり、ハッシュテーブルはすでに発明されていることを知る前に発明しました)およびその他の重要なデータ構造について知っています。しかし、真実は、この職業で成功するためには、インディーズに行くか、インタビュー中に彼らが尋ねる質問に対する答えを学ぶ必要があるということだと思います。

ちなみに、私は最近、フロントエンドのWeb開発者の役割についてインタビューしました。彼らは、答えが計算の複雑さと対数の両方の知識を必要とする質問をしました。私は20年前の数学を多かれ少なかれ正確に答えるのに十分なほど覚えていましたが、少し耳障りでした。フロントエンドの開発で対数を使用する必要はありません。

頑張って!


2
だから、あなたの答えは「はい」ですか?
ラファエル

6
TL; DR:「はい」。しかし、私の経験では、あなたが採用された後、あなたはほとんどの仕事の計算の複雑さについて話すつもりはありません。はい、データ構造とそのパフォーマンスは知っていますが、アルゴリズムがO(n)であるか、またはプログラマーが上手くいかないものを知っているだけです。優れたコードを迅速に記述し、後でホットスポットを最適化することに集中する方がはるかに優れています。通常、ほとんどのコードでは、パフォーマンスよりも可読性と保守性が重要です。
スコットシェーファー

3
企業環境では複雑さが生じる可能性があると思いますが、企業にとって最初の本当の懸念は出荷です:それが機能する場合は、アプリを改善するための予算があるまで、または顧客が貧しいことについて不平を言うまで戻ってくるまで十分ですパフォーマンス。アドホックプロジェクトのb2b状況では、おそらく非常にまれです。b2c、または非常に競争の激しい市場(既製の製品)では、新規採用のエントリーバーを引き上げる直接的な効果により、おそらくより頻繁に登場するでしょう。
ディディエック

4
@didierc "十分に良い"は常に物事を壊すものでもあります。
ラファエル

1
@didierc 1)そうですね、CSの背景がしっかりしている人は、(願わくば)正しいものとそうでないものについて良い直感を持っていますが、アドホックな問題解決者は「単純な」間違いを犯すかもしれません。多重コンパイル後の実行が正確に指定されたものであることを保証することは非常に重要であり、未解決の問題になります。2)いいえ
ラファエル

9

質問は非常に主観的であるため、答えはそれが依存することだと思います

少量のデータを扱う場合は、それほど重要ではありません。これらの場合、通常、言語の標準ライブラリが提供するものであれば何でも使用できます。

ただし、大量のデータを扱う場合、または何らかの理由でプログラムが高速であると主張する場合は、計算の複雑さを理解する必要があります。そうでない場合、問題をどのように解決する必要があるか、またはそれをどれだけ早く解決することができるかをどのように知るのですか?しかし、理論だけを理解しても、本当に優れたプログラマーになるには十分ではありません。非常に高速なコードを生成するには、マシンの動作方法(キャッシュ、メモリレイアウト、命令セット)、コンパイラーの動作(コンパイラーは最善を尽くしますが、完璧ではありません)も理解する必要があります。

要するに、複雑さを理解することは明らかにあなたをより優れたプログラマーにすると思う。


1
あなたは一般的に正しい考えを持っていると思いますが、「主観的」はこの問題を適切に説明していません。「状況に応じて」はより良い言葉です。また、大量のデータを処理しない非常に遅いプログラムを作成することもできます。私は最近、多項式表現/ストレージに関するmath.seの質問に答えました。通常、これにはかなり少量のデータが含まれます。たとえば、〜1000項の多項式が一般的です。実装によっては、実際のパフォーマンスに大きな違いがあります(乗算の場合は数百秒または数千秒)。
フィズ

4

重要なアルゴリズムを開発している人がアルゴリズムの複雑さを理解していない場合、それは確かに問題です。アルゴリズムのユーザーは一般に、優れたパフォーマンス特性を持つ優れた実装品質に依存しています。複雑さはアルゴリズムのパフォーマンス特性の唯一の要因ではありませんが、重要な要因です。アルゴリズムの複雑さを理解していない人は、有用なパフォーマンス特性を持つアルゴリズムを開発する可能性が低くなります。

利用可能なアルゴリズムが良質であると仮定すると、アルゴリズムのユーザーにとってはそれほど問題ではありません。これは、重要で、明確に指定された標準ライブラリを持つ言語を使用する開発者に当てはまります-ニーズを満たすアルゴリズムを選択する方法を知る必要があるだけです。問題は、ライブラリ内で利用可能な何らかのタイプの複数のアルゴリズム(ソートなど)がある場合に発生します。これは、多くの場合、複雑さを選択するための基準の1つであるためです。複雑さを理解していない開発者は、手元のタスクに効果的なアルゴリズムを選択するための基礎を理解できません。

それから、(より良い説明のために)非アルゴリズム的な懸念に焦点を当てる開発者がいます。たとえば、直感的なユーザーインターフェイスの開発に焦点を当てることがあります。このような開発者は、アルゴリズムの複雑さを心配する必要はありませんが、やはり、高品質に開発されているライブラリまたは他のコードに依存する場合があります。


3

それは、作業しているデータの量ではなく、開発するプログラムの種類に依存します。

概念的な複雑さを知らないプログラマーを知らないプログラマーに電話しましょう。

noobishプログラマーができること:

  • ビッグデータデータベースを開発する-彼は内部でどのように機能するかを知る必要はありません。彼が知っている必要があるのは、データベースの開発に関するルールだけです。彼は次のようなことを知っています:何にインデックスを付けるべきか、...データに冗長性を持たせる方が良い場合、そうでない場合...
  • ゲームを作成する-彼はゲームエンジンがどのように機能するかを研究し、そのパラダイムに従うだけで、ゲームとコンピューターグラフィックスは非常に大きなデータの問題です。単一の画像/フレームに対して1920 * 1080 * 32bit = cca 7.9MBを考慮してください... 60 FPSで少なくとも475MB / sです。フルスクリーン画像の不必要なコピーを1つだけ使用すると、1秒あたり約500MBのメモリスループットが浪費されることを考慮してください。しかし、彼はそれを気にする必要はありません、なぜなら彼はエンジンだけを使うからです!

noobishプログラマーはすべきではありません:

  • 使用するデータのサイズに関係なく、非常に頻繁に使用される複雑なプログラムを開発します。たとえば、小さなデータは、コンパイル時間などより遅いため、開発中に不適切なソリューションの顕著な影響を引き起こしません。 1つの単純なプログラムの秒数は、初心者のプログラマーの観点からはそれほど多くありません。その負荷を維持するには10コアが必要です!
  • 組み込みデバイス用のプログラムを開発します。組み込みデバイスは小さなデータで動作しますが、冗長な操作により不要な電力消費が発生するため、可能な限り効率的である必要があります

だから、テクノロジーを使いたいだけなら、noobishプログラマーは大丈夫です。ですから、新しいソリューションやカスタムテクノロジーなどの開発に関しては、初心者のプログラマーを雇わない方が良いでしょう。

ただし、会社が新しい技術を開発しない場合は、既に作成されたものを使用します。熟練した才能のあるプログラマーを雇うのは、才能の無駄です。同じことが当てはまります。新しいテクノロジーに取り組みたくなく、既に作成されたフレームワークを使用して顧客のアイデアをデザインやプログラムに入れるのが良い場合は、あなたが必要としないものを学ぶのは時間の無駄です。それがあなたの趣味であり、あなたが論理的な挑戦が好きなら。


1
「無能なプログラマー」という用語を使用した他の回答のように、より中立なラベルを使用するか、まったくラベルを使用しない場合、この回答は改善される可能性があります。
Mobyディスク

1
「概念的な複雑さ」の意味がわかりません。私の経験では、ツリーやハッシュテーブルについて十分に知らない人は、大きなデータベース(の一部)のインデックスを作成する方法に関してインテリジェントな決定を下すことができません。
フィズ

3

私はここに答えを書くのを少しためらっていますが、他のいくつかのコメント(私のコメントの一部はチャットに移動しました)を細かく見つけたので、次のように表示します...

コンピューティングには多くのことに対する知識のレベル/程度があります(この用語では、コンピューターサイエンスと情報技術の大まかな統合を意味します)。計算の複雑さは確かに広大な分野であり(OptPとは何かを知っていますか?またはAbiteboul-Vianuの定理が言っていることを?)、また多くの深さを認めます:CS学位を持つほとんどの人は研究に入る専門家の証明を作成できません計算の複雑さの出版物。

n2

私は、計算の複雑さの概念をいつ適用するかを知る状況(およびそれらを安全に無視できる状況を知る状況)と、Cでパフォーマンスに敏感なコードを実装するやや一般的なプラクティス(Javaの世界以外)とパフォーマンスに影響を受けない状況を敢えて比較しますPythonのものなど(余談ですが、これはジュリアの話では「標準的な妥協」と呼ばれています。)パフォーマンスについて考える必要がないことを知ることは、プログラミング時間を節約します。

そしてもう1つのポイントは、計算の複雑さを知っていても、プログラムの最適化が自動的にうまくいくわけではないということです。キャッシュの局所性、[場合によっては]パイプライン処理、そして最近ではパラレル/マルチコアプログラミングなど、アーキテクチャ関連のものをもっと理解する必要があります。後者には、独自の複雑性理論と実用的な考慮事項の両方があります。2013年のSOSP論文の後者の趣味「すべてのロックスキームには15分間の名声があります。すべてのターゲットアーキテクチャまたはワークロードで、他のどのロックスキームよりも常に優れていると考えられる9つのロックスキームはありません。したがって、ロックアルゴリズムは、ハードウェアプラットフォームと予想されるワークロードに基づいて選択する必要があります。」


1
長い目で見れば、パフォーマンスに敏感なビットのプログラミング言語を変更するよりも、より良いアルゴリズムを開発したり見つけたりする方が通常は有益です。複雑さの理解不足と時期尚早の最適化との間には強い関連性があることに同意します-それらは通常、最適化のためにパフォーマンスの影響を受けにくいビットを対象としているためです。
ロブ

1
実際には、(偶然の)画家のシュレミエルのアルゴリズムは、O(n ^ 2)ソートよりもはるかに頻繁です。
ピーターモーテンセン

-1

big-Oがわからない場合は、学ぶ必要があります。難しくはなく、本当に便利です。検索と並べ替えから始めます。

私は、多くの回答とコメントがプロファイリングを推奨していることに気づきました。そして、それらはほとんど常にプロファイリングツールを使用することを意味します

問題は、プロファイリングツールが高速化に必要なものを見つけるのにどれだけ効果的であるかという点で、マップ全体に存在することです。 ここで、プロファイラーが被る誤解をリストして説明しました。

その結果、プログラムがアカデミックなエクササイズよりも大きい場合、最高の自動プロファイラーでさえも公開できない眠れる巨人を含めることができます。 この投稿は、パフォーマンスの問題がプロファイラーからどのように隠れるかを示すいくつかの例を示しています。

しかし、彼らはこのテクニックから隠れることはできません


「Big-Oh」は便利だと主張しますが、別のアプローチを推奨します。また、「Big-Oh」(数学)の学習が「検索とソートから始まる」(アルゴリズムの問​​題)ことがどのようにわかるのかわかりません。
ラファエル

@Raphael:別のアプローチを推奨しません-それは直交的です。Big-Oはアルゴリズムを理解するための基本的な知識ですが、おもちゃではないソフトウェアでパフォーマンスの問題を見つけることは、コードを書いて実行した後ではなく前に行うことです。(学者はこれを知らないことがあるので、彼らはgprofを教え続け、善よりも害を与えます。)そうすることで、問題がO(n * n)アルゴリズムの使用であることに気付くかもしれません。それを認識できます。(およびbig-Oは、数学的に定義されたアルゴリズムのプロパティであり、別の主題ではありません。)
マイクダンラベイ

「そして、big-Oは、数学的に定義されたアルゴリズムの特性であり、別の主題ではありません。」-それは間違っており、危険なことです。「Big-Oh」は関数のクラスを定義します。それ自体、アルゴリズムとはまったく関係ありません。
ラファエル

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.