コーナーケース
概要
プログラムの内容によっては、特殊な入力のときだけ特殊な処理が必要になる場合がある。
また、問題としては特殊ではなくても、コードの都合で特殊対応が必要になる場合もある。
このような入力を、コーナーケースという。
コーナーケースを見落とすと、ほとんどのケースでは AC できるのに、$1$ ケースだけ WA になることがある。
コーナーケースの典型例
個数や回数などに 0 が絡む場合
0 回操作する、0 個選ぶ、条件を満たすものが 0 個である、$1$ 回ごとの増加量が 0 である、など。
0 が絡む場合、ゼロ除算を踏みやすい。
また、ループが $1$ 周も回らず、$1$ 周目にやることになっているはずの処理がスキップされることもある。
例えば、平均値を求める処理で個数が 0 個になると、個数で割る部分でゼロ除算が起きる。
また、ループの中で答えを初期化している場合、ループが $1$ 回も回らないと想定外の値が残ることがある。
データサイズが 0 である場合
入力全体のサイズが 0 であることは、競技プログラミングではほぼない。
しかし、「条件を満たすものを全て探す」などの処理では、中間データのサイズが 0 になることがある。
データが空だと、.front() や .back() によるアクセスが実行時エラーや未定義動作の原因になる。
また、ループが $1$ 周も回らず、$1$ 周目にやることになっているはずの処理がスキップされることもある。
データサイズが 1 である場合
データサイズが 1 の場合、サイズが $2$ 以上であることを前提にした処理が壊れることがある。
例えば、隣り合う $2$ 要素を見る、$2$ 番目に大きい値を見る、木の辺を見る、などの処理。
これらは、データが $1$ 個しかない場合にはそのまま使えない。
該当する値にアクセスしようとして実行時エラーになることがある。
また、ループが $1$ 周も回らず、$1$ 周目にやることになっているはずの処理がスキップされることもある。
異なるはずのものが一致する場合
考察中では別々のものとして扱っていても、制約上は同じ値・同じ位置になることがある。
例えば、以下のような場合がある。
- 区間の左端と右端が同じ
- 始点と終点が同じ
- 最大値と最小値が同じ
- 選んだ $2$ つの値が同じ
何が起こるかはコード次第だが、よく確認しないと意図しない動作になることがある。
探索時、目的データが端にある場合
探す値が先頭や末尾にある場合、あるいは答えが最小値や最大値になる場合。
境界の些細な書き間違いで論理エラーになることがある。
また、条件を満たすものが存在しない場合も確認する。
自力で書く二分探索や三分探索では、初期値や終了条件を間違えたときにおかしな挙動をしやすい。
端にあるデータ、端の外側になるデータ、存在しないデータを試すとよい。
境界値や制約の端で何か起こる場合
制約の最小値・最大値は、コーナーケースになりやすい。
最小値は実装の前提を壊しやすく、最大値は計算量やオーバーフローの確認に役立つ。
例えば、$N=1$、$K=0$、$A_i=0$、$A_i=10^9$ のような値は、意識して試すとよい。
使い方の応用
提出前に確認する
提出前には、サンプルテストだけでなく、自分でコーナーケースを作って確認する。
特に、以下のような入力は確認候補になる。
- 制約の最小値
- 制約の最大値
- データが空に近い場合
- データが $1$ 個だけの場合
- 条件を満たすものがない場合
- 答えが先頭や末尾にある場合
- 全て同じ値の場合
- 本来別々だと思っていた値が一致する場合
全部を毎回試す必要はない。
しかし、自分のコードがどの条件を前提にしているかを考え、その前提が壊れそうな入力を優先して試す。
提出後の結果から疑う
ほとんど AC しているのに、$1$ ケースや $2$ ケースだけ WA になる場合は、コーナーケースを疑う。
逆に、大量に WA している場合は、コーナーケース以前のミスである可能性が高い。
まずはサンプルや小さい自作テストで、基本的な動作を確認する。
テストと組み合わせる
コーナーケースは、見つけただけでは意味がない。
実際にその入力を作り、正しい出力を手計算して、プログラムの出力と比較する必要がある。
コーナーケースを考えることと、テストを作ることはセットで行う。
注意点
制約外入力は基本的に考えなくてよい
競技プログラミングでは、問題文の制約を満たす入力だけが与えられる。
そのため、制約外入力に対するエラー処理は、基本的には書かなくてよい。
ただし、制約の下限や上限そのものは、コーナーケースになりやすい。
制約内の端の値は確認する。
問題文ではなくコードの都合で発生することもある
問題文上は特殊に見えない入力でも、自分のコードの書き方によってコーナーケースになることがある。
例えば、最初の要素を特別扱いしている、ループの中で初期値を決めている、などの場合は注意が必要。
自分のコードがどの前提に依存しているかを確認する。
サンプルには出ないことが多い
コーナーケースは、サンプルに含まれていないことが多い。
サンプルが通っただけで安心せず、自分で気になる入力を作って試すこと。
関連知識
テスト
コーナーケースは、テストとして実際に入力を作って確認する。
正しい出力を先に考えてから、プログラムの出力と比較する。
エラー
コーナーケースを見落とすと、WA だけでなく、実行時エラーや TLE になることもある。
エラーの種類を切り分けて原因を探す。
if分岐
特殊な入力だけ別処理が必要な場合、if文で場合分けすることが多い。
不等号や条件式を間違えると、境界値で誤答しやすい。
vector
空の vector や、長さ $1$ の vector はコーナーケースになりやすい。
.front()、.back()、隣り合う要素へのアクセスには注意する。
二分探索
探す値が端にある場合や、条件を満たす値が存在しない場合に、初期値や終了条件のミスが出やすい。