Trở lại toán tìm chữsố tận khác n! Ngô Minh Đức Trong "Tối ưu tóansố nguyên tố" (THNT 10/2003), tác giả Đinh Hữu Công nêu thuật toán "Tìm chữsố tận khác n!" với n2, phân tích n!, ta có a2 > a5 (tức dấu = không xảy ra) hay a2 − a5>0 Do (n!)’ chia hết cho L(n!)=L(n!’) nhận 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 ta suy ra: L(n!)=L(L(n).L((n -1)!)) với n không chia hết cho Như biết giá trị L((5n)!) ta tính giá trị L((5n+j)!) với j=0,1,2,3,4 Các giá trị L(n!) xếp cột thành chuỗi chữsố sau: n: 01234 10 15 20 25 30 35 40 45 L(n!): 01264 22428 88682 88682 44846 44846 88682 22428 22428 66264 … (ta bỏ qua số giá trị đặc biệt 0!, 1! − xem L(0!)=0) Tuy nhiên giá trị L((5n)!) không thực rõ ràng Nếu ta tiếp tục xếp cột giá trị L((5n)!) ta thấy chúng lập thành chuỗi chữsố Điều cho phép ta nêu kết luận tương tự: cần biết giá trị L((25n)!) biết L((25n+5j)!) với j=0,1,2,3,4 (nghĩa giá trị L((25n+5j)!) phụ thuộc vào giá trị L((25n)!) Tiếp tục thế, xếp giá trị L((5kn)!) bảng sau: Ta rút kết luận (không thấy tác giả chứng minh): giá trị L((5kn+5k-1j)!) phụ thuộc vào giá trị L((5kn)!) với j=0,1,2,3,4 Nếu giá trị L((5kn)!) xác định giá trị L((5kn + 5k-1j)!) xác định Ví dụ: Ta có L((125.3)!)=L(375!)=2 L((125.3+25)!)=L(400!)=8 Thế L((125.8)!)=L(1000!)=2 nên L((125.8+25)!)=L(1025)! L((125.10)!=L(1250!)=2 nên L((125.10+25)!)=L(1275!) 8)… Tóm lại ta khẳng định L((125.k)!)=2 chắn L((125.k+25)!)phải chữsố thứ hai chuỗi 28488 (chữ số 8) Mặt khác, ta nhận thấy giá trị 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 dòng ta thấy có chuỗi phân biệt gồm chữ số, chuỗi bắt đầu số {0,2,4,6,8} Ta cần tổng hợp tất chuỗi lại thành bảng để sử dụng Chúng ta rút bảng sau dùng để tính giá trị L((5kn +5k−1j)!) với j=0,1,2,3,4 biết giá trị L((5kn)!): Chẳng hạn biết L((125.2)!)=8 ta tính L((125.2+25.2)!)=6 cách nhìn vào hàng (2 mod 4)=2 (do 25=52), chuỗi bắt đầu chữsố (82622), vị trí j=2 Thuật toán tìm L(n!) Đổi n số 5, ta được: Dựa vào bảng trên, ta thấy biết L((dn5n)!) ta tính L((dn5n +dn-15n-1 )!) Tương tự biết L((dn5n +dn-15n-1 )!), ta tính L((dn5n +dn-15n-1 + dn25n-2)!), v.v cuối tính L(n!) Tuy nhiên phải tính L((dn5n)!), muốn ta hình dung: Do L((dn+15n+1)!)=0(một quy ước cho phù hợp với bảng � để ta xét chuỗi có chữsố 0) Ta dựa vào L((dn+15n+1)!)=0 dde^? L((dn5n)!) Ví dụ với n=1592: Bước 1: đổi n số 5, Bước 2: Xét dòng (=4 mod 4), chuỗi có chữsố đầu 0, tức 06264 Bước 3: chữsố n (cơ số 5) 2, L((2.54)!) =2, chữsố vị trí j=2 Bước 4: Xét dòng (=3 mod 4), chuỗi có chữsố 2, tức 26648 Bước 5: chữsố thứ hai n (cơ số 5) 2, L((2.54+2.53)!) =6, chữsố vị trí j=2 Bước 5: Xét dòng (=2 mod 4), chuỗi có chữsố 6, tức 64244 Bước 6: chữsố thứ ba n (cơ số 5) 3, L((2.54+2.53 +3.52)!)=4 số vị trí j=3 … Cuối ta có: L(1592!) = L((2.54+2.53 +3.52 + 3.5 + 2)!)=4 Chúng ta mô tả bảng cách xây dựng mảng Ă4,5,5) Trong số cho biết vị trí dòng (0,1,2,3); số thứ hai cho biết chữsố chuỗi (0,2,4,6,8); số thứ ba cho biết vị trí j (0,1,2,3,4) Nếu gọi dk chữsố thứ k hệ số n, ta mô tả thuật tóan ngắn gọn sau: Sn = Ăn mod 4, 0, dn) Sn-1 = Ăn-1 mod 4, sn, dn-1) … S1= A(1,s2, d1) S0= s1, d0) Cuối ta có S0 = L(n!) Chương trình: Dữ liệu: gồm số tự nhiên n đặt file NONZERO.INP Quy định: Số viết nhiều dòng (nếu qúa dài) vd: NONZERO.INP 549485904089340974897979878989793489787935789973493489789438957897578 478993476894845989897348 Kết qủa: in hình L(n!) Chương trình tính L((1000!)!) = 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 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 chuso viet xuoi: vd so=1592, B=[1,5,9,2]} {Mang A chua cac chuso 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 ta nhan voi roi chia cho 10 bang cach bo di chuso 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 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 ... chuỗi có chữ số đầu 0, tức 06 264 Bước 3: chữ số n (cơ số 5) 2, L((2.54)!) =2, chữ số vị trí j=2 Bước 4: Xét dòng (=3 mod 4), chuỗi có chữ số 2, tức 26648 Bước 5: chữ số thứ hai n (cơ số 5) 2,... dựng mảng Ă4,5,5) Trong số cho biết vị trí dòng (0, 1,2,3); số thứ hai cho biết chữ số chuỗi (0, 2,4,6,8); số thứ ba cho biết vị trí j (0, 1,2,3,4) Nếu gọi dk chữ số thứ k hệ số n, ta mô tả thuật tóan... số 5) 2, L((2.54+2.53)!) =6, chữ số vị trí j=2 Bước 5: Xét dòng (=2 mod 4), chuỗi có chữ số 6, tức 64244 Bước 6: chữ số thứ ba n (cơ số 5) 3, L((2.54+2.53 +3.52)!)=4 số vị trí j=3 … Cuối ta có: