答えをある数で割ったあまりを出力する問題で使えるライブラリです。
計算の際に自動であまりを取るため、ミスを減らすことができます。
本ライブラリModIntの使用により、実行時間が遅くなる可能性があります。
使用にあたり注意してください。
最初にModInt.set_mod
かModInt.mod =
で、剰余の法を設定して下さい。
ModInt.set_mod(11)
ModInt.mod #=> 11
a = ModInt(10)
b = 3.to_m
p -b # 8 mod 11
p a + b # 2 mod 11
p 1 + a # 0 mod 11
p a - b # 7 mod 11
p b - a # 4 mod 11
p a * b # 8 mod 11
p b.inv # 4 mod 11
p a / b # 7 mod 11
a += b
p a # 2 mod 11
a -= b
p a # 10 mod 11
a *= b
p a # 8 mod 11
a /= b
p a # 10 mod 11
p ModInt(2)**4 # 5 mod 11
puts a #=> 10
p ModInt.raw(3) #=> 3 mod 11
ModInt.mod = 11
a = 12.to_m
puts a #=> 1
p a #=> 1 mod 11
ModInt
は、to_s
, inspect
を定義しています。
これにより、puts
はto_s
, p
はinspect
を内部で使っているため、出力が変更されています。
puts
メソッドは、Integer
の出力と変わらない振る舞いで便利ですが、逆にいえば見分けはつかないので注意して下さい。
a = ModInt.new(10)
b = ModInt(3)
new
を使わないシンタックスシュガーKernel#ModInt
を用意しています。
5.to_m
'2'.to_m
Integer
、String
インスタンスをModInt
に変換します。
エイリアス to_modint
, to_m
ModInt.set_mod(10**9 + 7)
ModInt.mod = 10**9 + 7
最初にこのメソッドを用い、modを設定して下さい。
これは、内部の実装として、グローバル変数$_mod
に設定しています。
また、内部では$_mod
が素数かどうかを判定して, グローバル変数$_mod_is_prime
に真偽値を代入します。
なお、このメソッドの返り値を使う機会は少ないと思いますが、mod=
の方は、代入した引数をそのまま返すのに対し、set_mod
は[mod, mod.prime?]
を返します。
エイリアス set_mod
, mod=
ModInt.mod
modを返します。
これは、内部の実装として、グローバル変数$_mod
を返します。
ModInt.raw(2, 11) # 2 mod 11
val
に対してmodを取らずに、ModIntを返します。
定数倍高速化のための、コンストラクタです。
val
が0
以上でmod
を超えないことが保証される場合、こちらでModInt
を生成した方が高速です。
ModInt.mod = 11
m = 12.to_m # 1 mod 11
n = -1.to_m # 10 mod 11
p i = m.val #=> 1
p j = n.to_i #=> 10
ModIntクラスのインスタンスから、本体の値を返します。
内部の実装として、@val
を返しています。
integer
に変換するときに、使用してください。
エイリアス val
, to_i
val
とto_i
は、ModInt
のインスタンスメソッドとしてはエイリアスで違いはありません。しかし、Integer
クラスにもto_i
メソッドがあるためto_i
の方が柔軟性がありRubyらしいです。内部実装でもどちらが引数であってもいいようにto_i
が使われています。なお、val
は、本家の関数名であり、内部実装のインスタンス変数名@val
です。
ModInt.mod = 11
m = 3.to_m
p m.inv #=> 4 mod 11
xy ≡ 1
なるy
を返します。
つまり、モジュラ逆数を返します。
ModInt.mod = 11
m = 2.to_m
p m**4 #=> 5 mod 11
p m.pow(4) #=> 5 mod 11
引数はInteger
のみです。
Integer
クラスと同じように、ModInt
同士、Integer
とModInt
で四則演算などができます。
詳しくは、使用例のところを見てください。
ModInt.mod = 11
p 5.to_m + 7.to_m #=> 1 mod 11
p 0 + -1.to_m #=> 10 mod 11
p -1.to_m + 0 #=> 10 mod 11
p 12.to_m == 23.to_m #=> true
p 12.to_m == 1 #=> true
Integer
とModInt
は==
, !=
による比較ができますが、本家ACLと一部の挙動が異なっています。
比較するInteger
は[0, mod)の範囲では真偽値を返しますが、それ以外の範囲の場合は必ずfalse
を返します。本ライブラリは[0, mod)の範囲で制約がありますが、本家ライブラリは制約がないので、この点で異なります。
- ABC156: D - Bouquet 2020/2/22開催
- ARC009: C - 高橋君、24歳 2012/10/20開催
+
, -
, *
, /
のメソッドは、それぞれ内部でdup
で複製させたものに対してadd!
, sub!
, mul!
, div!
のメソッドを用いています。レシーバのModInt
を破壊的に変更してもいいときは、こちらのメソッドを用いた方が定数倍ベースで速いかもしれません。
- 当ライブラリ
- 本家ライブラリ
- Rubyリファレンスマニュアル
- その他
- modint 構造体を使ってみませんか? (C++) - noshi91のメモ(2019/3/31)
- Pythonでmodintを実装してみた - Qiita(2019/4/1)
- C#版のModIntのドキュメント
- 本家ライブラリの再現。
- コードの本質的な部分への再現
- 実際どれぐらい遅いのか計測するため。ベンチマークとして。
- 利用者を増やして需要を確かめ、Ruby本体に入れるように訴えたい。
Rubyは、C言語/C++と異なり、次のような特徴があります。
- 負数を正数で割っても正数を返す定義
- 多倍長整数で、オーバーフローしない(※数が大きすぎると計算は遅くなります)
そのため、C言語/C++に比べるとModIntを使うメリットは薄れる部分もありますが、
- 可読性の向上
- Modの取り忘れを気にする必要がなくなる
などのメリットがあります。
$_mod
や$_mod_is_prime
というグローバル変数を使用しています。
特に実務などで、グローバル変数は忌避すべきものとされています。
しかし、クラス変数は、インスタンス変数に比べアクセスするのに時間がかかるようで、全てのそれぞれのModIntインスタンス変数@mod
を持たせるよりも遅くなることがありました。
そのため、総合的に1番実行時間が速かったグローバル変数を使用しています。
インスタンス変数、クラス変数、クラスインスタンス変数、グローバル変数、定数など色々ありますが、どれがどこで遅いのか・速いのか、何が最善なのかわかっていないので、実装を変える可能性があります。
$mod
はコードで書きたい人もいたので、名前衝突しないよう$_mod
としました。
Rubyの数値クラスは、Numeric
クラスから継承する仕様で、それに合わせています。
継承してると、少しだけいいことがあります。
==
を定義すると、!=
,zero?
,nonzero?
などのメソッドも定義される。- ここ重要です。
==
を定義しているので、!=
を定義しなくて済んでいます。
- ここ重要です。
*
を定義すると、abs2
メソッドも定義される。finite?
,real
,real?
,imag
, が使えるようになる。is_a?(Numeric)
でtrue
を返し数値のクラスだとわかる。
Integer
などの数値クラスではInteger.new(i)
のように書けないようにnew
メソッドが封じられていて、うまい継承方法がわかりませんでした。
また、そもそもInteger
と割り算などの挙動が違うため、Integer
から継承すべきではないという意見もありました。ModInt
は大小の比較などもすべきではないです。。
Rubyだと!付きメソッドが破壊的メソッドが多いから、本ライブラリでもそうしました。+
にadd
のエイリアスをつけるという案もありましたが、そのようなエイリアスがあっても使う人はほぼいないと考え保留にしました。
Ruby本体のprime
ライブラリにあるprime?
を使用せず、本家ライブラリのis_prime
関数に合わせてModInt.prime?(k)
を実装しています。こちらの方が速いようです。「Ruby本体のprime
ライブラリは計測などの目的であり高速化する必要はない」旨をRubyコミッターの方がruby-jpで発言していた記憶です。
ModInt#pow
の引数は、Integer
のみとしています。
しかし、ModInt
も許容すれば、コードがスッキリする可能性もあると思いますが、理論的にInteger
のみが来るはずと考えるためです。間違った形でModInt
を引数にとれてしまうとバグが起きたときに気がつきにくいため、封印しています。
コードがスッキリする可能性もあると思いますが、理論的に正しくないと考えるためです。間違った形でModInt
が大小比較できてしまうと、バグが起きたときに気がつきにくいため、封印しています。
それもいいかもしれないです。
Kernel#Integer
, Kernel#Float
, Kernel#Rational
に準じて、Kernel#ModInt
を作っています。
ModInt.mod = 11
m = ModInt(5)
raw
の由来は本家がそうしてたからです。
Rubyのto_i
, to_f
などに準じています。
右辺(right-hand side)の意味でrhs
という変数名が使われていましたが、馴染みがなくRubyではother
が一般的であるためother
に変更しています。
def ==(other)
@val == other.to_i
end
本家C++ライブラリの実装では、真偽値からmodint型の0, 1を作れるらしいです。
しかし、本ライブラリは、下記の理由から、対応しません。
- 定数倍高速化
- Rubyの他の数値型は、true/falseから直接変換できない。
- Rubyの偽扱いのfaltyはfalse/nilのみであり、コンストラクタだけ対応しても変な印象を受ける。
- Ruby中心に使っている人には馴染みがなく使う人が少なそう。
- 使う機会もあまりなさそう。