Hàm tính giai thừa

Liên hệ QC

nguyenmai412

Thành viên mới
Tham gia
13/6/15
Bài viết
8
Được thích
3
Hi cả nhà,

Em đang muốn tìm số chữ số 0 cuối cùng của một giai thừa (n!).

Thuật toán bình thường
1532231246506.png
tức là em lấy n, chia cho 5 với số mũ tăng dần (luỹ thừa của 5 nhỏ hơn n 1532231344513.png) và cộng tổng kết quả thì ra.
Ví dụ n=32, số chữ số 0 cuối cùng của 32! sẽ là 1532231488362.png, kết quả bằng 7

Nhưng khi em sử dụng hàm Fact trong excel thì kết quả 1532231549111.png
Đối với số từ 20 trở xuống thì theo đúng thuật toán, nhưng từ 21 trở lên thì không còn đúng nữa, em k biết do excel hay do thuật toán.

Cảm ơn cả nhà đã đọc.
 

File đính kèm

  • 1532231328346.png
    1532231328346.png
    1.6 KB · Đọc: 4
Hi cả nhà,

Em đang muốn tìm số chữ số 0 cuối cùng của một giai thừa (n!).

Thuật toán bình thường
View attachment 200138
tức là em lấy n, chia cho 5 với số mũ tăng dần (luỹ thừa của 5 nhỏ hơn n View attachment 200140) và cộng tổng kết quả thì ra.
Ví dụ n=32, số chữ số 0 cuối cùng của 32! sẽ là View attachment 200141, kết quả bằng 7

Nhưng khi em sử dụng hàm Fact trong excel thì kết quả View attachment 200142
Đối với số từ 20 trở xuống thì theo đúng thuật toán, nhưng từ 21 trở lên thì không còn đúng nữa, em k biết do excel hay do thuật toán.

Cảm ơn cả nhà đã đọc.
 
Thử:
Mã:
Public Function DemSo0(so As Long) As Long
Dim i As Long, j As Long
Dim k As Long
j = 1
Do While j <= so
j = j * 5
k = k + Int(so / j)
Loop
DemSo0 = k
End Function
Hoặc dùng công thức mảng:

=SUM(IF(A5/5^ROW(INDIRECT("1:100"))>=1,INT(A5/5^ROW(INDIRECT("1:100")))))
 
Lần chỉnh sửa cuối:
@thớt: lần sau mà hai hiếc gì đó thì tôi sẽ không trả lời.
Có lẽ là bạn quên mất ở đây ta nói về con toán giai thừa.
Kết quả của số giai thừa nó lớn lắm. Biến Long chỉ chứa tới hơn 2 tỷ 1 chút, tức là khoảng giai thừa của 16. Excel mặc định dùng biến Double cho nên chưa bị tràn số khi giai thừa lên đến 20.
Tuy nhiên máy tính chỉ chính xác được tới 15 chữ số cho nên vấn đề của hàm fact không thể tính theo con toán đơn giản. Có hai cách tính:
1. là dùng kỹ thuật số nguyên dài (long integer)
2. là dùng nguyên tắc cứ qua một lần 5 hoặc 10 thì số sẽ có thêm 1 số 0. Vì vậy chỉ cần đếm số lần 5,10 ấy thôi. Đó chính là cái nguyên tắc của "thuật toán bình thường" ở trên

Thử:
Mã:
Public Function DemSo0(so As Long) As Long
Dim i As Long, j As Long
Dim k As Long
j = 1
Do While j <= so
j = j * 5
k = k + Int(so / j)
Loop
DemSo0 = k
End Function
Code nhanh và gọn hơn (tiết kiệm 1 con toán nhân, đồng thời con toán chia số nguyên nhanh hơn chia số thực nhiều)
Public Function DemZero(ByVal so As Long) As Long
Do While so > 0
so = so \ 5
DemZero = DemZero + so
Loop
End Function
 
Hi cả nhà,

Em đang muốn tìm số chữ số 0 cuối cùng của một giai thừa (n!).

Thuật toán bình thường
View attachment 200138
tức là em lấy n, chia cho 5 với số mũ tăng dần (luỹ thừa của 5 nhỏ hơn n View attachment 200140) và cộng tổng kết quả thì ra.
Ví dụ n=32, số chữ số 0 cuối cùng của 32! sẽ là View attachment 200141, kết quả bằng 7

Nhưng khi em sử dụng hàm Fact trong excel thì kết quả View attachment 200142
Đối với số từ 20 trở xuống thì theo đúng thuật toán, nhưng từ 21 trở lên thì không còn đúng nữa, em k biết do excel hay do thuật toán.

