ei1333の日記

ぺこい

誰でもできる簡単デバッグ

デバッグ、大変ですよね

なぜか答えが合わない、なぜか Segmentation fault する、なぜか実行時間がかかる など色々あると思います

そんなデバッグを簡単に行う方法を考えました

デバッグの定義

Wikipedia によると、デバッグ とは 次のことを指します。

デバッグ(debug)とは、コンピュータプログラムや電気機器中のバグ・欠陥を発見および修正し、動作を仕様通りのものとするための作業である。

簡単なデバッグの方法

以下にいくつか挙げるので、好きなものを選んでください。

  1. 見なかったふりをする : 黙っていればバレへんか・・・

  2. 仕様を変更する : プログラムの仕様をバグったプログラムの挙動に合わせて変更すれば、デバッグは終了です。

  3. プログラムを削除する : 非自明ですが、プログラムを削除すればバグは消えます。コマンドから rm ファイル名 をする、ゴミ箱にファイルを捨てる、ディスクを破壊する など選択肢は色々あると思います。

  4. 他人に任せる : どこにバグがあるか分からないプログラムのデバッグは苦痛なので他の人に任せるのも手です。 これは自分も実践しています。GitHub - ei1333/library: CompetitiveProgramming C++ Library には無数のバグが含まれることが知られています(知られていないでくれ)。 バグの報告やプルリクを待つなどすると自然にデバッグされていきます。

ありがちなデバッグの方法

  1. デバッガを使う : C++ であれば gdb などデバッガがあります。配列を含む変数の値をステップごとに表示したり、どの分岐に入っているかなどを確認できます。統合開発環境と組み合わせて使うと使いやすいです。誰でもは出来ない気がするな

  2. printf / cout デバッグ : 気軽なデバッグの方法です。 Segmentation fault でありがちなんですが、異常終了すると出力がバッファに溜まったまま終わることがあるので、 printf であれば出力したあとに fflush(stdout)、cout であれば std::endlstd::flush(std::cout) を呼び出すべきです。

  3. define int long long:? ところでmarkdown記法を使っているのでシャープをつけるとバグりました C++でもsigned mainにしないとバグります

  4. 分岐で例外を吐くようにして提出する:場合分けのある問題の場合、それぞれの場合分けで RE を吐くようなコードを提出します 。WA が残っていればその場合分け以外、RE のみになればその場合分けでバグっていることが特定可能です。ペナルティは知りません。TLEやMLEを吐くようなコードも用意しておくと少しマシになります。僕は用意していませんが 

  5. 小さいケースは愚直解 : 大きいテストケースはガバガバな問題が稀にあって、この場合は小さいケースだけ愚直解の出力をすると良いです。小さい場合がコーナーケースになっている場合もあります。 だんだんデバッグというよりWAが出た時の対処法になってきたな もうやめます

  6. return 0 二分探索:Segmentation fault が起きたときに return 0 を入れる位置を二分探索すると  {n}ソースコードの行数として  {O(\log n)} で原因を特定可能です。これ実際 2分くらいの探索じゃないか すげえ

  7. using modint=double : modintの分子×分母の逆元の値はデバッグに不向きなので、デバッグ時にはmodint=doubleにするなどの工夫をするとよいです。

  8. 愚直解と比較する:愚直解を書くのは非常に不快な気持ちになりがちですが、結果的にそれをしたほうが早かったなどの場面が結構あります

  9. ビジュアライザを見る:https://csacademy.com/app/graph_editor/https://csacademy.com/app/geometry_widget/ が個人的に便利です。他にも調べれば色々あると思います。

  10. 添字を±1する:添字はずれがちなので慎重になることが重要です。適当にサンプルが合うように微調整する方法もありますが賛否あります。

  11. お風呂に入る:気分転換に別のことをするとふとバグに気づくこともあります。解法がわからない場合も同様です。

  12. 行列を静的配列にする:vector<vector>は遅いので、array<array<T,N>>にする or 静的配列にすると3億倍の定数倍改善になります。

  13. ループの外で配列を宣言する:ループの中で配列を無限界確保すると遅いです。ループの外で1回だけ配列を宣言して、それを初期化 or clear() して使うようにしましょう。

  14. まともな記事を読む:最初の4つくらいを見ると分かる通りこの記事はゴミなので、真面目にデバッグについて知りたい人はまともな記事を読むことを推奨します。