Đố vui về VBA!

Liên hệ QC

anhtuan1066

Thành viên gạo cội
Tham gia
10/3/07
Bài viết
5,802
Được thích
6,905
Nhằm cũng cố kiến thức về VBA cho các bạn mới bắt đầu và cả những bạn đang ứng dụng mà chưa hiểu nhiều về nó, tôi mở topic này với mong mõi qua những câu hỏi vui, các bạn sẽ nhận định lại sự hiểu biết cũa mình... (Kễ cã chính tôi cũng đang tập tành nên có rất nhiều cái chưa biết)
Mong rằng topic sẽ mang đến cho các bạn những khám phá thú vị với những cái tưỡng chừng như đã biết
Mong nhận dc bài viết về câu đố cũa các cao thủ! Còn các bạn mới thì đừng ngại khi đưa ra ý kiến cũa mình.. Có sai có sữa sẽ hoàn thiện!
Tôi xin mỡ màn trước bằng 1 câu hỏi đơn giãn
ANH TUẤN

CÂU HỎI 1: Tại sao biến K ko hoạt động?
Tôi muốn khi nhấn vào 1 button thì cell A1 sẽ tăng lên 1 đơn vị... Tôi đã làm như sau:
-Tạo 1 Command Button (nút nhấn thuộc thanh Control Toolbox), click phải chuột lên nút nhấn, chọn View code, rồi gõ vào đoạn code sau:
PHP:
Private Sub CommandButton1_Click()
   K = K + 1
   Range("A1").Value = K
End Sub
Ban đầu K chưa có gì, xem như =0, nhấn nút lần thứ nhất thì K dc tăng thêm 1, vậy K hiện tại sẽ bằng 1, và gán K vào cell A1 thì đương nhiên A1 sẽ =1... Nhấn nút lần 2, K lại dc tăng thêm 1 nên hiện tại K sẽ =2 và cell A1 cũng sẽ =2... vân vân.. từ đó diễn tiến tiếp...
Hi.. hi.. Điều này nghe qua có vẽ rất hợp lý, ấy thế mà khi nhấn nút nó chỉ hoạt động dc duy nhất 1 lần (A1 = 1) rồi thôi ko nhút nhít nữa...
Các bạn có thể giãi thích tại sao lại như thế ko? Tại sao những lần nhấn nút sau đó K lại ko tăng thêm tí nào (vì thực tế A1 vẫn cứ = 1 hoài) ?
ANH TUẤN
 
Xuân năm nay các Thầy/Cô làm biếng bài tập cho học sinh.
Giờ này GPE đối bài dữ.

Ra thử một đề bài đơn giản cho người mới tập cũng không đến nổi lúng túng:

Viết code liệt kê các ước số của mọt số nguyên.
1. muốn số nguyên thuần (Long) cũng được; mà muốn dùng số thực dạng nguyên (cho nó lớn hơn chút) cũng được. Miễn là biết cách xử lý số nguyên số thực nghiêm chỉnh.
2.muốn xuất kết quả ra kiểu nào tùy ý: mảng, chuỗi, hay trên bảng tính cũng được.
Tôi chưa biết phải xử lý số nguyên số thực thế nào nên tôi viết đơn giản thế này nhưng đến 1 tỷ thì nó chạy lâu quá
Rich (BB code):
Function UocSo(Src As Long)
Dim Res$, i&, k&

For i = 1 To Src
    If Int(Src / i) = Src / i Then
        Res = Res & i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
End Function
 
Upvote 0
Lần chỉnh sửa cuối:
Upvote 0
Tôi chưa biết phải xử lý số nguyên số thực thế nào nên tôi viết đơn giản thế này nhưng đến 1 tỷ thì nó chạy lâu quá
Rich (BB code):
Function UocSo(Src As Long)
Dim Res$, i&, k&

For i = 1 To Src
    If Int(Src / i) = Src / i Then
        Res = Res & i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
