はじめに
for文のカウンタ変数にvarを使って実行時間がTLEになった理由がvarが原因ではないとコメント頂きました。
初心者のハマりどころとして追記しました。(9/6 19:30)
AtCoderで問題を解いていて、for文のカウンタ変数でvarと型で実行時間にかなり差が出て驚いたため記事にしました。
MSでは、for文のカウンタ変数にvarを使うことを認めていますし、私も業務上では使います。var - C# Reference | Microsoft Docs
ただ、実行時間の制限がある競プロにおいてfor文のカウンタ変数にvarを使うと良くないかもしれません。(競プロ初心者なので、間違っておりましたらご指摘ください)
TLEになってしまう問題
「AtCoder Begginner Contest 057 C-Digits in Multiplication」を解いていた時のことです。
提出するとTLEになってしまいましたが、他のACになっているコードを見ても解法には違いがありません。
ところが、よく目を凝らしてみると、for文にvarを使っている箇所に違いがあったため、for文のカウンタ変数の型をlongに変更してみました。
すると、ACで通ったのです。
実際に提出したソースコードで比べてみました。
ソースコードは、for文のカウンタ変数の型を変えただけです。
言語は、「C# (.NET Core 3.1.201)」です。
| 実行時間 (ms) | ソースコード | |
|---|---|---|
| varの場合 | 2206 | 提出 #16188260 - AtCoder Beginner Contest 057 |
| longの場合 | 97 | 提出 #16518402 - AtCoder Beginner Contest 057 |
for文の変数の型をlongだけでこんなに実行時間に差が出るとは……。
原因
varは、型推論ですのでlongにしたい場合はサフィックスをつけて型を明示しなければいけませんでした。
ソースコードを見て頂ければ分かるのですが、
varの場合
for(vari=1;i*i<=N;i++)と記載していました。
MSのドキュメントでは、型の決定を以下のように記載しています。
整数リテラルの型は、そのサフィックスによって次のように決まります。
サフィックスがないリテラルの型は、int、uint、long、ulong の型のうちその値を表すことができる最初のものになります。
整数数値型 - C# リファレンス | Microsoft Docs
varの場合だとiの初期値は1と小さな値ですので、intとして型が決まります。
Nについての制約が以下になっていますので、
1 ≦ N ≦ 10^{10}
intの最大値、2,147,483,647を超えてしまい、オーバーフローしてしまいました。
オーバーフローをした状態でインクリメントすると、マイナスになります。
試しに以下のソースコードを実行するとiは、-2147483648になります。
正の最大値にインクリメントすると負の最大値になってしまいます。
inti=int.MaxValue;i++;オーバーフローしてしまったせいで、forで無限ループが発生してしまったためTLEになってしまいました。
よって、ソースコードのfor文を以下に直すと無事ACになりました。
for(vari=1L;i*i<=N;i++)しかもvar + サフィックスLの方が若干早い。
| 実行時間 (ms) | ソースコード | |
|---|---|---|
| var + サフィックスLの場合 | 93 | 提出 #16539970 - AtCoder Beginner Contest 057 |
| varの場合 | 2206 | 提出 #16188260 - AtCoder Beginner Contest 057 |
| longの場合 | 97 | 提出 #16518402 - AtCoder Beginner Contest 057 |
まとめ
競プロでは、for文のカウンタ変数にvarを使うときは型を意識しよう。
競プロなど時間制限がある場合、慌てているとサフィックスやキャストを忘れる可能性もあるためきちんと型を書いてもいいのかなと思いました。競プロでは、for文のカウンタ変数にvarを使わずきちんと型を書いた方が良さそう。