これを行う良い方法があるかどうか興味がありました。私の現在のコードは次のようなものです:
def factorialMod(n, modulus):
ans=1
for i in range(1,n+1):
ans = ans * i % modulus
return ans % modulus
しかし、それはかなり遅いようです!
私もnを計算できません! nが非常に大きいためn!明示的に計算することは不可能です。
また、私は http://en.wikipedia.org/wiki/Stirling%27s_approximation に出会い、これが何らかの形でここで使用できるかどうか疑問に思いましたか?
または、C++で再帰的なメモ関数を作成するにはどうすればよいですか?
コメントを回答に拡大:
はい、これを行うより効率的な方法があります。 しかし、それらは非常に乱雑です
そのため、その余分なパフォーマンスが本当に必要でない限り、これらを実装することはお勧めしません。
重要なのは、モジュラス(本質的に除算)がボトルネック操作になることに注意することです。幸いなことに、同じ数で何度もモジュラスを実行できる非常に高速なアルゴリズムがいくつかあります。
これらの方法は、本質的に弾性率を排除するため、高速です。
これらの方法だけで、中程度のスピードアップが得られます。本当に効率的にするには、ループを展開してIPCを改善する必要があります。
このようなもの:
ans0 = 1
ans1 = 1
for i in range(1,(n+1) / 2):
ans0 = ans0 * (2*i + 0) % modulus
ans1 = ans1 * (2*i + 1) % modulus
return ans0 * ans1 % modulus
しかし、奇数回の反復を考慮し、上記でリンクした方法の1つと組み合わせます。
ループ展開はコンパイラーに任せるべきだと主張する人もいるかもしれません。コンパイラーは現在、この特定のループを展開するほどスマートではないと反論します。よく見ると、その理由がわかります。
私の答えは言語に依存しませんが、主にCまたはC++を対象としています。
nは任意に大きくすることができます
さて、n
は任意に大きくすることはできません-_n >= m
_の場合、n! ≡ 0 (mod m)
(階乗の定義により、m
は要因の1つであるため)。
_n << m
_があり、exact値が必要であると仮定すると、私の知る限り、アルゴリズムを高速化することはできません。ただし、_n > m/2
_の場合、次のIDを使用できます( ウィルソンの定理 -@Daniel Fischerに感謝!)
乗算の数を約_m-n
_に制限する
(m-1)! -1 -1(mod m) 1 * 2 * 3 * ... *(n-1)* n *(n + 1)* ... *(m-2)*(m-1 )-1 -1(mod m) n! *(n + 1)* ... *(m-2)*(m-1)-1 -1(mod m) n! ≡-[(n + 1)* ... *(m-2)*(m-1)]-1 (mod m)
これにより、_m-n-1
_乗算でn! (mod m)
を計算する簡単な方法に加えて、 モジュラー逆行列 が得られます。
def factorialMod(n、モジュール): ans = 1 if n <=モジュール//2: #階乗を正常に計算します(範囲の右引数) ()は排他的) for i in range(1、n + 1): ans =(ans * i)%モジュラス else: #Fancypants range(n + 1、modulus)のiの大きなn の方法: ans =(ans * i)%モジュラス ans = modinv(ans、モジュラス) ans = -1 * ans +モジュラス ans%モジュラス を返します
上記の式を別の方法で言い換えることができます。これは、わずかに高速に実行される場合とされない場合があります。次のIDを使用します。
方程式を次のように言い換えることができます
n! ≡-[(n + 1)* ... *(m-2)*(m-1)]-1 (mod m) n! ≡-[(n + 1-m)* ... *(m-2-m)*(m-1-m)]-1 (mod m) (用語の逆順) n! ≡-[(-1)*(-2)* ... * m-n-2)*-(m-n-1)]-1 (mod m) n! ≡-[(1)*(2)* ... *(m-n-2)*(m-n-1)*(-1)(m-n-1)]-1 (mod m) n! ≡[(m-n-1)!]-1 *(-1)(m-n) (mod m)
これは、Pythonで次のように記述できます。
def factorialMod(n、モジュール): ans = 1 if n <=モジュール//2: #階乗を正常に計算します(範囲の右引数) ()は排他的) for i in range(1、n + 1): ans =(ans * i)%モジュラス else: #Fancypants range(1、modulus-n)のiに対する大きなn の方法: ans =(ans * i)%モジュラス ans = ---(modinv(ans、モジュラス) #mは奇数の素数であるため、nが偶数の場合は(-1)^(mn)= -1、nが奇数の場合は+1 if n%2 == 0: ans = -1 * ans +モジュラス ans%モジュラス を返します
exact値が必要ない場合、寿命が少し楽になります- スターリングの近似 を使用して近似値を計算できますO(log n)
timeの値( 二乗によるべき乗 )を使用します。
最後に、これがタイムクリティカルでPythonを使用している場合は、C++に切り替えてみてください。個人的な経験から、速度の大幅な増加以上を期待する必要があります。これは、これがまさにネイティブにコンパイルされたコードexcelsat(また、何らかの理由で、GMPはPythonのBignumよりもはるかに細かく調整されているようです)。
n! mod mはO(n1/2 +ε)単純なO(n)の代わりの操作。これにはFFT多項式乗算の使用が必要であり、非常に大きなn、例えばn> 104。
アルゴリズムといくつかのタイミングの概要は、ここで見ることができます: http://fredrikj.net/blog/2012/03/factorials-mod-n-and-wilsons-theorem/
M = a*(a+1) * ... * (b-1) * b (mod p)
を計算する場合、次のアプローチを使用できます。加算、減算、乗算が高速(mod p)で、O( sqrt(b-a) * polylog(b-a) )
の実行時間の複雑さを取得できると仮定した場合。
簡単にするために、_(b-a+1) = k^2
_は正方形であると仮定します。これで、製品をk個の部分、つまりM = [a*..*(a+k-1)] *...* [(b-k+1)*..*b]
に分割できます。この製品の各要因は、適切なx
のp(x)=x*..*(x+k-1)
という形式です。
Schönhage–Strassenアルゴリズム などの多項式の高速乗算アルゴリズムを除算と征服の方法で使用することにより、多項式p(x) in O( k * polylog(k) )
の係数を見つけることができます。さて、明らかにO( k * polylog(k) )
の同じ次数k多項式のk
点を置換するアルゴリズムがあります。つまり、p(a), p(a+k), ..., p(b-k+1)
を高速に計算できます。
多くの点を1つの多項式に置き換えるこのアルゴリズムは、C。PomeranceとR. Crandallの著書「Prime numbers」に記載されています。最終的に、これらのk
値がある場合、O(k)
でそれらを乗算し、目的の値を取得できます。
_(mod p)
_を使用したすべての操作に注意してください。正確な実行時間はO(sqrt(b-a) * log(b-a)^2 * log(log(b-a)))
です。
私のコメントを拡大すると、これは[100、100007]のすべてのnに対して約50%の時間がかかります。ここで、m =(117 | 1117)です。
Function facmod(n As Integer, m As Integer) As Integer
Dim f As Integer = 1
For i As Integer = 2 To n
f = f * i
If f > m Then
f = f Mod m
End If
Next
Return f
End Function
私はこの次の関数をquoraで見つけました:
With f(n、m)= n! mod m;
function f(n,m:int64):int64;
begin
if n = 1 then f:= 1
else f:= ((n mod m)*(f(n-1,m) mod m)) mod m;
end;
おそらく時間のかかるループを使用して、文字列に格納されている大きな数を掛けてビートを打ちます。また、任意の整数mに適用できます。
この関数を見つけたリンク: https://www.quora.com/How-do-you-calculate-n-mod-m-where-n-is-in-the -1000s-and-m-is-a-very-large-prime-number-eg-n-1000-m-10-9 + 7