internal の設計
設計目標
internal は、複数の表現パッケージが必要とするものの公開インターフェースには含めないコードを 1 か所にまとめます。現在は関数が 1 つだけで、すべての pow と xiαi のすべての評価の背後にある自然数べきのルーチンです。
数学的背景
単位元 1 を持つモノイドの元 a と e∈N に対して、ae は e 個の積で、a0=1 です。e を二進表記 e=∑jbj2j で書くと、次が得られます。
ae=j:bj=1∏a2j,
そして因子 a2j は二乗の繰り返しによって得られます。
設計上の判断
明示的なループ不変条件を持つ二分累乗法
pow_nat は 3 つの値 state、exp、factor を保持し、初期値はそれぞれ 1、e、a で、次を維持します。
state⋅factorexp=ae.
exp≥2 の各ステップは、b=expmod2 として (state,exp,factor) を (state⋅factorb,⌊exp/2⌋,factor2) で置き換えます。不変条件は保たれます。
state⋅factorb⋅(factor2)⌊exp/2⌋=state⋅factorb+2⌊exp/2⌋=state⋅factorexp=ae.
ループは exp=0 で結果 state として、または exp=1 で結果 state⋅factor として停止します。どちらの場合も不変条件から ae が得られます。exp は各ステップで半分になるので、二乗は ⌊log2e⌋ 回です。掛け合わされる値はすべて a のべきで互いに可換なので、* の結合性しか使いません。
単位元はパラメータ
単位元は One インスタンスから取るのではなく、one~ として渡されます。呼び出し側は自分の単位元 (DensePolynomial::one()、係数に対しては One::one()) を既に知っており、そうすることでヘルパーは Mul だけを必要とし、制約を最小に保てます。
正しさ / 不変条件
- 結合的な
* に対して pow_nat(a, e, one=u) = u · a^e です。u が単位元ならこれは ae です。
- e=0 は乗算せずに
one を返すので、00=1 です。
- 乗算は高々 2⌊log2e⌋+1 回です。
採用しなかった代替案
- 乗算の繰り返し (e−1 回の積) は、多項式のべきには遅すぎます。
- 各表現に トレイトメソッド を置くと、ループが 4 回重複します。
境界
luna-poly の外からはインポートできません。
- 自然数の指数のみです。負のべきやべき剰余はありません。
- 各乗算のコストは呼び出し側に依存します。多項式の場合は最後の二乗が支配的です。