私は理論的なコンピューター科学者です。私は次のことを聞いた:
実務家は、デバッグが難しいことで悪名高いランダム化アルゴリズムを好まない。そのため、決定論的アルゴリズムの方がはるかに好ましいです。
この声明はどのくらい本当ですか?あなたが持っている場合は素晴らしいだろう例や反例を提供することができます。
大きな問題の1つは再現性です。
コードがおおまかに機能するとします。ほとんどの場合、正しい結果が得られます。しかし時々それはそれがうまくいかない状況に入る。確定的アルゴリズムの場合、誤った結果をもたらす入力を見つけたら、同じ入力を使用して、デバッガーを使用して、どこが間違っているかを見つけるまですべての手順を実行できます。
確率的アルゴリズムを使用すると、常に誤った結果を与える入力がない状況に遭遇する可能性がありますが、入力はときどきが誤った結果をもたらす可能性があります。そのため、問題のある入力を提供することはできません。すべての手順willを実行すると、問題のある手順で終了することがわかっている場合は、入力を提供できません。
少なくとも、アルゴリズムに確定性を与えるために、入力と乱数ジェネレーターの状態の両方をコードに提供できる必要があります。もちろん、通常の操作では、アルゴリズムは可能な限りランダムにする必要がありますが、再現性が必要です。
私はこの答えについて少し異なる方向に進みました-デバッグからではなく、テストの観点から:
通常、純粋な関数の設定は非常に簡単です。
同じ入力、同じ結果出力
これにより、適切なテストケースを非常に簡単に定義できます。標準のテスト値のセットといくつかの特別な関心事項(関数の機能に応じて異なります)を選択し、そのテストメソッドを記述して、関数が返す結果と期待した結果を比較します。戻るために。これにより、変更を加えるたびに実行できる確実なテストメソッドのセットが提供されます。
メソッドにランダム性を持たせ始めると、この問題が発生します。
同じ入力が入力され、多くの可能な値が出力されます。
次に、ランダム化された関数が特定の入力に対して返す可能性のあるすべての値を把握し、それらに対してテストする必要があります。そして、ランダム化された関数が何をするかに応じて、それは明らかに実行不可能(手動またはプログラムで追加する必要のある多くのオプションのため)、または完全に不可能です。
突然、堅実なテストスイートを作成するための労力が劇的に増加しました。その時点で、通常はすぐにスキップされます。
これをどのように回避しますか?
アルゴリズムの作成中にこれを回避するには、ランダム性をカプセル化します。ランダムな部分を実際に引き起こすスニペットを、アルゴリズムの外にある独自のオブジェクト内に配置しようとします。これで、テストスイート中に実際にそのオブジェクトをmockできます-基本的に、「ランダム化された」オブジェクトが返す値を修正します。これは理想的ではなく、すべてのシナリオをカバーしているわけではありませんが、テストはほとんどありません。 「乱雑さ」がなくなったので、テストスイートを作成するための労力はほんのわずかに増えました。テスト目的で、それを通常の純粋な関数のように扱うことができます。
関数を手動でデバッグする場合も同様の原則が適用されます。ほとんどの場合、アルゴリズムが生成したランダムな値が実際にわからないため、関数を壊した関数で発生したことを正確に再現することは困難です。そのため、手動デバッグは非常に困難になります。
私が知っている最悪の例で答えます:カーネルコードの不適切なロックによる競合状態。
競合状態は非決定性の概念そのものです。競合が発生するかどうかを制御することはできません。あなたはわざとランダムな入力を入れているのではなく、マシン自体がランダムなジェネレータとして機能します。
そのため、このような競合状態にある場合、確実に再現することはできません。コードを試すたびに、結果は異なります。さらに悪いことに、ほとんどの場合コードは正常に機能しますが、100万分の1のケースではカーネルパニックまたは破損したデータがトリガーされます。
たまにしか観察できない状態に関する情報をどのように収集しますか?あなたは絶対にロギングに頼らなければなりません、デバッガーはあなたに何の役にも立ちません。そのため、ユーザーの認識は、コードに導入したログ出力に限定されます。さらに悪いことに、ログを書き込むというまさにその行為は、競合状態をトリガーするのをほとんど不可能にするかもしれません、エラーはハイゼンバグに変わるかもしれません。
条件は非決定的であるため、コードを実行するたびにログ出力は異なる値を読み取ります。つまり、1回はカーネルパニックが発生し、2回目は破損したデータが取得され、3回目は他の場所で破損したデータが取得される、というようになります。あなたのバグは移動するターゲットではなく、どこかに現れるジャンプするターゲットであり、調査すると、次にそれ自体が表示されたときに背中の真後ろにポップアップします。
そのような条件のデバッグはvery不満になる可能性があります。
一方、確定的なバグがある場合は、常にその発生を段階的にバックトレースできます。結果が表示され、どの入力が誤った結果の原因であるかについての質問が表示されます。それらの入力を調べてもう一度実行し、適切な入力が誤った出力につながる場所を特定するまで、間違った入力を再帰的に調べます。
要するに:
非決定論的なコードのデバッグは、それ自体が終了することが保証されていない非決定論的なアルゴリズム自体です。
確定的コードのデバッグは、確定的アルゴリズムです。