Cảm ơn cả nhà đã đọc.
Số chữ số trong Excel bị giới hạn nên không thể tính hàm fact với giá trị lớn, dùng hàm tự tạo để tích giai thừa lớn
Mã:
Function Giaithua(ByVal n As Byte) As String
  ' n phai nho hon 255
  Dim Str As String, i As Byte, m As Byte, tmp, f
  Const k = 15
  tmp = 1
  For i = 2 To n
    f = tmp * i
    If Len(CStr(f)) > k Then
      tmp = CStr(tmp)
      For m = i To n
        tmp = Nhan(m, tmp)
      Next m
      Giaithua = tmp: Exit Function
    Else
      tmp = f
    End If
  Next i
  Giaithua = tmp
End Function
Private Function Nhan(ByVal m As Byte, ByVal so As String) As String
  Dim sArr(), tmp As String, i As Byte, j As Byte
  Const k = 10
  j = Int(Len(so) / k) + 1
  ReDim sArr(1 To j, 1 To 2)
  For i = 1 To j
    tmp = CStr(Val(Right(so, k)) * m)
    sArr(i, 1) = Right(tmp, k)
    sArr(i, 2) = Replace(tmp, sArr(i, 1), "")
    so = Replace(so, Right(so, k), "")
  Next i
  For i = 2 To j
    tmp = CStr(Val(sArr(i, 1)) + (10 ^ k) * Val(sArr(i, 2)) + Val(sArr(i - 1, 2)))
    sArr(i, 1) = Right(tmp, k)
    sArr(i, 2) = Replace(tmp, sArr(i, 1), "")
  Next i
  tmp = ""
  For i = 1 To j
    tmp = sArr(i, 1) & tmp
  Next i
  Nhan = sArr(j, 2) & tmp
End Function
 

File đính kèm

  • Giaithua.xlsb
    15.8 KB · Đọc: 5
@thớt: lần sau mà hai hiếc gì đó thì tôi sẽ không trả lời.
Có lẽ là bạn quên mất ở đây ta nói về con toán giai thừa.
Kết quả của số giai thừa nó lớn lắm. Biến Long chỉ chứa tới hơn 2 tỷ 1 chút, tức là khoảng giai thừa của 16. Excel mặc định dùng biến Double cho nên chưa bị tràn số khi giai thừa lên đến 20.
Tuy nhiên máy tính chỉ chính xác được tới 15 chữ số cho nên vấn đề của hàm fact không thể tính theo con toán đơn giản. Có hai cách tính:
1. là dùng kỹ thuật số nguyên dài (long integer)
2. là dùng nguyên tắc cứ qua một lần 5 hoặc 10 thì số sẽ có thêm 1 số 0. Vì vậy chỉ cần đếm số lần 5,10 ấy thôi. Đó chính là cái nguyên tắc của "thuật toán bình thường" ở trên


Code nhanh và gọn hơn (tiết kiệm 1 con toán nhân, đồng thời con toán chia số nguyên nhanh hơn chia số thực nhiều)
Public Function DemZero(ByVal so As Long) As Long
Do While so > 0
so = so \ 5
DemZero = DemZero + so
Loop
End Function
Dạ em cảm ơn Thầy, lần sau em sẽ lưu ý câu chữ ạ
Bài đã được tự động gộp:

Số chữ số trong Excel bị giới hạn nên không thể tính hàm fact với giá trị lớn, dùng hàm tự tạo để tích giai thừa lớn
Mã:
Function Giaithua(ByVal n As Byte) As String
  ' n phai nho hon 255
  Dim Str As String, i As Byte, m As Byte, tmp, f
  Const k = 15
  tmp = 1
  For i = 2 To n
    f = tmp * i
    If Len(CStr(f)) > k Then
      tmp = CStr(tmp)
      For m = i To n
        tmp = Nhan(m, tmp)
      Next m
      Giaithua = tmp: Exit Function
    Else
      tmp = f
    End If
  Next i
  Giaithua = tmp
End Function
Private Function Nhan(ByVal m As Byte, ByVal so As String) As String
  Dim sArr(), tmp As String, i As Byte, j As Byte
  Const k = 10
  j = Int(Len(so) / k) + 1
  ReDim sArr(1 To j, 1 To 2)
  For i = 1 To j
    tmp = CStr(Val(Right(so, k)) * m)
    sArr(i, 1) = Right(tmp, k)
    sArr(i, 2) = Replace(tmp, sArr(i, 1), "")
    so = Replace(so, Right(so, k), "")
  Next i
  For i = 2 To j
    tmp = CStr(Val(sArr(i, 1)) + (10 ^ k) * Val(sArr(i, 2)) + Val(sArr(i - 1, 2)))
    sArr(i, 1) = Right(tmp, k)
    sArr(i, 2) = Replace(tmp, sArr(i, 1), "")
  Next i
  tmp = ""
  For i = 1 To j
    tmp = sArr(i, 1) & tmp
  Next i
  Nhan = sArr(j, 2) & tmp
End Function
Dạ em cảm ơn anh ạ.
 
Web KT
Back
Top Bottom