Tải bản đầy đủ (.doc) (5 trang)

Bài toán giai thừa và chữ số 0

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (144.51 KB, 5 trang )

Trở lại bài toán tìm chữ số tận cùng khác 0 của n!

Ngô Minh Đức
Trong bài "Tối ưu các bài tóan về số nguyên tố" (THNT 10/2003), tác giả Đinh Hữu
Công đã nêu ra thuật toán "Tìm chữ số tận cùng khác 0 của n!" với n<231.
Trong bài này tôi xin nêu ra một cách khác giải quyết bài tóan này vô cùng nhanh chóng.
Cơ sở của nó hòan tòan từ bài viết "Tìm chữ số tận cùng khác 0 của n!" (Least
Significant Non−Zero Digit of n!) của Mathpages site.
Sau đây là bài báo tiếng Anh trên đã được dịch và thay đổi một số đọan:
Gọi L(k) là chữ số tận cùng khác 0 của một số nguyên k bất kỳ. Các giá trị đầu tiên của
dãy L(k!) với k=2,3,4,…là:
2,6,4,2,2,4,2,8,8,8,6,8,2,8,8,6,8,2,4,4,8,4,6,4,4,8,4,6,8,…
Chúng ta sẽ tìm cách xác định giá trị của L(k!) với mọi k là số tự nhiên cho trước.
Viết n! dưới dạng:
N!=2a2.3a3.5a5.7a7…
Chúng ta ký hiệu (n!)’ để chỉ số n! đã bỏ đi hết các chữ số 0 tận cùng
Ta có định lý:
Trong phân tích của n! số mũ ap của p được tính theo công thức:

Dựa vào các tính chất về phần nguyên, dễ thấy nếu p1 < p2 thì ap1≥ ap2
Vậy thì a2 ≥ a5, từ đó suy ra số mũ của 10 trong n! là a10 = a5. Bỏ đi hết các lũy thừa
của 10 trong n! ta được (n!)’:
N!’=2a2.3a3.5a5.7a7…
Dựa vào định lý trên, bạn hãy chứng minh rằng với n>2, trong phân tích của n!, ta luôn
có a2 > a5 (tức là dấu = không xảy ra) hay a2 − a5>0. Do đó (n!)’ chia hết cho 2 và
L(n!)=L(n!’) sẽ nhận một trong các giá trị {2;4;6;8}
Mặt khác, ta có công thức: n!=n(n-1)!
Kết hợp hai điều này ta có thể suy ra:
L(n!)=L(L(n).L((n -1)!)) với mọi n không chia hết cho 5
Như vậy nếu đã biết được giá trị của L((5n)!) thì ta có thể tính được giá trị của L((5n+j)!)
với j=0,1,2,3,4


Các giá trị của L(n!) được xếp cột thành từng chuỗi 5 chữ số như sau:
n: 01234 5 10 15 20 25 30 35 40 45
L(n!): 01264 22428 88682 88682 44846 44846 88682 22428 22428 66264 …
(ta bỏ qua một số giá trị đặc biệt như 0!, 1! − xem như L(0!)=0)
Tuy nhiên giá trị của L((5n)!) không thực sự rõ ràng. Nếu ta tiếp tục xếp cột các giá trị


