Problem 160 (階乗の非零末尾数字)
コンテンツ
http://projecteuler.net/index.php?section=problems&id=160
階乗の非零末尾の数字をk個求める。
n!に含まれる5の数をp(n)とすると
n!/10^p(n) mod (10^k) が分かればよい。
まず、n!/10^p(n) = 0 (mod 2^k) であるから
n!/10^p(n) mod (5^k) を求めて、あとは連立合同式
x = 0 (mod 2^k), x = ? (mod 5^k)
を解けばよい。
n!/5^p(n) mod (5^k) から n!/10^p(n) mod (5^k) を求めている。
import Data.List import Mod -- p:odd prime, k: natural number -- productMod (p^k) [ n | n <- [1..p^k-1], mod n p /= 0] = - 1 (mod p^k) (Generalization of Wilson's theorem) -- 2^(4*5^(k-1)) = 1 (mod 5^k) (Fermat's little theorem) del5 n | mod n 5 == = del5 $ div n 5 | otherwise = n -- solve n!/5^p(n) mod (5^k) solve5 n k = f $ divMod n m where m = 5^k f (,) = 1 f (q,r) | even q = proMod5 (q*m) (q*m+r) * g (q,r) `mod` m | otherwise = - proMod5 (q*m) (q*m+r) * g (q,r) `mod` m g (q,r) = let (q',r') = divMod q 5 in f (q',r'*5^(k-1)) `mod` m proMod5 x y = productMod m.map del5$ [x+1..y] -- n!/10^p(n) mod (5^k) solve10 n k = let phi = 4*5^(k-1) n5 = sum.takeWhile (>).map (div n).iterate (*5)$ 5 r = mod n5 phi a = powMod 2 (phi-r) (5^k) b = solve5 n k in mod (a*b) $ 5^k lastDigit n k = let a = invMod (2^k) (5^k) b = solve10 n k in mod (a*b*2^k) (10^k) main = print.lastDigit (10^12) $ 5
Modには剰余に関する関数が定義されている。
module Mod where import Data.List -- input; m,n:正整数 -- output; a,b s.t. am+bn=gcd(m,n) gcd' m n | m < n = let (a,b) = gcd' n m in (b,a) | r == = (1,1-q) | otherwise = let (a,b) = gcd' n r in (b,a-b*q) where (q,r) = divMod m n -- product xs (mod p) productMod p = foldl' (mulMod p) 1 where mulMod p a b = mod (a*b) p -- xn=1(mod p) なる x を返す invMod n = fst.gcd' n -- a^n (mod p) powMod a n p | n == = 1 | even n = powMod (mod (a*a) p) (div n 2) p | otherwise = (a * powMod a (n-1) p)`mod` p
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)