Lại thử thách. Thắng sẽ được cái giải... rút

Liên hệ QC

VetMini

Ăn cùng góc phố
Tham gia
21/12/12
Bài viết
16,939
Được thích
23,308
Buồn buồn ra đề chọc bà con chơi:

Ai cũng biết trò chơi Búa, Bao, Kéo (oẳn tù tì)

Ví dụ đã có sẵn code UserForm (hay gì gì đó) lấy được cái bạn chọn là ( "BUA", "DAO", "KEO" } (dạng Ascii Text rõ rệt)
Và máy Random ra một trong { "BUA", "DAO", "KEO" }. Ta có hai biến string
ban = một trong ( "BUA", "BAO", "KEO" }
may = một trong( "BUA", "BAO", "KEO" }
(viết hoa, không phải phải lo đến chuyện hoa và thường)

Bi giờ nhiệm vụ của bạn chỉ là gán vảo biến ketQua (string) với một trong ba "Thang", "Thua", "Hoa"

Điển hình code:
Mã:
If ban = "BUA" Then
  If may = "KEO" Then
    ketQua = "Thang"
  ElseIf may = "BAO" Then
    ketQua = Thua"
  Else
    ketQua = "Hoa"
  End If
ElseIf ban = "BAO" Then
  If may = "BUA" Then
    ketQua = "Thang"
  ElseIf may = "KEO" Then
    ketQua = Thua"
  Else
    ketQua = "Hoa"
  End If
Else ' ban ="KEO"
  If may = "BAO" Then
    ketQua = "Thang"
  ElseIf may = "BUA" Then
    ketQua = Thua"
  Else
    ketQua = "Hoa"
  End If
End If

Code trên đúng lô gic nhưng quá dài (về số dòng) và dễ bị gõ nhầm.

Thử thách bây giờ là viết lại cho ít dòng nhất (tốc độ không quan trọng)
 
Em chia thành 2 trường hợp:
* Một: ban = may -> ketqua = Hoa
* Hai: ban <> may, có 6 lượt đối chiếu. Lập thành danh sách
ds_ban-may = array(BUA-BAO, BUA-KEO, BAO-BUA, BAO-KEO, KEO-BUA, KEO-BAO)
ds_ketqua = array(Thua, Thang, Thang, Thua, Thua, Thang)
ketqua = ds_ketqua (application.match(ban-may, ds_ban-may,0))
 
Theo em:
Txt = "BUABAOKEO"
A = InStr(Txt, ban) - InStr(Txt, may)
If A = 3 Or A = -6 Then MsgBox "Thang" elseif A=0 Msgbox "Hoa" else MsgBox "Thua"
 
Lần chỉnh sửa cuối:
Sub df()
bua = 1: keo = 2: bao = 3
Ban = bao
May = keo
If Ban = May Then
kq = "Hoa"
ElseIf Ban - May = -1 Or Ban - May = 2 Then
kq = "Thang"
Else: kq = "Thua"
End If
End Sub
 
Theo em:
Txt = "BUABAOKEO"
A = InStr(Txt, ban) - InStr(Txt, may)
If A = 3 Or A = -6 Then MsgBox "Thang" elseif A=0 Msgbox "Hoa" else MsgBox "Thua"

Chuỗi Txt cũng ngắn, dễ chỉnh sửa. Vì vậy bạn cho thẳng làm hằng (literal) lúc gán cho A, hoặc khai báo theo hằng (Const) thì nó hiệu quả hơn một chút. VBA không phải nạp địa chỉ của Txt vào làm đối số trong hàm InStr (tuy tôi nói bài này không cần tốc độ, nhưng kinh nghiệm chung thì vẫn bàn)

Một dòng, hahahaha ...

.
Mã:
Ketqua = Choose(Int(InStr(1, "BAOBUA,BUAKEO,KEOBAO,BUABAO,KEOBUA,BAOKEO,KEOKEO,BAOBAO,BUABUA,", ban & may) / 21) + 1, "Thang", "Thua", "Hoa")