của L((5n)!) thì ta sẽ thấy chúng cũng lập thành những chuỗi 5 chữ số. Điều đó cho phép
ta nêu một kết luận tương tự: chỉ cần biết giá trị của L((25n)!) thì sẽ biết được
L((25n+5j)!) với j=0,1,2,3,4 (nghĩa là giá trị của L((25n+5j)!) chỉ phụ thuộc vào giá trị
của L((25n)!)
Tiếp tục như thế, chúng ta có thể sắp xếp các giá trị của L((5kn)!) như bảng sau:

Ta rút ra được kết luận (không thấy tác giả chứng minh): giá trị của
L((5kn+5k-1j)!) chỉ phụ thuộc vào giá trị của L((5kn)!) với j=0,1,2,3,4. Nếu giá trị của
L((5kn)!) xác định được thì giá trị của L((5kn + 5k-1j)!) lập tức cũng xác định được.
Ví dụ:
Ta có L((125.3)!)=L(375!)=2 và L((125.3+25)!)=L(400!)=8
Thế thì L((125.8)!)=L(1000!)=2 nên L((125.8+25)!)=L(1025)! cũng bằng 8
và L((125.10)!=L(1250!)=2 nên L((125.10+25)!)=L(1275!) cũng bằng 8)…
Tóm lại ta khẳng định nếu L((125.k)!)=2 thì chắc chắn L((125.k+25)!)phải là chữ số thứ
hai trong chuỗi 28488 (chữ số 8)
Mặt khác, ta nhận thấy các giá trị của L((625n)!) giống với L(n!). Điều đó cho thấy
L((5kn)!)=L((5k+4n)!) với n≠1 (không thấy tác giả chứng minh). Trên mỗi dòng ta thấy
có đúng 5 chuỗi phân biệt gồm 5 chữ số, mỗi chuỗi bắt đầu bằng một trong các số
{0,2,4,6,8}. Ta chỉ cần tổng hợp tất cả các chuỗi này lại thành một bảng để sử dụng
Chúng ta rút ra được bảng sau dùng để tính giá trị của L((5kn +5k−1j)!) với j=0,1,2,3,4
khi đã biết giá trị của L((5kn)!):

Chẳng hạn nếu đã biết L((125.2)!)=8 ta tính được L((125.2+25.2)!)=6 bằng cách nhìn vào

hàng (2 mod 4)=2 (do 25=52), chuỗi bắt đầu bằng chữ số 8 (82622), vị trí j=2
Thuật toán tìm L(n!)
Đổi n ra cơ số 5, ta được:


Dựa vào bảng trên, ta thấy nếu biết L((dn5n)!) ta sẽ tính được L((dn5n +dn-15n-1 )!)
Tương tự khi biết được L((dn5n +dn-15n-1 )!), ta sẽ tính được L((dn5n +dn-15n-1 + dn25n-2)!), v.v.. và cuối cùng sẽ tính được L(n!)
Tuy nhiên đầu tiên phải tính được L((dn5n)!), muốn vậy ta hãy hình dung:

Do đó L((dn+15n+1)!)=0(một sự quy ước cho phù hợp với bảng � để ta xét chuỗi có
chữ số đầu tiên bằng 0). Ta dựa vào L((dn+15n+1)!)</I>=0 dde^? L((dn5n)!)
Ví dụ với n=1592:
Bước 1: đổi n ra cơ số 5, được
Bước 2: Xét dòng 0 (=4 mod 4), chuỗi có chữ số đầu là 0, tức là 06264.
Bước 3: chữ số đầu tiên của n (cơ số 5) là 2, do đó L((2.54)!) =2, là chữ số ở vị trí j=2.
Bước 4: Xét dòng 3 (=3 mod 4), chuỗi có chữ số đầu tiên là 2, tức là 26648
Bước 5: chữ số thứ hai của n (cơ số 5) là 2, do đó L((2.54+2.53)!) =6, là chữ số ở vị trí
j=2
Bước 5: Xét dòng 2 (=2 mod 4), chuỗi có chữ số đầu tiên là 6, tức là 64244
Bước 6: chữ số thứ ba của n (cơ số 5) là 3, do đó L((2.54+2.53 +3.52)!)=4 là số ở vị trí
j=3

Cuối cùng ta có:
L(1592!) = L((2.54+2.53 +3.52 + 3.5 + 2)!)=4
Chúng ta mô tả bảng trên bằng cách xây dựng một mảng Ă4,5,5). Trong đó chỉ số đầu
tiên cho biết vị trí dòng (0,1,2,3); chỉ số thứ hai cho biết chữ số đầu tiên của chuỗi
(0,2,4,6,8); chỉ số thứ ba cho biết vị trí j (0,1,2,3,4). Nếu gọi dk là chữ số thứ k trong hệ
cơ số 5 của n, ta có thể mô tả thuật tóan ngắn gọn như sau:
Sn = Ăn mod 4, 0, dn)
Sn-1 = Ăn-1 mod 4, sn, dn-1)


