実装でよくつまづいているポイントをメモ。
二乗すると以上になるような最小の自然数
は(isqrt n)
で求まる。には対応する関数がないが、やはりisqrt
が使える:
(+ 1 (isqrt (- n 1)))
ただしの時に壊れているので注意。(SBCL x86-64では、速度が必要な場合にはsqrt
を使ったほうがよい。)
であるような最小の整数
が与えられたとき、であるような最小のはと書ける。計算の際は
(integer-length (- n 1))
とするのがよい。同様に、であるような最大の、つまりは
(- (integer-length n) 1)
で得られる。
ちなみに、以上であるような最小のの累乗が欲しい場合は、(ash 1 (integer-length (- n 1)))
だが、これはsb-int:power-of-two-ceiling
としてSBCLにあったりする。
で割るとあまる整数で以上/以下のもの
で割るとあまる整数で以上の最小のものは。あるいは、位相の差を足すと考えてのほうがわかりやすい?
で割るとあまる整数で以下の最大のものは。同様に、でもよい。
以下のの正の倍数の総数
以下のの正の倍数の総数はである。
以下の、を法としてと合同な正の整数の数は
がいちばんシンプル? (ただし、はあまりを求める演算とする。)非負整数を数える場合はをしなければよい。
以下のの正の倍数の総和
以下のの正の倍数の総和は
である。ただ、等差数列の和が両端の和に長さ/2を掛けたものであることがわかっていれば、確認しなくても問題なさそう。
自然数をできるだけ等分する
個のものを人に配る際にできるだけ等分になるようにすると、個を人に、個を人に配ることになる。ちょうど割り切れる時は大きいほうがが個、小さいほうが個になっていることに気を付ける。
が一定であるような区間
が与えられたとき、であるようなの区間は
であり、であるようなの区間は
である。
TZCNT
tzcnt (trailing zero count) はLSBからの連続する0の数を数える命令である(例: )。これを実現する効率の良い書き方は
(- (integer-length (logand x (- x))) 1)
だろう。SBCLならinteger-length
はbsr命令に落ちる。 x
が0の時に-1が返ることに注意。
integer-length
の代わりにlogcount
を使って
(logcount (- (logand x (- x)) 1))
と書いても良い。こちらはが0の時に0が返るという違いがある。logcount
はSBCL (バージョン1.2.8以降)ではpopcnt命令になり、だいたい上と同程度の効率になる。
2の累乗かどうか判定する
(= 1 (logcount x))
や(zerop (logand x (- x 1)))
など。後者はでも真になることに注意。
実数より小さい/大きいもっとも近い整数
実数より小さいもっとも大きな整数は。ならでもよい。
実数より大きいもっとも小さな整数は。ならでもよい。
以上/以下の偶数/奇数
- 偶数の場合だけ1足す。つまり、以上の奇数にする:
(logior 1 x)
- 奇数の場合だけ1足す。つまり、以上の偶数にする:
(logand -2 (+ x 1))
- 偶数の場合だけ1引く。つまり、以下の奇数にする:
(logior 1 (- x 1))
- 奇数の場合だけ1引く。つまり、以下の偶数にする:
(logand -2 x)
区間の重なり
2つの半開区間が重なっている必要十分条件はかつであること。 2つの閉区間が重なっている必要十分条件はかつであること。
区間の入れ替え
隣り合った区間を長さを保つように入れ替えると、新しい切れ目の位置はになる。
lower_bound
とupper_bound
upper_bound
とlower_bound
を使った添え字の処理のまとめ
列が昇順にソートされている場合
- 以上でもっとも左にある数の位置:
lower_bound(x)
- 以下でもっとも右にある数の位置:
upper_bound(x)-1
- より大きいもっとも左にある数の位置::
upper_bound(x)
- より小さいもっとも右にある数の位置:
lower_bound(x)-1
- 以上の数の総数:
N - lower_bound(x)
- 以下の数の総数:
upper_bound(x)
- より大きい数の総数:
N - upper_bound(x)
- より小さい数の総数:
lower_bound(x)
- ちょうどに等しい数の総数:
upper_bound(x) - lower_bound(x)
- に等しい数の存在する区間:
[lower_bound(x), upper_bound(x))
整数列に対して有理数(割り算の結果)で評価する場合、floor
やceil
で考えたいかもしれない。その場合の書き方:
- 以上でもっとも左にある数の位置:
lower_bound(ceil(x))
- 以下でもっとも右にある数の位置:
upper_bound(floor(x))-1
- より大きいもっとも左にある数の位置::
upper_bound(floor(x))
- より小さいもっとも右にある位置の数:
lower_bound(ceil(x))-1
- 以上の数の総数:
N - lower_bound(ceil(x))
- 以下の数の総数:
upper_bound(floor(x))
- より大きい数の総数:
N - upper_bound(floor(x))
- より小さい数の総数:
lower_bound(ceil(x))
- ちょうどに等しい数の総数:
max(0, upper_bound(floor(x)) - lower_bound(ceil(x)))
- に等しい数の存在する区間:
[lower_bound(ceil(x)), upper_bound(floor(x))
ただし、無効な区間は空とする。 - であるようなの総数:
upper_bound(floor(R)) - lower_bound(ceil(L))
- であるようなの総数:
lower_bound(ceil(R)) - lower_bound(ceil(L))
- であるようなの総数:
upper_bound(floor(R)) - upper_bound(floor(L))
- であるようなの総数:
lower_bound(ceil(R)) - upper_bound(floor(L))
1-basedで番目に小さい数がということは、以下の数が個存在するような最小のがに等しいということなので、upper_bound(t)
で以下の数の総数を求めてupper_bound(t) >= k
になるような最小のを求めればよい。列全体をいくつかの(昇順の)部分列にわけて、upper_bound
をそれぞれに対して計算して総和を取ることも可能。
1-basedで番目に大きい数がということは以上の数が個存在するような最大のがに等しいということなので、N - lower_bound(t)
で以上の数の総数を求めてN - lower_bound(t) >= k
になるような最大のを求めればよい。列全体をいくつかの(昇順の)部分列にわけて、N-lower_bound(t)
をそれぞれに対して計算して総和を取ることも可能。
列が降順にソートされている場合
- 以下でもっとも左にある数の位置:
lower_bound(x)
- 以上でもっとも右にある数の位置:
upper_bound(x)-1
- より小さいもっとも左にある数の位置:
upper_bound(x)
- より大きいもっとも右にある位置の数:
lower_bound(x)-1
- 以下の数の総数:
N - lower_bound(x)
- 以上の数の総数:
upper_bound(x)
- より小さい数の総数:
N - upper_bound(x)
- より大きい数の総数:
lower_bound(x)
- ちょうどに等しい数の総数:
upper_bound(x) - lower_bound(x)
- に等しい数の存在する区間:
[lower_bound(x), upper_bound(x))
累積和上のDP
元のDP配列上のという操作が、累積和上では, に対応することを覚えておく。の先頭にを追加せず、としたほうがわかりやすいことが多いと思う。
とする。たとえばをに足す、つまりという漸化式がなりたっている場合、もらうDPで考えると
配るDPで実装する場合、と遷移すればいいことになる。
平方分割
バケットサイズをとする。区間を処理するときはとして
- :
- 区間を処理
- :
- 区間を処理
- バケット列上の区間を処理
- 区間を処理
する。
引き算ができる場合はを処理するほうが簡単か。
巡回置換を戻す
巡回置換が配列上の部分列として現れている場合に、その部分を恒等に戻したい場合、を交換してとを交換して……とを交換する。一般に(操作としては左から右に適用することに注意)なので、その逆をやればよいということになる。
配列上の操作としては、配列におけるの初期位置をとして、とをスワップしてをスワップして……とをスワップすればよい。
2次元配列の回転
高さ, 幅の行列を反時計回りに回転する。回転後の行列をとすると。添え字を0-basedとして
- 90°の回転:
- 180°の回転:
- 270°の回転:
転置
- 左上-右下の対角線に関する転置:
- 左下-右上の対角線に関する転置:
グリッド上の正方形
グリッド上の正方形について。
上の点を通る傾き-1の直線は上のマスを個含む。また、を通る傾き1の直線は上のマスを個含む。
上の点を通る直線は、直線のそれぞれと正方形の端で交わる。
極座標と直交座標
complex
に対する極座標はabs
とphase
で求まる。逆に極座標からcomplex
を得たい場合は(* scale (cis radian))
とする。