Tính toán với số cực lớn

tuhocvba

Administrator
Thành viên BQT
[Trình độ Excel Sơ Cấp]
Bài toán đặt ra là nếu ta phải làm việc tính toán với các số có độ dài 100 chữ số hay 1000 chữ số thì phải làm thế nào?
Thông thường, chúng ta làm việc với các số có 15 chữ số đã là lớn lắm rồi.
Trong trường hợp như vậy, chúng ta cần xây dựng hàm riêng.
Ví dụ để thực hiện phép so sánh sau:
Bạn cần đăng nhập để thấy đính kèm

Ta xây dựng hàm so sánh: Bignum_Comp(Gt1, Gt2) có giá trị trả về là:
  • 1 Nếu Gt1 > Gt2
  • 0 Nếu Gt1 = Gt2
  • -1 Nếu Gt1 < Gt2
Sau đây chúng ta sẽ cùng nhau tìm cách giải quyết bài toán này.
 

vbano1

SMod
Thành viên BQT
Bước 1: Chúng ta cần loại bỏ các ký tự vô nghĩa.
Ví dụ ta có số là "000 333" thì ta mong muốn nhận về là "333" để xử lý. Vì vậy chúng ta cần một hàm để tách chuỗi ký tự này, loại bỏ các ký tự vô nghĩa là 0 hoặc khoảng trống ở bên trái.
Mã:
Public Function ZeroTrim(strSrc As String) As String
'
' Chay tu ben trai, tim ky tu khong phai la 0 hoac khoang trong
'
    Dim i As Integer

    ZeroTrim = "0"
    For i = 1 To Len(strSrc)
        Select Case Mid(strSrc, i, 1)
            Case " ", "0"
            Case Else
                ZeroTrim = Trim(Mid(strSrc, i, Len(strSrc))) 'Tim thay ky tu khac 0 hoac khoang trong thi thuc hien tach chuoi ky tu sau do ket thuc ham ngay lap tuc'
                Exit Function
        End Select
    Next i

End Function
Bước 2: Thuật toán so sánh
-Chúng ta sẽ có ngay kết quả so sánh giữa GT1 và GT2 nếu chúng có một số âm, một số dương, hoặc cả hai số bằng 0.
Ta sẽ tạo ra một hàm trung gian để nhận biết âm dương hay là 0.
Mã:
Public Function Bignum_Sgn(ByVal GiaTri As String) As Integer
'
' Nhan biet <0 hay = 0 hay > 0
'
'  -1:Nho hon 0 ;   0:=0 ;1:Lon hon 0
'
    GiaTri = ZeroTrim(GiaTri)
    Select Case Val(Left(GiaTri, 2))
        Case Is < 0:    Bignum_Sgn = -1
        Case 0:         Bignum_Sgn = 0
        Case Else:      Bignum_Sgn = 1
    End Select
        
End Function
-Trường hợp cả hai số cùng âm hoặc cùng dương:
Chúng ta cần so sánh độ dài của hai số. Nếu độ dài khác nhau, chúng ta cũng có ngay kết quả so sánh.
Nếu có cùng độ dài. Chúng ta cần chạy từ trái về phải và so sánh từng ký tự của hai số (dùng hàm ). Nếu có sự khác biệt lập tức dừng và đưa ra ngay kết quả so sánh.
Ví dụ:
123456789
125678943
Như vậy, trong trường hợp hai số cùng âm hay dương, thì ta sẽ lấy giá trị tuyệt đối của hai số cho khỏi phiền hà.
Nếu là 123 thì giữ nguyên là 123. Nếu là -123 thì chỉ lấy 123, bỏ đi dấu "-".
Mã:
Public Function Bignum_Abs(strNo As String) As String
'
' Tra ve gia tri tuyet doi
'
    Bignum_Abs = strNo
    If Left(strNo, 1) = "-" Then Bignum_Abs = Right(strNo, Len(strNo) - 1)
    