S1</SUB>= A(1,s2, d1)
S0</SUB>= s1, d0)
Cuối cùng ta có S0 = L(n!)
Chương trình:
Dữ liệu: gồm số tự nhiên n đặt trong file NONZERO.INP
Quy định: Số có thể viết trên nhiều dòng (nếu qúa dài)
vd: NONZERO.INP
549485904089340974897979878989793489787935789973493489789438957897578
478993476894845989897348
Kết qủa: in ra màn hình L(n!)
Chương trình có thể tính L((1000!)!) = 6 trong thời gian chưa tới 0.5 giây
Program Least_Significant_Non_Zero_Digit_of_f_n;
(*=============KHAI=BAO=============*)
Const InputFile=’NONZERO.INP’;


Const Digits: array[’0’..’9’] of byte=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
Table:array[0..3,0..4,0..4] Of Byte=
(((0,6,2,6,4), (2,2,4,2,8), (4,4,8,4,6), (6,6,2,6,4), (8,8,6,8,2)),
((0,2,8,8,4), (2,4,6,6,8), (4,8,2,2,6), (6,2,8,8,4), (8,6,4,4,2)),
((0,4,2,4,4), (2,8,4,8,8), (4,6,8,6,6), (6,4,2,4,4), (8,2,6,2,2)),
((0,8,8,2,4), (2,6,6,4,8), (4,2,2,8,6), (6,8,8,2,4), (8,4,4,6,2)));
Type BigInt= array[1..10000] of 0..9;
Var A: BigInt;
R5:BigInt;
N, N5:Word;
L: Byte;
(*==========DOC=DU=LIEU===========*)
Procedure Input;

Var I:Word;
F:Text;
Ch:Char;
B:BigInt;
Begin
Assign(F,InputFile);
Reset(F);
While Not EOF(F) Do
Begin
While not EOLN(F) Do
Begin
Read(F,Ch);
N:=N+1;
B[N]:=Digits[Ch];
End;
Readln(F);
End;
Close(F);
{Mang B chua cac chu so viet xuoi: vd so=1592, B=[1,5,9,2]}
{Mang A chua cac chu so viet nguoc: vd so=1592, A=[2,9,5,1]}
{Ta su dung mang A cho thuan tien ve sau}
For I:=1 To N Do A[I]:=B[N-I+1];
If (N=1) And ((A[1]=0) Or (A[1]=1)) Then {0! hay 1!}
Begin
Write(1); Readln; Halt(1);
End;
End;
(*=========DOI=SANG=CO=SO=5=========*)
Procedure Radix5;
Var I,J:Word; C:Byte;

Begin
C:=0;


N5:=0;
Repeat
N5:=N5+1;
R5[N5]:=A[N5] mod 5;
{Thay vi chia 5 ta nhan voi 2 roi chia cho 10 bang cach bo di chu so tan cung}
For I:=N5 To N Do
Begin
A[I]:=A[I]*2+C;
C:=A[I] div 10;
A[I]:=A[I] mod 10;
End;
If C=1 Then Begin
N:=N+1;
A[N]:=1;
C:=0;
End;
J:=J+1;
A[N5]:=0;
Until N5=N;
End;
(*=========TIM=CHU=SO=TAN=CUNG=KHAC=0=CUA=N!=========*)
Procedure Solve;
Var S:Byte;
I:Word;
Begin
S:=0;

For I:=N5 DownTo 1 Do
S:=Table[(I-1) Mod 4,S div 2,R5[I]];
L:=S;
End;
(*=========CHUONG=TRINH=CHINH=========*)
Begin
Input;
Radix5;
Solve;
Writeln(L);
Readln;
End.



×