Giải bài toán ngược (bốc thuốc) cho kế toán - Goal Seek

Liên hệ QC

QUANGTUHN

Thành viên mới
Tham gia
1/6/19
Bài viết
12
Được thích
27
Một ứng dụng của Excel - GOAL SEEK: Đây là công cụ hỗ trợ tìm ẩn số trong nhiều tình huống bài toán ngược của kế toán. Dân trong nghề hay gọi là "bốc thuốc" cho khớp số liệu:
- Lập bảng kê chi tiết mặt hàng cho khớp với tổng cộng hóa đơn
- Xuất dùng vật tư chi tiết theo số tổng đã có sẵn...
- Xác định điểm hòa vốn trong kinh doanh
...
Để các bạn dễ hình dung, mình có làm thông qua 1 ví dụ bằng video, bạn có thể tham khảo.
Các bạn download file đính kèm, thao tác theo để hiểu rõ hơn về tính năng hỗ trợ Goal seek này của Excel.
 

File đính kèm

  • hoa_don_ban_hang_le VLXD_done.xls
    58.5 KB · Đọc: 42
Một ứng dụng của Excel - GOAL SEEK: Đây là công cụ hỗ trợ tìm ẩn số trong nhiều tình huống bài toán ngược của kế toán. Dân trong nghề hay gọi là "bốc thuốc" cho khớp số liệu:
- Lập bảng kê chi tiết mặt hàng cho khớp với tổng cộng hóa đơn
- Xuất dùng vật tư chi tiết theo số tổng đã có sẵn...
- Xác định điểm hòa vốn trong kinh doanh
...
Để các bạn dễ hình dung, mình có làm thông qua 1 ví dụ bằng video, bạn có thể tham khảo.
Các bạn download file đính kèm, thao tác theo để hiểu rõ hơn về tính năng hỗ trợ Goal seek này của Excel.
Kế toán là để cho sự chính xác, và quản lý minh bạch
Mà lại bốc thuốc? có đi ngược lại bản chất vấn đề?
 
Kế toán là để cho sự chính xác, và quản lý minh bạch
Mà lại bốc thuốc? có đi ngược lại bản chất vấn đề?
Làm kế toán đều mong muốn sự minh bạch, chính xác, rõ ràng..
Tuy nhiên thực tế tại một số DN (không phải mọi doanh nghiệp), kế toán lại gặp phải các bài toán như vậy đó bạn.
Bạn đã bao giờ gặp chuyện chủ doanh nghiệp muốn kết quả kinh doanh lãi/lỗ số này, số kia?!?. Như vậy thì chính là bài toán ngược cho kế toán đó bạn à.
 
Đây là bài toán giải ngược. Ví dụ như lập dự toán lên 15.5 triệu. Nhưng bọn củ chuối phê duyệt khác, ví dụ như 15.15 triệu, và chúng ta buộc phải chỉnh, bóp méo lại cho khớp.
 
Đây là bài toán giải ngược. Ví dụ như lập dự toán lên 15.5 triệu. Nhưng bọn củ chuối phê duyệt khác, ví dụ như 15.15 triệu, và chúng ta buộc phải chỉnh, bóp méo lại cho khớp.
Mình đang phải xử lý kiểu này đây. Chỉnh số lượng hay đơn giá để tổng cộng khớp với phê duyệt, thường thì mình hay chỉnh số lượng. Mỗi tội cũng phải ra được số liệu hòm hòm gần khớp, và sử dụng Goal Seek này để số khớp 100%.
 
Ví dụ quá đơn giản, bài toán ví dụ chỉ là giải phương trình 1 ẩn bậc 1, hoàn toàn có thể tính nhẩm ra.
Thực tế bài toán tính tổng ngược phức tạp hơn nhiều. Ví dụ:
Tôi có các sản phẩm với đơn giá như sau:
A: 23.000
B: 45.000
C: 43.000
D: 28.000
E: 31.000
Chọn 1 giỏ sản phẩm với tổng giá trị là 500.000
Khó hơn: Liệt kê các phương án để chọn 1 giỏ sản phẩm với tổng giá trị là 500.000
 
Ví dụ quá đơn giản, bài toán ví dụ chỉ là giải phương trình 1 ẩn bậc 1, hoàn toàn có thể tính nhẩm ra.
Thực tế bài toán tính tổng ngược phức tạp hơn nhiều. Ví dụ:
Tôi có các sản phẩm với đơn giá như sau:
A: 23.000
B: 45.000
C: 43.000
D: 28.000
E: 31.000
Chọn 1 giỏ sản phẩm với tổng giá trị là 500.000
Khó hơn: Liệt kê các phương án để chọn 1 giỏ sản phẩm với tổng giá trị là 500.000
Bài của bác tính thử 1 phương án có kết quả là :
A: 23.000*0
B: 45.000*0
C: 43.000*0
D: 28.000*9
E: 31.000*8
 