End Function
Vậy chúng ta sẽ có hàm so sánh hai số có độ dài cực lớn như sau:
Mã:
Public Function Bignum_Comp(ByVal GiaTri1 As String, ByVal GiaTri2 As String) As Integer
'
' So sanh GiaTri1 va GiaTri2
'
' 
'
'  -1:GiaTri1<GiaTri2 0:GiaTri1=GiaTri2 1:GiaTri1>GiaTri2
'
    Dim i As Long, L As Long
    Dim Sgn1 As Integer, Sgn2 As Integer 'So sanh nhanh
    
    GiaTri1 = ZeroTrim(GiaTri1)
    GiaTri2 = ZeroTrim(GiaTri2)
    
    Sgn1 = Bignum_Sgn(GiaTri1)    'So sanh nhanh
    Sgn2 = Bignum_Sgn(GiaTri2)
    
    Bignum_Comp = 0
    
    If Sgn1 > Sgn2 Then
        Bignum_Comp = 1  'GiaTri1 > GiaTri2
    ElseIf Sgn1 < Sgn2 Then
        Bignum_Comp = -1 'GiaTri1 < GiaTri2
    ElseIf Sgn1 = 0 And Sgn2 = 0 Then
        Bignum_Comp = 0  'GiaTri1 = GiaTri2 = 0
    
    End If
    
    If Bignum_Comp <> 0 Then Exit Function  'ket qua so sanh da duoc quyet dinh

    '------------------------------------------------
    ' Truong hop: Sgn1=1,Sgn2=1 or Sgn1=-1,Sgn2=-1
    '------------------------------------------------
    GiaTri1 = Bignum_Abs(GiaTri1)   'Lay Gia Tri tuyet doi
    GiaTri2 = Bignum_Abs(GiaTri2)
        
    L = Max(Len(GiaTri1), Len(GiaTri2))
    
    GiaTri1 = String(L - Len(GiaTri1), "0") & GiaTri1
    GiaTri2 = String(L - Len(GiaTri2), "0") & GiaTri2

    
    For i = 1 To L
        Select Case StrComp(Mid(GiaTri1, i, 1), Mid(GiaTri2, i, 1))
            Case 0
            Case 1
                Bignum_Comp = 1 * Sgn1
                Exit For
            Case -1
                Bignum_Comp = -1 * Sgn1
                Exit For
        End Select
    Next i
    
End Function
Như vậy bài toán mà đặt ra đã được giải quyết xong.
 

tuhocvba

Administrator
Thành viên BQT
[Trình độ Excel Sơ Cấp]
Bài toán 2: Thực hiện phép cộng đối với số cực lớn.
Bạn cần đăng nhập để thấy đính kèm
 
  • Like
Reactions: Sco
Hàm chính Bignum_Add:
Mã:
Public Function Bignum_Add(ByVal GiaTri1 As String, ByVal GiaTri2 As String) As String
'
' Tinh tong GiaTri la Chuoi Ky Tu -String
'
'  Ket qya tra ve:Ket qua tinh toan
'      Se bi loi neu tham so dau vao la Cells rong
'
    Bignum_Add = ""
    
    If Not (Bignum_IsNumeric(GiaTri1) And Bignum_IsNumeric(GiaTri2)) Then Exit Function 'Neu khong phai la so thi bi loi
    
    If Bignum_Sgn(GiaTri1) >= 0 Then
        If Bignum_Sgn(GiaTri2) >= 0 Then
            Bignum_Add = Bignum_Add_S(GiaTri1, GiaTri2)
        Else
            Bignum_Add = Bignum_Subt(GiaTri1, Bignum_Abs(GiaTri2))
        End If
    Else
        If Bignum_Comp(GiaTri2, 0) >= 0 Then
            Bignum_Add = Bignum_Subt(GiaTri2, GiaTri1)
        Else
            Bignum_Add = "-" & Bignum_Add_S(Bignum_Abs(GiaTri2), Bignum_Abs(GiaTri1))
        End If
    End If
    
    If Bignum_Sgn(Bignum_Add) = 0 Then Bignum_Add = "0"

End Function
Hàm phụ Bignum_IsNumeric kiểm tra một chuỗi ký tự nhập vào có phải là dạng số hay không:
Mã:
Public Function Bignum_IsNumeric(ByVal strNo As String) As Boolean
'
' Ky tu la 0~9 hoac -0~9
'
'  Gia Tri Tra Ve True:La So False:Khong Phai La So
'
    Dim i As Integer
    
    Bignum_IsNumeric = True
    
    strNo = ZeroTrim(strNo)
    
    For i = 1 To Len(strNo)
        Select Case Mid(strNo, i, 1)
            Case "0" To "9", "-"
            Case Else:  Bignum_IsNumeric = False
        End Select
    Next i
        
End Function
Hàm cộng hai chuỗi ký tự Bignum_Add_S:
Mã:
Private Function Bignum_Add_S(ByVal GiaTri1 As String, ByVal GiaTri2 As String) As String
'
'

    Dim strResult As String
    
    Dim i As Integer
    Dim Tmp As Integer
    Dim L As Integer
    
    Bignum_Add_S = ""
    
    strResult = ""
    
    GiaTri1 = ZeroTrim(GiaTri1)
    GiaTri2 = ZeroTrim(GiaTri2)
    L = Max(Len(GiaTri1), Len(GiaTri2))
    GiaTri1 = String(L - Len(GiaTri1), " ") & GiaTri1
    GiaTri2 = String(L - Len(GiaTri2), " ") & GiaTri2
    
    Dim R As Integer 
    
    R = 0
    strResult = ""
    L = Len(GiaTri1)
    
    For i = L To 1 Step -1
        Tmp = Val(Mid(GiaTri1, i, 1)) + Val(Mid(GiaTri2, i, 1)) + R
        If Tmp > 9 Then
            R = Tmp \ 10
            strResult = (Tmp Mod 10) & strResult
        Else
            R = 0
            strResult = Tmp & strResult
        End If
    Next i
    strResult = R & strResult
    Bignum_Add_S = ZeroTrim(strResult)