End Function
i là ước số của Src thì Src / i là ước số của Src
 
Upvote 0
i là ước số của Src thì Src / i là ước số của Src
Túm lại sẽ có 100 ước số của 1 tỷ. Có tốn giây nào đâu mà chậm.
Mã:
Function UocSo(Src As Long)
Dim Res, i
t = Timer
For i = 1 To Sqr(Src)
    If Src Mod i = 0 Then
        Res = Res & i & ";" & Src / i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
MsgBox Timer - t
End Function

1675655985864.png
 
Upvote 0
Túm lại sẽ có 100 ước số của 1 tỷ. Có tốn giây nào đâu mà chậm.
Mã:
Function UocSo(Src As Long)
Dim Res, i
t = Timer
For i = 1 To Sqr(Src)
    If Src Mod i = 0 Then
        Res = Res & i & ";" & Src / i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
MsgBox Timer - t
End Function

View attachment 286116
Function UocSo(100) có giá trị 10 xuất hiện 2 lần
 
Upvote 0
Tôi chưa biết phải xử lý số nguyên số thực thế nào nên tôi viết đơn giản thế này nhưng đến 1 tỷ thì nó chạy lâu quá
Rich (BB code):
Function UocSo(Src As Long)
Dim Res$, i&, k&

For i = 1 To Src
    If Int(Src / i) = Src / i Then
        Res = Res & i & ";"
    End If
Next
UocSo = Left(Res, Len(Res) - 1)
End Function
Cỡ trình độ bạn mà làm vầy thì cố hơi ỷ y. Suy tính lại chút xem :p
 
Upvote 0
Chú về code ở bài #1481:
Ước số lớn hơn a/2 thì là a. Do đó không cần đi hết đến a.
res = "1"
For i = 2 To Src/2
If Src Mod i = 0 Then
Res = Res & ";" & i & ";" & Src / i
End If
Next i
funcName = res & ";" & Src

Đấy là tôi chỉnh code của bạn. Nếu tính về hiệu quả thì côn toán Mod không hiệu quả lắm.

For i = 1 To Src
i2= Src \ i ' nếu số nguyên
' i2 = Fix(Src/i) ' nếu số thực
If i * i2 = Src Then
Res = Res & ";" & i
If i <> i2 Then Res = Res & ";" & i2
If i >= i2 Then Exit For
End If
Next i

Code này còn có thể cải tiến thêm 1 bước nữa. Có ai muốn thử xem?

Chú thích: bình thuonwgf thì để chặt bỏ đuôi một số thực, người lập trình viên dùng hàm Fix, một hàm cổ điến gắn liền với lập trình hơn nửa thế kỷ.
Điểm vượt trội của ham này so với Int là Fix luôn luôn chặt bỏ khúc thập phân, khong cần biết số là cái gì. Vì vaayj, ngwoif sử dụng hàm này luôn luôn được bảo đảm kết quả là "lấy phần nguyên, bỏ phần thập phân"

Chú 2: dùng số thực chỉ nới thêm độ lớn của số thôi. Nên ghi nhớ rằng số nào cũng bị giới hạn chính xác ở 15 chữ số.
 
Upvote 0
Upvote 0
Theo tôi thì chạy đến a/2 vẫn là nhiều. Tôi chỉ chạy đến căn bậc 2 của a thôi.
Nếu dùng điểm hội tụ (a/b >= b) như code thứ 2 thì không cần biết đến a/2 hay sqrt(a)

Điểm cải tiến là: nếu a là số lẻ thì ước số của a không thể là số chẵn..

For i = 1 To a Step IIF(a And 1, 2, 1)
...

Câu hỏi phụ:
Code tren cho ra từng cặp ước số, ước số nhỏ nhất rồi đến lớn nhất, kế đó là ước số nhỏ nhì rồi đến lớn nhì,....
Vậy code thế nào để sắp xếp kết quả nhỏ đến lớn?
 