Lần chỉnh sửa cuối:
Bạn có thể hướng dẫn cách làm tổng quát không?
1 Phương án thì chạy thử bằng solver bác ạ.
Nếu liệt kê toàn bộ chắc phải code. Cái này chưa thử.
-----
Tính thử bài 6, kết quả chỉ ra được 6 tổ hợp:
18*A+2*C
8*B+5*D
15*A+5*E
12*A+8*D
6*B+10*A
9*D+8*E
 
Lần chỉnh sửa cuối:
Ví dụ quá đơn giản, bài toán ví dụ chỉ là giải phương trình 1 ẩn bậc 1, hoàn toàn có thể tính nhẩm ra.
Thực tế bài toán tính tổng ngược phức tạp hơn nhiều. Ví dụ:
Tôi có các sản phẩm với đơn giá như sau:
A: 23.000
B: 45.000
C: 43.000
D: 28.000
E: 31.000
Chọn 1 giỏ sản phẩm với tổng giá trị là 500.000
Khó hơn: Liệt kê các phương án để chọn 1 giỏ sản phẩm với tổng giá trị là 500.000
Bác "huuthang_bd" ơi, ví dụ đơn giản hay phức tạp là một ý thôi. Còn với kế toán quan trọng là tính ứng dụng. Với cá nhân em thì thấy ví dụ của của chủ bài là hữu ích.
Không cần phải quá cao siêu, phức tạp. Như kế toán bọn em, nhiều người cũng chưa biết cái này đâu ạ.
 
1 Phương án thì chạy thử bằng solver bác ạ.
Nếu liệt kê toàn bộ chắc phải code. Cái này chưa thử.
-----
Tính thử bài 6, kết quả chỉ ra được 6 tổ hợp:
18*A+2*C
8*B+5*D
15*A+5*E
12*A+8*D
6*B+10*A
9*D+8*E
Dạ, anh CHAOQUAY cho e hỏi, a tìm tổ hợp này ntn ạ? mình dò tay từng trường hợp hay có cách nào k ạ? Anh hướng dẫn giúp e. E cảm ơn anh nhiều
Bài đã được tự động gộp:

1 Phương án thì chạy thử bằng solver bác ạ.
Nếu liệt kê toàn bộ chắc phải code. Cái này chưa thử.
-----
Tính thử bài 6, kết quả chỉ ra được 6 tổ hợp:
18*A+2*C
8*B+5*D
15*A+5*E
12*A+8*D
6*B+10*A
9*D+8*E
Vì solver mỗi lần e chạy chỉ ra 1 trường hợp tối ưu nhất, còn ra những tổ hợp như vậy e chưa biết làm ntn ạ? Có cách nào ứng dụng cả solver và vba k ạ
 
Lần chỉnh sửa cuối:
Dạ, anh CHAOQUAY cho e hỏi, a tìm tổ hợp này ntn ạ? mình dò tay từng trường hợp hay có cách nào k ạ? Anh hướng dẫn giúp e. E cảm ơn anh nhiều
Vì solver mỗi lần e chạy chỉ ra 1 trường hợp tối ưu nhất, còn ra những tổ hợp như vậy e chưa biết làm ntn ạ? Có cách nào ứng dụng cả solver và vba k ạ
Cái này tôi viết bằng VBA, không dùng solver bạn ạ.
Số tổ hợp thỏa điều kiện nhiều hơn con số này nhiều đó bạn.
Bài trước chỉ chạy thử 1 lần rồi dừng, chạy hết thì hơi lâu.
Lát sẽ chạy thử toàn bộ xem sao.
 
Cái này tôi viết bằng VBA, không dùng solver bạn ạ.