End Function
Phép trừ hai chuỗi ký tự Bignum_Subt:
Mã:
Public Function Bignum_Subt(ByVal GiaTri1 As String, ByVal GiaTri2 As String) As String
'
'
'
    If Bignum_Sgn(GiaTri1) >= 0 Then
        If Bignum_Sgn(GiaTri2) >= 0 Then
            If Bignum_Comp(GiaTri1, GiaTri2) >= 0 Then
                Bignum_Subt = Bignum_Subt_S(GiaTri1, GiaTri2)
            Else
                Bignum_Subt = "-" & Bignum_Subt_S(GiaTri2, GiaTri1)
            End If
        Else
            Bignum_Subt = Bignum_Add(GiaTri1, Bignum_Abs(GiaTri2))
        End If
    Else
        If Bignum_Sgn(GiaTri2) >= 0 Then
            Bignum_Subt = "-" & Bignum_Add(Bignum_Abs(GiaTri2), GiaTri1)
        Else
            Bignum_Subt = Bignum_Subt_S(Bignum_Abs(GiaTri2), Bignum_Abs(GiaTri1))
        End If
    End If

    If Bignum_Sgn(Bignum_Subt) = 0 Then Bignum_Subt = "0"
    
End Function
Mã:
Private Function Bignum_Subt_S(ByVal GiaTri1 As String, ByVal GiaTri2 As String) As String
'
' Phep Tru
'
'  DieuKien S1>0,S2>0,S1>=S2
'
    Dim strResult As String

    Dim i As Integer
    Dim L As Integer
    Dim tmp1 As Integer, tmp2 As Integer
    
    strResult = ""
    
    
    GiaTri1 = ZeroTrim(GiaTri1)
    GiaTri2 = ZeroTrim(GiaTri2)
    L = Max(Len(GiaTri1), Len(GiaTri2))
    GiaTri1 = String(L - Len(GiaTri1), "0") & GiaTri1
    GiaTri2 = String(L - Len(GiaTri2), "0") & GiaTri2
    
    Dim R As Integer
    
  
    For i = Len(GiaTri1) To 1 Step -1
        tmp1 = Val(Mid(GiaTri1, i, 1)) - R
        tmp2 = Val(Mid(GiaTri2, i, 1))
        If tmp1 >= tmp2 Then
            R = 0
            strResult = (tmp1 - tmp2) & strResult
        Else
            R = 1
            strResult = (10 + tmp1 - tmp2) & strResult
        End If
    Next i
    
    Bignum_Subt_S = ZeroTrim(strResult)


End Function
 

tuhocvba

Administrator
Thành viên BQT
Nhìn vào bài viết #4 có lẽ có phần khó hiểu nếu không có giải thích gì thêm.
Bài toán đặt ra cho chúng ta: Nếu chúng ta thực hiện phép cộng hai số mà các số này có độ dài quá lớn, ví dụ khoảng 32 ký tự thì máy tính của chúng ta sẽ bị tràn số.
Vì vậy để giải quyết bài toán này, chúng ta sẽ mô phỏng lại cách con người tính toán.
Nếu coi các số này là chuỗi ký tự, chứ không phải là số, khi đó chúng ta sẽ không bị vấn đề tràn số của máy tính.
Bạn cần đăng nhập để thấy hình ảnh


Giống như con người tính toán, chúng ta sẽ thực hiện từ phải sang trái, cộng lần lượt từng con số.
Ở đây các bạn chú ý vào hàm Bignum_Add_S :
Mã:
For i = L To 1 Step -1
        Tmp = Val(Mid(GiaTri1, i, 1)) + Val(Mid(GiaTri2, i, 1)) + R
        If Tmp > 9 Then
            R = Tmp \ 10
            strResult = (Tmp Mod 10) & strResult
        Else
            R = 0
            strResult = Tmp & strResult
        End If
    Next i
Đây chính là trái tim của chương trình.
Trong đoạn code trên thì R chính là phần nhớ từ phép cộng có trừ trước đó.
Chả hạn:
Mã:
   85
+26
Ban đầu lấy 5+6+R thì lúc này R =0.
Nhưng kết quả phép tính này là 11 > 10 cho nên R = 1.
Ở phép cộng tiếp theo: 8+2+ R = 8 + 2 + 1

Những phần rườm rà khác thực ra chỉ là phụ thôi: Ví dụ: Trong trường hợp có một số âm, một số dương, thì bản chất phép cộng lúc này lại là phép trừ.
 
Top