n!的最末位非零數(原帖)
給定乙個數n(n <= 10 ^ 1000),如何快速求得n!的最末位非零數是乙個經典的問題。一直以來都被這個問題困擾,今天仔細想了下,終於給想通了,儘管可能有些笨拙,現把想法記錄於此。
在n很小的情況下,有乙個簡便的方法:求出1到n之間每個數的2的因子數和5的因子數,記為f(2)和f(5),顯然f(2) >= f(5)。由於在末尾只有2和5相乘才能產生0,如果我們把2和5拋去,那麼肯定不會有0,這樣就可以一邊乘一邊模10,防止溢位。剩下的一堆2和5如何處理呢?因為2肯定比5多,因此最末位肯定是偶數(0的階乘和1的階乘除外)。而乙個偶數不停地乘2,最末位的規律是:2 -> 4 -> 8 -> 6 -> 2 -> ...出現了4位1迴圈,這樣我們先用f(2) - f(5),使得一部分2和5匹配上,2 * 5 = 10,對末尾不產生影響,剩下的2就模一下4,剩幾再乘幾次2就可以了。
但是這個方法在n非常大的時候肯定就不行了,但是可以利用找迴圈這個思想繼續做。如果算階乘的時候跳過5的倍數,記g(n)為跳過5的倍數的時候,從1乘到n的最末非零位,也就是把5的倍數當1乘。可以發現:
g(1) = 1, g(2) = 2, g(3) = 6, g(4) = 4, g(5) = 4, g(6) = 4, g(7) = 8, g(8) = 4, g(9) = 6, g(10) = 6, g(11) = 6, g(12) = 2, g(13) = 6...
又出現了迴圈,每10個數迴圈一次。如何計算g(n)就變的很簡單,求出n的最末位,就知道對應的g(n)是多少了,當然需要特判n = 1的情況。由於我們把5的倍數的數都提出來了,提出來的這些數(5、10、15、20、25、30...)每個除以5後又組成了乙個階乘序列!除完5一共提出了n / 5個5,根據之前的分析,每個5都可以拿出乙個2和它配對然後把它消去,這樣乙個5就相當於少乙個2,我們就要把原來的數乘以3個2(模四迴圈)。這樣一來5的個數其實也可以模四,模完四之後剩k的話,就可以乘以k個8,就把所有的5消去了。現在總結一下:對乙個數n的階乘,計算它的末尾非零位,先計算g(n),相當於非5的倍數的數的乘積最末非零位先算好了,然後乘以n / 5 % 4個8,處理了提出的n / 5個5,這樣之後還剩下n / 5的階乘沒有算。遞迴的求解n / 5的階乘的最末位非零數,再乘上去就得到結果了。
這個做法的複雜度就很低了,達到o(log n),對於10 ^ 1000的資料,利用高精度做就行了。利用這種迴圈的思想,算排列數p(n, k)的最末非零數也就可以做到了。
附hoj 1013**:
1<
cstdio
>
2<
cstring
>
3const
intn
=1024;4
5inthash[
10] = ;
6intone_digit_hash[
10] = ;78
intlast_digit(
char
str[n],
intst,
intto)923
if(str[st] =='
0') st++;
24ret
=last_digit(str, st, to)
*ret %10
;25while
(num_of_five
--) ret
=ret *8
%10;
//mul one 5 equals mul one 8
2627
return
ret;
2830
intmain()
31
n 最後一位非零數 poj 1150
題意 求p n,m 的最後一位非零數。思路 討論1 n中2,5,3,7,9因子的個數,具體移步 按照我的理解,求n!最後非零為,先把1 n中 2 和 5 的因子的數量求出來,因為只有2和5可以構成0,接下來就要分別求出1 n中最後一位為3,7,9的數字的數量,然後相乘就可以得到n!的最後一位的數 i...
結尾非零數的奇偶性
描述 給你乙個正整數列表 l,如 l 2,8,3,50 判斷列表內所有數字乘積的最後乙個非零數字的奇偶性,奇數輸出1,偶數輸出0.如樣例輸出應為0 coding utf 8 l 2,8,3,50 product 1 for i in l product i def main global produ...
結尾非零數的奇偶性Python
題目描述 給你乙個正整數列表 l,判斷列表內所有數字乘積的最後乙個非零數字的奇偶性。如果為奇數輸出1,偶數則輸出0.例如 l 2,8,3,50 則輸出 0 思路 最簡單的方法就是累乘,最後根據結果判斷非零數的奇偶性。但是這樣和結尾0的個數該題犯了一樣的記憶體錯誤。其實可以和這道題一樣,發現問題的本質...