Bài trước chỉ chạy thử 1 lần rồi dừng, chạy hết thì hơi lâu.
Lát sẽ chạy thử toàn bộ xem sao.
Em Vân hi vọng được học tập code tổng quát của anh @CHAOQUAY !
@huuthang_bd
Code này tôi tính ra tất cả là 2937 tổ hợp thỏa mãn cho bài 6, không biết là đã hết hay chưa. Chắc bạn cũng đã code cho bài này, hy vọng bạn có thể chia sẻ code để tham khảo.
---
Trước khi chạy code:
nhấn alt+F11 -> chọn tools references... -> tìm, tích chon Microsoft scripting runtime
---
Bài này viết cho trường hợp giá trị j khi tạo DicLuu là duy nhất.
Nếu giá trị này không duy nhất, phần code "trả kết quả" phía cuối phải điều chỉnh lại
Mã:
Option Explicit
Sub A_Solver_Toanbo()
Dim Ten
Dim Nguon, Slpt
Dim Min, Max
Dim Tong
Dim TongR
Dim TH, THMR
Dim BangTra
Dim Ptgh
Dim Thang, Tam
Dim DicTt As New Scripting.Dictionary
Dim DicKq As New Scripting.Dictionary
Dim DicLuu As New Scripting.Dictionary
Dim Kq
Dim r, rw, rs, rws, c, cl, cs, cls, i, j, k, x, z, t, Tm
Tm = Timer

With Sheet1
    Ten = .Range("A3:A7")
    Nguon = .Range("B3:B7")
    Tong = .Range("B1")
End With
'TIM MIN, MAX, TINH CAC PHAN TU GOC
For r = 1 To UBound(Nguon)
    k = Fix(Tong / Nguon(r, 1))
    For cl = 1 To k
        j = Nguon(r, 1) * cl
        If DicLuu.Exists(j) = False Then
            DicLuu(j) = Array(Array(Nguon(r, 1)), cl, Ten(r, 1))
        Else
            Tam = DicLuu(j)
            k = UBound(Tam)
            ReDim Preserve Tam(k + 1)
            Tam(k + 1) = Array(Array(Nguon(r, 1)), cl, Ten(r, 1))
            DicLuu(j) = Tam
        End If
         
        If Max < j Then Max = j
    Next cl
Next r
Slpt = DicLuu.Count
'XONG TIM MIN, MAX, TINH CAC PHAN TU GOC
'SORT NGUON MAX_MIN
Nguon = DicLuu.Keys
ReDim Thang(Max)
For i = 0 To Slpt - 1
    Thang(Nguon(i)) = Thang(Nguon(i)) + 1
Next i
k = 0
For i = Max To 0 Step -1
    If Thang(i) <> "" Then
        k = k + Thang(i)
        Thang(i) = k
    End If
Next i
ReDim Tam(Slpt - 1)
For i = 0 To Slpt - 1
    k = Thang(Nguon(i))
    Thang(Nguon(i)) = Thang(Nguon(i)) - 1
    Tam(k - 1) = Nguon(i)
Next i
Nguon = Tam
'XONG SORT NGUON MAX_MIN
'LAP BANG TRA
ReDim BangTra(Max)
For i = 0 To Slpt - 1
    cls = Nguon(i)
    BangTra(cls) = i
Next i
'XONG LAP BANG TRA
'TIM PHANTUGIOIHAN
k = 0
For i = Slpt - 1 To 0 Step -1
    k = k + Nguon(i)
    If k >= Tong Then
        Ptgh = i
        Exit For
    End If
Next i
'XONG TIM PHANTUGIOIHAN
'LAP MANG THMR XUAT PHAT
DicTt.RemoveAll
For i = 0 To Ptgh 'CHI LAY TOI PHANTUGIOIHAN
    cls = BangTra(Nguon(i))
    DicTt(i) = Array(Array(Nguon(i)), Nguon(i), cls)
Next i
'XONG LAP MANG THMR XUAT PHAT
'TIM TOHOP
DicKq.RemoveAll
k = 0
Do While DicTt.Count
    THMR = DicTt.Items
    DicTt.RemoveAll
    k = k + 1
    For i = 0 To UBound(THMR)
        TH = THMR(i)(0)
        TongR = THMR(i)(1)
        cls = THMR(i)(2)
        ReDim Preserve TH(k)
        For j = cls + 1 To Slpt - 1
            If TongR + Nguon(j) = Tong Then
                TH(k) = Nguon(j)
                DicKq(DicKq.Count) = TH
            Else
                If TongR + Nguon(j) < Tong Then
                    TH(k) = Nguon(j)
                    cl = BangTra(Nguon(j))
                    DicTt(DicTt.Count) = Array(TH, TongR + Nguon(j), cl)
                End If
            End If
        Next j
    Next i
Loop
'XONG TIM TOHOP
'TRA KET QUA ( TINH CHO TRUONGHOP 1 PHAN TU DICLUU = 1 MANG )
Tam = DicKq.Items
ReDim Kq(1 To DicKq.Count, 1 To (k + 1) * 2)
For i = 0 To UBound(Tam)
    THMR = Tam(i)
    cls = UBound(THMR) 
    For j = 0 To cls
        TH = DicLuu(THMR(j))
        Kq(i + 1, j * 2 + 1) = TH(2)
        Kq(i + 1, j * 2 + 2) = TH(1)
    Next j
