【競プロ】intとlong longについて【C/C++】
先日のABCでWAしました。
理由はlong long
型を使っていなかったからです。
定期的にこのミスをやらかすのですが、なんで常にlong long
型を使うんじゃなくて、「基本的にはint
で、必要があればlong long
」というスタンスをとっている人が多いんだろう…。と疑問に思いました。本記事でこの問題に決着をつけたいと思います。
僕はAtCoder以外の競プロコンテストにほとんど参加していないので、その立場から書きます。
基本的には「美学」?
この件について検索すると、次のツイートがヒットしました。
C++で競プロする人、intとlong longを使い分ける人が多い印象なんだけれど、全部long longにしないのって何でなんです?(´・_・`)
— 平岡 拓也☀‹‹\(´・_・` )/›› (@HiraokaTakuya) 2019年7月7日
実質宗教的な問題です(ごく稀にメモリ制限が効いてきますがAtCoderならまずないです)
— Joe (@xuzijian629) 2019年7月7日
制約から値のとる範囲を想定できるので、intで十分ならintを使う……美学とかの問題で深い意味は無いと思います。
— show (@shows1011) 2019年7月7日
chokudaiさんもいつかの放送でintで十分な領域を扱う変数にlongを割り当てるのは美しくないと言われていたような……
この情報を認めればlong long
を必要以上に使わないのは「気持ちの問題」ということになります。
確かに私もint
で済む問題でlong long
を使うことになんとなく抵抗感を覚えます。
メリットを整理
少し両者の特性を整理してみましょう
int
を使うメリット
long long
を使うメリット
- 大きな数字でも格納することができるようになる
- オーバーフローエラーが回避できる
- 具体的にはです(かなり大きいですね…!)
- int型はです(結構あっという間に到達してしまいます)
概ねこのように考えて良いのではないでしょうか。
ちなみに、これを書くにあたって結構Google検索しましたが、
「long long
を使うと速度的に間に合わなくなっちゃう問題があるよ!」と主張している記事は見当たらず、
せいぜい「定数倍改善にはなるね~」という主張くらいでした。
「お気持ち」の整理
これは個人的なお気持ちなので、人によっては他の理由でlong long
を嫌っているかもしれません。
- なんとなく必要以上にメモリを食ってる感じが嫌だ
- たとえ正誤に関わらないくらい微々たる差でも高速化したい
- C問題以上ならまだしもA問題やB問題で
long long
を使うのは気に食わない - 宣言時の見た目がなんか嫌だ
using ll = long long
している人が多いと思いますが、皆さんはどうでしょうか?- ちなみに私は
using lint = int
としています
- なぜか「十分な精度で計算している」ということに対する気持ちよさ、快さは感じないことの方が多い
- 気持ちよく感じることもある
- long long型の変数でvector配列のサイズ数を指定するのは怖い
- long longを使う問題ではfor文の
i
の型に戸惑う- その他、intでも十分な他の変数の型をどうしようか迷う
気持ちの問題なら…
C++11からstd::int32_t
、std::int64_t
という型が定義されました。これはそれぞれ32bit/64bitの幅を保証した符号付き整数型です。
実はint
やlong long
が実際何bitかは処理系依存なのでこういうものが定義されています。
これを使えば「気持ちの面」が解消される人ももしかしたらいるかも…? (自分が挙げた「速度」とか「必要以上のメモリ」の解決には何も役立ちませんが…)
# include <bits/stdc++.h> using namesapce std; // using int64 = int64_t するのもいいかも? int main() { int64_t a, b; cin >> a >> b; int64_t ans; ans = solve(a, b); cout << ans << endl; return 0; }
…どうでしょうかね
悪魔の#define int long long
int
をlong long
に置き換えてしまう非常に強力なマクロです。long long
やll
などという見た目が嫌な人はこういう方法を取るのもありだと思います。
ただし、C++的には予約語をdefineするのは未定義動作なので注意してください。GCCでは認められています。
# include <bits/stdc++.h> # define int long long using namespace std; // int main()と書くとエラー signed main() { int a, b; cin >> a >> b; int ans; ans = solve(a, b); cout << ans << endl; reutnr 0; }
類似ケース
似たようなケースとしてv[0]
とv.at(0)
が挙げられると思っています。
少なくともAtCoderにおいてはどちらでも速度的に問題になることはほぼないのでat
を使うべきなはずです。
しかし、見た目の問題や微々たる速度面、そして「お気持ち」で[]
を選択している人も多いと思います。
ちなみに僕はこの手のミスで困った記憶がないのと、どうしてもv[0]
という見た目から外れるのが嫌で[]
を使っています。
総括
美学と実用…折り合いが難しいですが、僕の結論は以下の通りです。
- A問題B問題でも64bit整数型を使う
- しばらく使っても気持ち悪さをずっと感じるようだったら32bit整数型に戻す
- C問題以上では64bit整数型を常に使う
using int32 = int32_t
とusing int64 = int64_t
を行い、この2つを使う- ついでに
using float32 = float
とusing float64 = double
とusing float128 = long double
を行い、これを使う - いろいろ書いたけど本当は実力をつけて必要なサイズの見積もりをきちんと行い適切な型を選択するのが一番良い、ということは頭に入れておく
明らかにint型で間に合う問題(特にグラフ系)でlong long型を使うのはかなり心苦しいんですけどね……。 ひとまず僕の中のint-long long戦争は一旦ケリをつけることにします。