1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

大きな整数の階乗の剰余を求める

Posted at

math.factorialを使う

pythonでは整数Nの階乗の剰余は以下のようにmath.factorialを使えば簡単に求まりますが、Nが大きくなると非常に大きな数を一旦生成するのであまり効率が良くありません。(計算量は$O(\frac{1}{2}n^2)$)

from math import factorial
M = 10**9+7
N = 10**6
print(N, factorial(N)%M)
# 1000000 641102369

Nの階乗の素因数分解を求める

そこでまずNの階乗の素因数分解を行います。考え方は、例えば10の階乗の場合、2の倍数が5個、$2^2=4$の倍数が2個、$2^3=8$の倍数が1個、なので素因数2は計8個あることが分かります。これをN以下のすべての素数について行うとNの階乗の素因数分解が求まります。

from sympy import primerange
def fact2fct(n):   # nの階乗の素因数分解
  ret = dict()
  for p in primerange(2,n+1):
    ret[p], pp = 0, p
    while pp <= n:
      ret[p] += n // pp
      pp *= p
  return ret

N = 10
print(N, fact2fct(N))
#10 {2: 8, 3: 4, 5: 2, 7: 1}

各素因数に対してpow関数で剰余計算を行う

素因数分解の形になればpow関数が使えます。(計算量はほぼ$O(n)$)

def factmod(fct,M):
  ret = 1
  for p in fct.keys():
    ret *= pow(p,fct[p], M)
    ret %= M
  return ret

M = 10**9+7
n = 10**6
stime = time.time()
print(n, factmod(fact2fct(n), M))
#1000000 641102369

(応用) 1からNまでの数の最小公倍数の素因数分解

この考え方を使うと1からNまでの数の最小公倍数の素因数分解も同様に実装できます。

from sympy import primerange
def lcmall(n):   # 1からnのすべての数の最小公倍数の素因数分解
  ret = dict()
  for p in primerange(2,n+1):
    pp, i = p, 0
    while pp <= n:
      pp *= p
      i += 1
    ret[p] = i
N = 10
print(N, lcmall(N))
# 10 {2: 3, 3: 2, 5: 1, 7: 1}

(開発環境:Google Colab)

この考え方は以下のProject Eulerの問題を解くのに役に立ちます。

  • Problem 429: Sum of squares of unitary divisors.
  • Problem 772: Balanceable k-bounded partitions.
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?