Next i
'XONG TRA KET QUA ( TINH CHO TRUONGHOP 1 PHAN TU DICLUU = 1 MANG )
With Sheet2
    .UsedRange.Clear
    .Range("A6").Resize(UBound(Kq), UBound(Kq, 2)) = Kq
    .UsedRange.Columns.AutoFit
    .Range("A3") = DicKq.Count & "_" & k
    .Range("A1") = Timer - Tm
End With
End Sub
 

File đính kèm

  • So_Theo_Tong.xlsb
    75.1 KB · Đọc: 28
Lần chỉnh sửa cuối:
Tôi không có viết code cho bài này đâu bạn. Nhưng kết quả của bạn sao lại tính 18A+2C rồi còn tính 17A+2C+1A nữa. Kết quả thực chắc ít hơn con số bạn đưa ra nhiều.
 
Chỉ là test thử cách tính, quan trọng gì đâu bạn.
 
Tôi không có viết code cho bài này đâu bạn. Nhưng kết quả của bạn sao lại tính 18A+2C rồi còn tính 17A+2C+1A nữa. Kết quả thực chắc ít hơn con số bạn đưa ra nhiều.
@huuthang_bd
Chào bạn: Bài này khó cho mình & các bạn khác khi không biết lập trình, hi.

@CHAOQUAY
Chào bạn: mình có ý tương này bạn thử áp vào code xem đươc không

Bài 6 ta lược bỏ bớt 3 số 0 cuối cho gọn, kết quả thu đươc 1 số ít như sau (còn nữa....)

224669

Bài toán sẽ tương đương với tìm Tổng bất kỳ các số trong bảng sao cho bằng 500, với bảng số như bên dưới.
Vậy mình nghĩ răng có thể dùng code để xử lý "BẢNG SỐ".

224676

Đóng góp vài ý tưởng như vậy thôi. Xin cám ơn các bạn
 
@huuthang_bd
Chào bạn: Bài này khó cho mình & các bạn khác khi không biết lập trình, hi.

@CHAOQUAY
Chào bạn: mình có ý tương này bạn thử áp vào code xem đươc không

Bài 6 ta lược bỏ bớt 3 số 0 cuối cho gọn, kết quả thu đươc 1 số ít như sau (còn nữa....)

View attachment 224669

Bài toán sẽ tương đương với tìm Tổng bất kỳ các số trong bảng sao cho bằng 500, với bảng số như bên dưới.
Vậy mình nghĩ răng có thể dùng code để xử lý "BẢNG SỐ".

View attachment 224676

Đóng góp vài ý tưởng như vậy thôi. Xin cám ơn các bạn
Tham gia vài ý với bạn:
- Việc bỏ số 0 là không cần thiết, số chia giảm 1000, số bị chia cũng giảm 1000 -> coi như là không cần giảm.
- Theo như cách của hình 1 của bạn, nếu số lượng >40 dòng mà dùng các công cụ của excel để giải quyết có lẽ là hơi bị mệt.
Cách tính của tôi trong bài 15 cũng tương tự như cách của bạn trong hình 2, chỉ khác cái là tôi gom tất cả các số thành mảng 1 chiều chứ không phải 2 chiều như của bạn. Sau đó lập tổ hợp dựa trên mảng 1 chiều này.

Theo hình 1, bạn có thể làm trên 1 file cho ra hết kết quả được hay không?
 
@huuthang_bd
Code này tôi tính ra tất cả là 2937 tổ hợp thỏa mãn cho bài 6, không biết là đã hết hay chưa. Chắc bạn cũng đã code cho bài này, hy vọng bạn có thể chia sẻ code để tham khảo.
---
Trước khi chạy code:
nhấn alt+F11 -> chọn tools references... -> tìm, tích chon Microsoft scripting runtime
---
Bài này viết cho trường hợp giá trị j khi tạo DicLuu là duy nhất.
Nếu giá trị này không duy nhất, phần code "trả kết quả" phía cuối phải điều chỉnh lại
Mã:
Option Explicit
Sub A_Solver_Toanbo()
Dim Ten
Dim Nguon, Slpt
Dim Min, Max
Dim Tong
Dim TongR
Dim TH, THMR
Dim BangTra
Dim Ptgh
Dim Thang, Tam
Dim DicTt As New Scripting.Dictionary
Dim DicKq As New Scripting.Dictionary
Dim DicLuu As New Scripting.Dictionary
Dim Kq
Dim r, rw, rs, rws, c, cl, cs, cls, i, j, k, x, z, t, Tm
Tm = Timer