.

Sửa lại như vầy, ngắn hơn được chút:
Ketqua = Array("Thang", "Thua", "Hoa")(InStr(1, "BAOBUA,BUAKEO,KEOBAO,BUABAO,KEOBUA,BAOKEO,KEOKEO,BAOBAO,BUABUA,", ban & may) \ 21))

Ngắn thêm chút nữa thì như vầy:
KetQua = Array("Thang", "Thua", "Hoa")((InStr(1, "UAE", Mid(ban, 2, 1)) - InStr(1, "UAE", Mid(may, 2, 1)) + 2) Mod 3)
 
Lần chỉnh sửa cuối:
Sub df()
bua = 1: keo = 2: bao = 3
Ban = bao
May = keo
If Ban = May Then
kq = "Hoa"
ElseIf Ban - May = -1 Or Ban - May = 2 Then
kq = "Thang"
Else: kq = "Thua"
End If
End Sub
If Then Else là code dở nhất trong trường hợp so sánh một trị (Ban - May). Nó chỉ hiệu quả khi elseif là con tính khác với If. Trogn trường hợp này, Select Case hiệu quả hơn. Con tính Ban-May chỉ phải tính 1 lần.
Seclect Case Ban - May
Case 0
kq = "hoa"
Case -1, 2
kq = "Thang"
Case Else
kq = "Thua"
End Select
 
If Then Else là code dở nhất trong trường hợp so sánh một trị (Ban - May). Nó chỉ hiệu quả khi elseif là con tính khác với If. Trogn trường hợp này, Select Case hiệu quả hơn.
Chờ mãi, chờ mãi chẳng ai xài Select Case, là mục tiêu của chủ đề này. Đành phải tự viết :p :p :p
Theo ý riêng thì những code bên trên, nếu dùng số (1, 2, 3) thay cho búa bao kéo, thì độ dài code phải tính thêm 1 hàm con chuyển từ kết quả nhận về thành số. Đại khái:
PHP:
Function TuTiSo(TuTi As String) As Long
    Select Case TuTi
        Case "Bua"
            TuTiSo = 1
        Case = "Bao"
            TuTiSo = 2
        Case Else
            TuTiSo = 3
    End Case
End Function
Sau đó mới tới:
PHP:
Sub OanTuTi(May As String, Ban As String)
    Dim SMay As Long, SBan As Long
    SMay = TuTiSo(May)
    SBan = TuTiSo(Ban)
    ....
End Sub
 
Lần chỉnh sửa cuối:
Chờ mãi, chờ mãi chẳng ai xài Select Case, là mục tiêu của chủ đề này. Đành phải tự viết :p :p :p
Theo ý riêng thì những code bên trên, nếu dùng số (1, 2, 3) thay cho búa bao kéo, thì độ dài code phải tính thêm 1 hàm con chuyển từ kết quả nhận về thành số. Đại khái:
PHP:
Function TuTiSo(TuTi As String) As Long
    Select Case TuTi
        Case "Bua"
            TuTiSo = 1
        Case = "Bao"
            TuTiSo = 2
        Case Else
            TuTiSo = 3
    End Case
End Function
Sau đó mới tới:
PHP:
Sub OanTuTi(May As String, Ban As String)
    Dim SMay As Long, SBan As Long
    SMay = TuTiSo(May)
    SBan = TuTiSo(Ban)
    ....
End Sub

Lão già này cố tình viết code cho dài:

Function TuTiSo(TuTi As String) As Long
' dùng ký tự giữa của bua bao keo bởi vỉ chúng unique. Tuy ta phải tốn thêm hàm Mid nhưng bù lại chuỗi để duyệt ngắn hơn, ba ký tự thay vì 11, và không phải thêm con toán thâu từ 1,5,9 về 1,2,3
TuTiSo = InStr("uae", Mid(TuTi, 2, 1)
End Function

Code thứ 2 của bài #6 ở trên là cách chuyển bua bao keo thành số 1 2 3 dựa vào tính chất unique của ký tự giữa.

Và nếu dùng Select như bài #7 đề nghị ở trên thì:

' không cần phải SMay, SBan
Select TuTiSo(ban) - TuTiSo(may)
...
 
Lão già này cố tình viết code cho dài:
Đó là tôi chưa viết Sub Main:
Giả sử như Sub OanTuTi(May As String, Ban As String) trả về 1 Msgbox hoặc 1 giá trị gán xuống đâu đó. Sẽ phải có 1 Sub Main đại khái:

PHP:
Sub Main ()
    Dim May As String, Ban As String
    May = [A1].Value 'Or from somewhere else'
    Ban = [A2].Value 'Or from somewhere else'
    OanTuTi May, Ban
End Sub
Hoặc nếu OanTuTi là 1 Function thay vì Sub

PHP:
Sub Main ()
    Dim May As String, Ban As String
    May = [A1].Value 'Or from somewhere else'
    Ban = [A2].Value 'Or from somewhere else'
    [A3] = OanTuTi (May, Ban)
End Sub
 
Tôi không biết mọi người bàn về cái gì nhưng tôi nghĩ chỉ đơn giản thế này là được.

Mã:
Function OanTuTi(ByVal May As String, ByVal Ban As String) As String
    If Ban = May Then
        OanTuTi = "hoa"
    ElseIf InStr(1, "baobuakeobao", Ban & May) Then
        OanTuTi = "thang"
    Else
        OanTuTi = "thua"
    End If
End Function
 
Tôi đã phê bình bài #4 và #5 rồi mà bỏ qua #2 và #3 thì không công bình :p

Em chia thành 2 trường hợp:
* Một: ban = may -> ketqua = Hoa
* Hai: ban <> may, có 6 lượt đối chiếu. Lập thành danh sách
ds_ban-may = array(BUA-BAO, BUA-KEO, BAO-BUA, BAO-KEO, KEO-BUA, KEO-BAO)
ds_ketqua = array(Thua, Thang, Thang, Thua, Thua, Thang)
ketqua = ds_ketqua (application.match(ban-may, ds_ban-may,0))

1. Code kẹp từng đôi ban-may rất hay. Nhưng bị cái tội là không lợi dụng được tính chất "thắng thua tay ba" của trò oẳn tù tì.
Dùng số thì phải đổi chuỗi thành số, tuy phức tạp hơn nhưng sẽ uyển chuyển dễ sửa hơn nếu (để ý: nếu) code chính là Form trả về trị của các Radio Button là 1, 2, 3

2. Bạn đã có cơ hội tự tạo array cho Match thì nên sắp xếp chúng theo thứ tự, Match nhanh hơn

Theo em:
Txt = "BUABAOKEO"
A = InStr(Txt, ban) - InStr(Txt, may)
If A = 3 Or A = -6 Then MsgBox "Thang" elseif A=0 Msgbox "Hoa" else MsgBox "Thua"

Như đã bàn bài #4, code so sánh 2 lần A. Nếu dùng Select hoặc thêm con toán đổi thì xem đẹp hơn (tốc độ cũng vậy)
Đổi các trị -6, -3, 0, 3, 6 về thành 3
A = (InStr(Txt, ban) - InStr(Txt, may) + 7) Mod 9
If A = 1 Then MsgBox "Thang" elseif A=7 Then MsgBx "Hoa" else MsgBox "Thua"
Ngắn hơn, nhưng chậm hơn vì IIF tính tất cả, không dừng ở true
MsgBox IIF(A = 1, "Thang", IIF(A = 4, "Thua", "Hoa"))
 
Tôi không biết mọi người bàn về cái gì nhưng tôi nghĩ chỉ đơn giản thế này là được.
...
Bác bị sót câu này:
Thử thách bây giờ là viết lại cho ít dòng nhất (tốc độ không quan trọng)

Code của bác rất đơn giản và hiệu quả. Nếu cần ví dụ so sánh cho tác giả bài #4 thì rất tiện:
Code ElseIF của bác test lô gic của một trị khác cho nên phải dùng IF-ElseIF : Code ElseIF của tác giả #4 test lô gic của cùng một trị cho nên dùng Seclect-Case tiện hơn.

Để tóm code bác lại 1 dòng, dùng IIF thì phải hy sinh sự đơn giản vàhiệu quả tốc độ (IF tính đến ture thì dừng, IIF phải tính tất cả các tham số)

OanTuTi = IIF(Ban = May, "Hoa", IIF(InStr(1, "baobuakeobao", Ban & May), "Thang", "Thua"

Tới đây thì coi như code này là ngắn nhất và dễ chỉnh sửa nhất so với mọi bài. Mi còm-pơ-li-ment-sờ, Còng-gỡ-rát!. (me compliments, congrats!)
Nếu bác còn rút được nữa thì có thể coi như đoạt giải. %$$
 
Nếu bác còn rút được nữa thì có thể coi như đoạt giải. %$$
Nếu giải là một khoản tiền vào tài khoản hoặc chí ít cũng một thẻ cào, hoặc thẻ đi tẩm quất ở trung tâm nào có nhiều nhân viên nữ trẻ đẹp, thì cũng ham đấy. Còn giải ảo thì để người khác chơi thôi.
 
Tổng Kết Sơ KHởi:

Bạn nào biết tôi thì cũng biết rằng tôi có tật dương Đông kích Tây. Một lời nói của tôi cũng có khi chỉ dùng để dẫn bên đối thoại đi vào một đề tài khác.

Không như bài "cam quýt", chỉ thử thách ở cai mẹo "còn 3 một bên giỏ thì có thể tăng lên một đĩa"; bài này chuyên về lập trình hơn.

Đầu tiên hết, bài #1 tôi nhẹ nhàng giới thiệu nguyên tắc "thắng thua vòng tròn" của oẳn tù tì là để đưa quý vị vào giải thuật đằng sau nó: bảng chân lý (truth table).
Cái truth table, theo đúng định công việc của nó, gồm 2 tầng:
- tầng 1 gồm các lựa chọn của bạn.
- tầng 2 gồm các lựa chọn của máy, chia ra theo từng nhánh lựa chọn của bạn.
- kế đó là kết quả của các nhánh con. Kết quả của tầng 2.
Cũng bài #1, code thuần If-Then-Else ấy chính là cách code truth table.
Dựng truth table là phương pháp kinh điển nhất để gải quyết lô gic phức tạp.

Bài #2, #3, #5 bước qua một giai đoạn khác của truth table: chuyển đổi cái chọn lựa có-không (true-false) ra từng giai đoạn số liên tục để có thể dùng làm:
- Chỉ số chọn lựa trong mảng kết quả (mảng Array) > bài #2
- Phân từng khoảng giá trị để If-Then-Else > bài #3
- Nạp số cho hàm Choose và bảo nó lựa chọn. Cách này tương tự như bài #2 nhưng nạp cho Choose thì trông rõ, dễ đọc hơn Array.

Bài #6, tôi cũng dùng nguyên tắc giống như #2, #3, #5. Nhưng ở đây, tôi đưa ra thêm một thuật toán đặc biệt hay dùng vào thời máy tính còn rất giới hạn về bộ nhớ: thuật toán thâu ngắn bảng dò. Thuật toán này đòi hỏi người viết code phải quan sát để tìm phần unique của từng phần tử, và lập chuỗi dò (hoặc bảng dò) theo phần unique ấy thôi. Với ba trị "BUA BAO KEO" thì phần unique là "U A E" (ký tự thứ 2 của mỗi trị). Vì ở đây chỉ có 3 trị cho nên chẳng tiết kiệm được bao nhiêu, tôi cố tình đưa ra để giới thiệu thuật toán này.
Nếu đem áp dụng vào thuật toán dò tên tháng thì tiết kiệm rất nhiều. Tính duy nhất của mỗi tên tháng là ký tự 2 và 3 của chúng: "anebprayunulugectovec". Chuỗi dò rất hiệu nghiệm.

Khi phê bình bài #4, tôi cố tình đưa ra cho các bạn tập code ở mức trung bình đến khá: phân biệt khi nào dùng IF-Then-Els và khi nào thì Select-Case.

Bài #8 cũng đại khái theo giải thuật truth table, diễn giải thành nhiều dòng cho dễ hiểu.

Qua bài #11 thì là một biến thể của truth table. Theo lý thuyết, truth table đi theo tầng, và gồm tất cả các chọn lựa. Ở đây, tác giả bài #11 đã tìm được một lối đơn giản hoá các chọn lựa bằng cách đưa trường hợp đăc biệt (hoà) ra riêng. Như vậy, bảng truth table được tách ra phần (partition), phần thứ nhất gồm các trường hợp bằng nhau, và phần thứ hai gồm các trường hợp ăn thua. Với phương pháp phân phần này, code của table giản dị hơn vì bên phần ăn thua chỉ cần xét ăn thua vòng tròn mà không phải bận tâm với hoà.
Trong kỹ thuật xét "ăn thua vòng tròn" này, quý vị nên để ý là nó vòng lại 1 mức để uốn vòng tròn thành đường thẳng cho dễ xét. "baubaokeo" trở thành "buabaokeobua".

Nếu hiểu rõ bài này, quý vị cần học ở tình độ trung bình sẽ học được những kỹ thuật sau:
- truth table > if-then-else
- so sánh giữa if-then-else và select-case
- kỹ thuật chuyển đổi lô gic sang số để dùng làm chỉ số cho hàm Choose hay Array chọn lựa
- kỹ thuật tìm uniqu features để rút ngắn bảng dò
- kỹ thuật tách truth table thành phần (partition) để giản dị hoá công việc code.
- kỹ thuật nắn vòng tròn "ăn thua vòng" ra đường thẳng.

Nhất thời tạm kết là như vậy. Quý vị nào có gì cần thêm hay thắc mắc thì cứ cởi mở mà hỏi.
Xin lỗi là các kỹ thuật ơ bài này hơi cao đối với bậc bắt đầu.

Chú thích: bảng chân lý ngày xưa là bạn đồng hành của Pascal. Có lẽ vì Pascal dùng để dạy toán lô gic.
Tác giả bài #11 có lẽ chơi nhiều với Pascal (qua Delphi) cho nên mấy cái phân tích lô gic này trở thành phản ứng tự nhiên, và tìm ngay ra được cách giản dị hoá.

Chú thích 2: bài này lô gic chỉ qua 2 tầng cho nên bảng có thể tính được trong đầu. Gặp lô gic nhiều tầng hơn thì phải vẽ ra giấy mới thấy. Bây giờ với công cụ tân tiến, chuyện vẽ ra giấy có thể thực hiện trên Excel hay Word.
 
Bài #8 cũng đại khái theo giải thuật truth table, diễn giải thành nhiều dòng cho dễ hiểu.
Thì từ lâu tôi đã quan niệm viết cho dễ hiểu, dù cho viết "kiểu hàn lâm nhưng nông dân cũng hiểu"
* Viết hàm con & dùng Case thay cho If là hàn lâm, tôi học của 1 lão trên GPE, lão này thích case và hàm con
** Viết ngắn 1 dòng như bài #5 đối với người mới học sẽ là khó vì 1 câu lồng mấy lệnh: nối chuỗi, tìm kiếm, tính toán phép chia, lấy số nguyên, và chọn.
*** Bài #6 đề nghị sửa bài #5 bằng mảng, thì vẫn là 1 câu lồng 4, 5 lệnh.
...
Riêng bài 11 thì siêu phàm rồi :p
 
Một cách chuyển logic thành số
Mã:
Function OanhTuTi(ByVal ban$, ByVal may$)
  OanhTuTi = Choose((InStr("baobuakeobao", ban & may) > 0) + (ban = may) * 2 + 3, "Hoa", "Thang", "Thua")
End Function
 
Web KT
Back
Top Bottom