Upvote 0
Vậy code thế nào để sắp xếp kết quả nhỏ đến lớn?
Do đầu bài chỉ yêu cầu liệt kê ở bất kỳ đâu (1), cộng với yêu cầu phụ là sắp xếp (ở bất kỳ đâu theo 1) nên tôi dùng 2 chuỗi kết quả: 1 chuỗi chứa b và 1 chuỗi chứa a/b.
- Chuỗi b đang tăng, không cần sắp lại
- Chuỗi a/b thì nối chuỗi ngược
Nếu yêu cầu liệt kê chung thì cuối code nối 2 chuỗi này với nhau
 
Lần chỉnh sửa cuối:
Upvote 0
Vòng lặp + cặp xuôi ngược ==> đệ quy

1675701744924.png

Code nhảy số chẵn:

1675702028798.png
 
Upvote 0
Nếu không phải hội tụ thì là phân kỳ :p :p. Chỉ 1 chuỗi và tự sắp xếp.
Mã:
Function UocSo(Src As LongPtr)
Dim Res, i As Long, sq
sq = Sqr(Src)
If sq = fix(sq) Then Res = sq
For i = sq - 1 To 1 Step -1
    If Src Mod i = 0 Then
        Res = i & IIf(Res = "", "", ";") & Res & ";" & (Src / i)
    End If
Next
UocSo = Res
End Function
 
Upvote 0
Giải thuật hay ở chỗ sử dụng đư của kiểu chuỗi.ợc tính chất "nối hai đầu" (double ended queue) của kiểu chuỗi.
(VBA không có các kiểu sử dụng con trỏ cho nên khó thực hiện các cấu trúc dữ liệu phức tạp hơn vậy)

Nên để ý tránh các chỗ tự ép kiẻu, khi dùng lẫn lộn số thực, số nguyên, và variant. Nếu cần ép kiểu thì ép thẳng rõ rệt.
VD
For i = sq - 1 To 1 Step -1
là ép kiểu ngầm, sq - 1 là số thực, gán vapf i thì nó ép kiểu thành Long
Src / i theo nguyên tắc là phép chia số thực, kết quả là số thực. Chỉ vì pử đây chia chẵn cho nên khong thấy lỗi.
 
Upvote 0
là ép kiểu ngầm, sq - 1 là số thực, gán vapf i thì nó ép kiểu thành Long (1)
Src / i theo nguyên tắc là phép chia số thực, kết quả là số thực. Chỉ vì pử đây chia chẵn cho nên khong thấy lỗi. (2)
(1) sửa thành For i = Fix(sq) - 1 to 1 Step -1
(2) Src / i là thực, nhưng chắc chắn nguyên vì nằm trong điều kiện If Src Mod i = 0 và đã thỏa
 
Upvote 0
Kết luận.
Khi đố câu ước số trên là tôi muốn chỉ dẫn các bạn mới lập trình hai điểm:
1. Về Toán
Tác giả bài #1481 đã sáng suốt nhận ra định lý số học "nếu b là ước số của a thì cũng hiện hữu một a/b cũng là ước số của a". Chỉ cần thêm trường hợp b = a/b thồi.
2. Về thuật toán
Thuật đệ quy cho phép vòng lặp đảo nghịch thứ tự kết quả.

Bài #1494 cho thấy một thủ thuật sử dụng chuỗi để thay thế cấu trúc xếp lượt 2 đầu.
Lúc đầu tôi định phê là thủ thuật này chỉ sử dụng được cho chuỗi chứ nếu đề bài đòi mảng thì té re. Nhưng kịp nghĩ lại là mình lầm. Chuỗi có thể dùng hàm split để tách thành mảng.

Chú thích:
Nhiều ngôn ngữ có kiểu con trỏ cho nên có những cấu trúc kiểu mà VBA không làm được:
- Kiểu ngăn xếp; LIFO
- Kiểu xếp hàng, FIFO
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT
Back
Top Bottom