With Sheet1
    Ten = .Range("A3:A7")
    Nguon = .Range("B3:B7")
    Tong = .Range("B1")
End With
'TIM MIN, MAX, TINH CAC PHAN TU GOC
For r = 1 To UBound(Nguon)
    k = Fix(Tong / Nguon(r, 1))
    For cl = 1 To k
        j = Nguon(r, 1) * cl
        If DicLuu.Exists(j) = False Then
            DicLuu(j) = Array(Array(Nguon(r, 1)), cl, Ten(r, 1))
        Else
            Tam = DicLuu(j)
            k = UBound(Tam)
            ReDim Preserve Tam(k + 1)
            Tam(k + 1) = Array(Array(Nguon(r, 1)), cl, Ten(r, 1))
            DicLuu(j) = Tam
        End If
        
        If Max < j Then Max = j
    Next cl
Next r
Slpt = DicLuu.Count
'XONG TIM MIN, MAX, TINH CAC PHAN TU GOC
'SORT NGUON MAX_MIN
Nguon = DicLuu.Keys
ReDim Thang(Max)
For i = 0 To Slpt - 1
    Thang(Nguon(i)) = Thang(Nguon(i)) + 1
Next i
k = 0
For i = Max To 0 Step -1
    If Thang(i) <> "" Then
        k = k + Thang(i)
        Thang(i) = k
    End If
Next i
ReDim Tam(Slpt - 1)
For i = 0 To Slpt - 1
    k = Thang(Nguon(i))
    Thang(Nguon(i)) = Thang(Nguon(i)) - 1
    Tam(k - 1) = Nguon(i)
Next i
Nguon = Tam
'XONG SORT NGUON MAX_MIN
'LAP BANG TRA
ReDim BangTra(Max)
For i = 0 To Slpt - 1
    cls = Nguon(i)
    BangTra(cls) = i
Next i
'XONG LAP BANG TRA
'TIM PHANTUGIOIHAN
k = 0
For i = Slpt - 1 To 0 Step -1
    k = k + Nguon(i)
    If k >= Tong Then
        Ptgh = i
        Exit For
    End If
Next i
'XONG TIM PHANTUGIOIHAN
'LAP MANG THMR XUAT PHAT
DicTt.RemoveAll
For i = 0 To Ptgh 'CHI LAY TOI PHANTUGIOIHAN
    cls = BangTra(Nguon(i))
    DicTt(i) = Array(Array(Nguon(i)), Nguon(i), cls)
Next i
'XONG LAP MANG THMR XUAT PHAT
'TIM TOHOP
DicKq.RemoveAll
k = 0
Do While DicTt.Count
    THMR = DicTt.Items
    DicTt.RemoveAll
    k = k + 1
    For i = 0 To UBound(THMR)
        TH = THMR(i)(0)
        TongR = THMR(i)(1)
        cls = THMR(i)(2)
        ReDim Preserve TH(k)
        For j = cls + 1 To Slpt - 1
            If TongR + Nguon(j) = Tong Then
                TH(k) = Nguon(j)
                DicKq(DicKq.Count) = TH
            Else
                If TongR + Nguon(j) < Tong Then
                    TH(k) = Nguon(j)
                    cl = BangTra(Nguon(j))
                    DicTt(DicTt.Count) = Array(TH, TongR + Nguon(j), cl)
                End If
            End If
        Next j
    Next i
Loop
'XONG TIM TOHOP
'TRA KET QUA ( TINH CHO TRUONGHOP 1 PHAN TU DICLUU = 1 MANG )
Tam = DicKq.Items
ReDim Kq(1 To DicKq.Count, 1 To (k + 1) * 2)
For i = 0 To UBound(Tam)
    THMR = Tam(i)
    cls = UBound(THMR)
    For j = 0 To cls
        TH = DicLuu(THMR(j))
        Kq(i + 1, j * 2 + 1) = TH(2)
        Kq(i + 1, j * 2 + 2) = TH(1)
    Next j
Next i
'XONG TRA KET QUA ( TINH CHO TRUONGHOP 1 PHAN TU DICLUU = 1 MANG )
With Sheet2
    .UsedRange.Clear
    .Range("A6").Resize(UBound(Kq), UBound(Kq, 2)) = Kq
    .UsedRange.Columns.AutoFit
    .Range("A3") = DicKq.Count & "_" & k
    .Range("A1") = Timer - Tm
End With
End Sub
Dạ, e cảm ơn anh @CHAOQUAY
 
Web KT
Back
Top Bottom