Giới thiệu Cơ bản về vòng lặp For . . . next (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

ptm0412

Bad Excel Member
Thành viên BQT
Administrator
Tham gia
4/11/07
Bài viết
14,716
Được thích
37,418
Donate (Momo)
Donate
Giới tính
Nam
Nghề nghiệp
Consultant
Nhân có người bạn hỏi về For . . . next, nay mình xin đóng góp những gì mình biết để các bạn chưa biết xem qua.
Trong các ngôn ngữ lập trình mình biết: VBA, VB6, FoxPro, Pascal đều có các cấu trúc vòng lặp. Vòng lặp là 1 cấu trúc chương trình cho phép 1 câu lệnh hoặc 1 nhóm câu lệnh thực hiện 1 số lần có giới hạn. Giới hạn này có thể biết trước và có thể không, nhưng phải có để máy tính ngừng lại khi đủ số lần lặp ấn định trước. Giới hạn này có thể xác định bằng 1 con số cụ thể, 1 con số là kết quả của 1 phép tính, và cũng có thể là 1 điều kiện thoát ra khỏi vòng lặp.
Vòng lặp for là đơn giản và dễ sử dụng hơn so với while do vì nó giới hạn cụ thể số vòng lặp.
Thí dụ: for i = 1 to 10, for i = 1 to len(chuoiA), for i = 0 to k*2 . . .
Như vậy, dòng lệnh nào đặt giữa For và Next sẽ thực hiện n lần, kết quả của dòng lệnh đó sẽ bị thay đổi n lần. Kết quả sau lần thực hiện thứ n mới được dùng cho các dòng lệnh sau cấu trúc For này hoặc là kết quả cuối cùng.
Ta có nhận xét rằng sau 1 vòng, biến i tăng lên 1 cho đến khi bằng số lần quy định.
Vậy vấn đề căn bản của chúng ta là gì?

1. Xác định rằng bài toán phải thực hiện nhiều lần 1 phép tính mới ra kết quả.
2. Xác định số lần tính đó.
3. xác định câu lệnh nào để thực hiện sự tính toán.

THí dụ đơn giản nhất: tính giai thừa của 6:
ta biết n! = 1 x 2 x 3 x.... x n.

1. vậy là thích hợp để dùng For.
2. xác định số lần tính: ta thấy 6! có 5 bài toán nhân. Ta chọn số vòng lặp là n. ta viết for i =1 to 5
3. xác định câu lệnh thực hiện nhân:
a. Phải đặt 1 biến là kq
b. giá trị của kq là giá trị của kết quả trước đó nhân với giá trị hiện tại của i vì i tăng lên sau mỗi vòng lặp, ta lấy luôn i làm thừa số cho phép nhân.
Vậy ta có câu lệnh: kq = kq * i
Đến đây ta phải giả định rằng khi chạy vòng đầu tiên, có trục trặc gì không. Có. Có ở chỗ chưa có giá trị ban đầu của kq nên không nhân đưộc. vậy ta gán giá trị ban đầu của kq là 1:
ta viết kq = 1 ở bên trên For
Thứ hai ta giả định rằng sau 5 vòng lặp giá trị của kq là như thế nào. ta được kq = 1 * 1 * 2 * 3 * 4 * 5
số 1 đỏ là giá trị ban đầu, số 1 đen đến số 5 là 5 giá trị của i, nhân 5 lần là do ta quy định.
Không phải là 6! mà chỉ là 5!. vậy ta sửa lại For i = 1 to 6

Cuối cùng ta có vòng lặp hoàn chỉnh:

kq = 1
For i = 1 to 6
kq = kq * i
next i

Để ứng dụng bài tập này lên Excel, ta cần đưa nó vào giữa cặp Private sub và end sub. Mở 1 Worksheet mới, tại cell A1 gõ vào 1 số bất kỳ để tính giai thừa. Ta muốn kết quả nằm ở cell B1. Ta cũng muốn xem sau 1 vòng tính, giá trị của kq là bao nhiêu nằm lần lượt ở A2, A3, . . .
Bạn đừng chê cái ý muốn này (tính giai thừa trò trẻ ấy mà có gì mà xem), có ích đấy khi bạn thử ở những vòng lặp phức tạp hơn, hãy đi từ dễ đến khó.
Tạo 1 nút lệnh đặt tên là cmb1, double click vào cmb1 vào cửa sổ code chèn vào giữa sub và end sub để có 1 macro hoàn chỉnh như sau:

Private Sub Cmb1_click()
num=range("sheet1!A1").value
Range("sheet1!A1:A100").clear
kq = 1
For i = 1 to num
kq = kq * i
range("sheet1!A1").Offset(i,0).value = kq
next i
range("sheet1!B1").value = kq
end sub


Sau đó trở lại Excel, click nút lệnh xem kết quả.

chú ý range("sheet1!A1").Ofset(i,0).value = kq đặt bên trong For next nên chạy 6 lần hiện lên 6 cell, vị trí quy định bởi Offset

Còn range("sheet1!B1").value = kq đặt ngoài vòng For next nên chỉ chạy 1 lần hiện lên ở 1 cell B1.

Lần sau mình sẽ giới thiệu những thí dụ khác khó dần lên, rồi 2 vòng For lồng nhau.
 
Lần chỉnh sửa cuối:
Cảm ơn bạn... Chính tôi là người đang thắc mắc về mấy vòng lập này... Thức ra đễ hiểu nó cũng ko khó lắm (thậm chí có thể nói là quá dễ hiểu), nhưng giữa hiểu và đi đến vận dụng nhuần nhuyễn là 2 chuyện khác nhau... Vậy nên rất cần những bài tập nhỏ từ thấp lên cao đễ các bạn mới học như tôi có thể từ từ nắm vững dc cốt lõi vấn đề...
Mong rằng bạn post thêm nhiều bài hơn nữa về món này... (Có những bài tập cụ thể từ đơn giản đến.. vừa vừa... )
Chân thành cảm ơn!
ANH TUẤN
 
Upvote 0
Tôi chỉ viết được thế này thôi liệu có ra KQ như của bạn ko:
Sub Macro1()

kq = 1
For i = 1 To 6
kq = kq * i
Next i
Range("sheet1!B1").Value = kq
End Sub
Nếu dc thì những dòng lệnh # trong VD của bạn có ý nghĩa gì bạn giải thích hộ với ( trừ lệnh xóa a1:100)
 
Upvote 0
Cám ơn bạn , tất cả phải từ đơn giản đến phức tạp. bài toán lập trình phức tạp chẳng qua chỉ là tổng hợp khoa học những cái đơn giản...
 
Upvote 0
kq = 1
For i = 1 To 6
kq = kq * i
Next i
Range("sheet1!B1").Value = kq
End Sub
Có thể nói thêm 1 tí tại sao phải cần cho giá trị kq=1 ? Theo như tôi hiểu thì đó là nạp giá trị ban đầu... nhưng có 1 vài trường hợp tôi thấy các tác giả bỏ qua giai đoạn này luôn mà chạy thẳng vào vòng lập...
Bạn có thể giãi thích thêm và cho 1 vài ví dụ nào đó ko có giai đoạn nạp giá trị ban đầu ko?
ANH TUẤN
 
Upvote 0
anhtuan1066 đã viết:
Có thể nói thêm 1 tí tại sao phải cần cho giá trị kq=1 ? Theo như tôi hiểu thì đó là nạp giá trị ban đầu... nhưng có 1 vài trường hợp tôi thấy các tác giả bỏ qua giai đoạn này luôn mà chạy thẳng vào vòng lập...
Bạn có thể giãi thích thêm và cho 1 vài ví dụ nào đó ko có giai đoạn nạp giá trị ban đầu ko?
ANH TUẤN
Bạn thử bỏ kq=1 xem thế nào, nó hiểu kq là 0.
Private Sub ViDu()
num = Range("A1").Value
Range("A1:A100").Clear
'kq = 1
For i = 1 To 6
kq = kq + i
'kq = kq & Format(i, "00")
Range("A1").Offset(i, 0).Value = kq
Next i
Range("B1").Value = kq
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Trong ví dụ này thì tôi hiểu.. Ý tôi muốn tìm hiểu thêm 1 ví dụ nào đó ko cần nạp giá trị ban đầu... Các bạn hiểu ko nhỉ?
 
Upvote 0
Thì bác tính theo kiểu cấp số cộng
vd bác muốn tính tổng 1+2+...+n

vậy với For... next
bác dùng

For i = 1 to n do
kq = kq+i
next i

'xuất kết quả.

ở trên bác ko cần khởi động biến kq.

Thân.
 
Upvote 0
Uh, cảm ơn Soibien... Tôi hiểu rồi
Nhờ các bạn cho thêm 1 vài ví dụ nữa... từ thấp thấp lên đến tầm trung...
Cảm ơn trước
 
Upvote 0
Xin góp một í nho nhỏ, chớ phiền lòng!

ptm0412 đã viết:
PHP:
 Private Sub Cmb1_click()
    num=range("sheet1!A1").value
    Range("sheet1!A1:A100").clear
    kq = 1
    For i = 1 to 6
         kq = kq * i
        range("sheet1!A1").Ofset(i,0).value = kq
     next i
    range("sheet1!B1").value = kq
 end sub
Mình xin mạnh dạn góp í với PTN0412
Vì bạn truyền đạt kiến thức cho những đối tượng trình độ vỡ lòng hay cấp 1; Nên hướng dẫn cả cách tránh sai sót không đáng;
Ở đây thay vì như vậy, chúng ta nên khai báo biến kq, như sau
Dim kQ as long (Tại sao 1 chữ viết hoa, 1 chữ thường thì . . . . đã đề cập trong 1 bài nào đó hôm nay!)
Cũng như vậy, không nên khai biến i, mà nên iW (hay iZ, Ij) . . .
Những trường hợp này, nên chăng kèm tiếp đầu ngữ của loại/ kiểu biến
VD
Dim bI As Byte
Dim iK As Interger
Dim lW As Long
Dim dZj As Double
Có gì bỏ qua cho sự đường đột nha!!)*&^)
 
Lần chỉnh sửa cuối:
Upvote 0
ptm0412 đã viết:
Private Sub Cmb1_click()
num=range("sheet1!A1").value
Range("sheet1!A1:A100").clear
kq = 1
For i = 1 to 6
kq = kq * i
range("sheet1!A1").Ofset(i,0).value = kq
next i
range("sheet1!B1").value = kq
end sub
Xin lỗi vì đoạn trên có chỗ sai: ở dòng 5 xin sửa lại là : For i = 1 to num
Như vậy mới có nghĩa phù hợp với dòng 2 : lấy giá trị cho num ở cell A1.
Trong thí dụ thì tính 6! nhưng bạn có thể mở rộng ra tính cho số nguyên bất kỳ (đừng lớn quá, khoảng dưới 120)
Có nghĩa là bạn cho 1 số nguyên vào cell A1, nhấn nút lệnh, kết quả mỗi lần tính hiện ra ở A2, A3, . . ., kết quả cuối cùng hiện ra ở cell B1.
 
Upvote 0
SA_DQ đã viết:
Mình xin mạnh dạn góp í với PTN0412 (Không biết có phải tên bạn là Phan Tú Nam ? - Nhưng dù sao cũng cứ nói vậy;)

Phạm Thành Mỹ, nếu bạn muốn biết, 0412 là ngày và tháng sinh. Bạn không biết mình nhưng mình có biết bạn khi xưa trên diễn đàn Tuổi Trẻ.
SA_DQ đã viết:
Vì bạn truyền đạt kiến thức cho những đối tượng trình độ vỡ lòng hay cấp 1; Nên hướng dẫn cả cách tránh sai sót không đáng;
Mình không hiểu câu này? các dấu chấm phẩy làm mình bối rối.

SA_DQ đã viết:
Ở đây thay vì như vậy, chúng ta nên khai báo biến kq, như sau
Dim kQ as long (Tại sao 1 chữ viết hoa, 1 chữ thường thì . . . . đã đề cập trong 1 bài nào đó hôm nay!)
Cũng như vậy, không nên khai biến i, mà nên iW (hay iZ, Ij) . . .
Những trường hợp này, nên chăng kèm tiếp đầu ngữ của loại/ kiểu biến
VD
Dim bI As Byte
Dim iK As Interger
Dim lW As Long
Dim dZj As Double
Có gì bỏ qua cho sự đường đột nha!!)*&^)
Cám ơn bạn không hết, có gì mà bỏ qua. Có điều mình muốn các bạn khác tập trung vào For next nên lược giản lại, khai báo biến là vấn đề của cả project chứ không phải vấn đề của riêng For. Các thí dụ đơn giản thế này vẫn chạy bình thường chưa bị ảnh hưởng.
Thôi để mình đưa thêm thí dụ đây.
 
Upvote 0
Thí dụ cơ bản thứ hai về For

Thí dụ hôm nay cũng thuộc loại dễ: Đếm số từ trong 1 chuỗi.

1. Trước tiên bạn nghĩ về thuật toán để giải vấn đế này. Ta thấy mỗi từ ngăn cách nhau bằng 1 ký tự trống. Như vậy ta duyệt từng ký tự từ trái qua phải, nếu gặp ký tự trống thì tính là 1 từ cho đến hết chuỗi.
Như vậy phải loại trừ trường hợp do lỗi đánh máy có thể có 2, 3 ký tự trống giũa các từ. Muốn vậy ta dùng hàm trim() của Excel. Xin nói thêm rằng VBA cũng có hàm trim() nhưng trim của VBA chỉ loại những ký tự trắng ở 2 đầu chuỗi, còn trim của Excel loại luôn những ký tự trắng thừa ở giữa chuỗi. Muốn gọi hàm trim của Excel thì dùng
Mã:
Application.trim(chuoi)
Đặt tên biến cho chuỗi cần đếm từ là Mystr
Giả sử ta vẫn lấy giá trị từ cell A1 và cho kết quả vào cell B1.
Mã:
mystr = Range("sheet1!A1").Value
mystr = Application.Trim(mystr)
Range("sheet1!A2:d100").Clear

2. Xác định số lần duyệt: Duyệt từng ký tự đến hết thì số lần duyệt bằng với chiều dài chuỗi. Ta viết:
For i = 1 to len(mystr)

3. Xác định câu lệnh thực hiện:
- Đọc từng ký tự là mid(chuoi,vị trí, 1), vị trí ta có thể lấy biến i vì i tăng đúng số lần ta mong muốn
- Xét ký tự có phải ký tự trắng không là cấu trúc của câu lệnh có điều kiện if:
If mid(mystr,i,1)=" " then . .
- Nếu phải thì đếm thêm 1 từ: đặt một biến kq như thí dụ 1:
Kq = kq + 1
- Nếu không phải thì giữ nguyên kq:
Else
Kq = kq
End if
Vậy ta có vòng lặp đầy đủ như sau:

PHP:
For i = 1 to len(mystr)
If mid(mystr,i,1)=" " then 
Kq=kq+1
Else
Kq = kq
End if
Next i
Tương tự như thí dụ bài #1, bạn phải khai báo biến kq là integer hoặc gán cho kq giá trị ban đầu kq = 0.
Sau đó bạn đưa vào code hoàn chỉnh để chạy trên Excel, và giả sử bạn vẫn muốn xem kết quả từng lần chạy để nếu for chạy không đúng bạn có thể biết nó không đúng chỗ nào:
Bạn cần tạo 1 nút lệnh để thực hiện lệnh chạy giả sử có tên Cm2

Mã:
[COLOR=red]Private Sub Cm2_Click()[/COLOR]
[COLOR=red]mystr = Range("sheet1!A1").Value[/COLOR]
[COLOR=red]mystr = Application.Trim(mystr)[/COLOR]
[COLOR=red]Range("sheet1!A2:d100").Clear[/COLOR]
[COLOR=red]kq = 0[/COLOR]
[COLOR=red]For i = 1 To Len(mystr)[/COLOR]
[COLOR=red]If Mid(mystr, i, 1) = " " Then[/COLOR]
[COLOR=red]kq = kq + 1[/COLOR]
[COLOR=red]Else[/COLOR]
[COLOR=red]Kq = kq[/COLOR]
[COLOR=red]End If[/COLOR]
[COLOR=red]Range("sheet1!A1").Offset(i, 0).Value = kq[/COLOR]
[COLOR=red]Next i[/COLOR]
[COLOR=red]Range("sheet1!B1").Value = "Soá töø laø " & Str(kq)[/COLOR]
[COLOR=red]End sub[/COLOR]

Cho 1 chuỗi vào cell A1 rồi chạy thử. Hình như không đúng, thiếu 1 từ. Bạn hãy xem lại, lý do là chỉ khi gặp ký tự trắng, kq mới tăng giá trị lên 1, ký tự cuối chuỗi không phải ký tự trắng, nên kq không tăng cho từ cuối cùng. Vậy bạn có thể giải quyết bằng nhiều cách thí dụ: chọn 1 trong các cách:
- Thêm ký tự trắng vào cuối chuỗi: mystr = Range("sheet1!A1").Value & “ “
- Tăng kq lên 1 ở cuối chuỗi (ngoài vòng For, nếu trong vòng for sẽ bị tăng nhiều lần): kq=kq + 1
- Gán giá trị ban đầu kq = 1

Sau khi sửa xong bạn chạy lại, bảo đảm như ý.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Cho tôi hỏi:
-Tại sao phải cần Range("sheet1!A2:d100").Clear
-Tại sao phải cần Range("sheet1!A1").Offset(i, 0).Value = kq
Tôi đoán đây là dùng đễ liệt kê kq có dc khi i chạy.. Nhưng sao phải cần nó? Đưa kq vào thẳng B1 ko dc sao?
 
Upvote 1
anhtuan1066 đã viết:
Cho tôi hỏi:
-Tại sao phải cần Range("sheet1!A2:d100").Clear
-Tại sao phải cần Range("sheet1!A1").Offset(i, 0).Value = kq
Tôi đoán đây là dùng đễ liệt kê kq có dc khi i chạy.. Nhưng sao phải cần nó? Đưa kq vào thẳng B1 ko dc sao?
Sao bạn này hay bắt bẻ, bạn Mỹ (Hình như là his name) muốn cho bạn thấy kq sẽ là ...Muốn vậy thì phải clear trước. Còn không MsgBox kq.
Bạn ấy muốn HD cụ thể mà.
 
Upvote 0
Ko phải bắt bẽ.. trời đất ơi... Tôi ko hiểu thật mà... Ý tôi là tại sao phải cần có 2 đoạn code đó... Vì mục đích của ta là đưa kết quả vào cell B1, đúng ko? Vậy A2:D100 liên quan gì trong vụ này? Offset liên quan gì trong vụ này?
 
Upvote 0
anhtuan1066 đã viết:
Ko phải bắt bẽ.. trời đất ơi... Tôi ko hiểu thật mà... Ý tôi là tại sao phải cần có 2 đoạn code đó... Vì mục đích của ta là đưa kết quả vào cell B1, đúng ko? Vậy A2:D100 liên quan gì trong vụ này? Offset liên quan gì trong vụ này?
Tại vì mình muốn cho các bạn mới học biết được và thấy được kết quả sau mỗi vòng lặp. Trong thí dụ dễ này bạn thấy không cần, nhưng khi áp dụng vòng lặp phức tạp hơn, bạn cần biết vòng lặp bị lỗi ở chỗ nào.
Cụ thể như thí dụ đếm từ, chính là nhìn vào các cell A bạn thấy được đến vòng lặp cuối mới bị lỗi. bạn sẽ tập trung vào chỗ đó để tìm nguyên nhân.
Mình coi như thí dụ này là bài tập, sheet 1 là trang giấy nháp ấy mà.
Còn clear, là để bạn thử với nhiều số hoặc nhiều chuỗi, thử lần sau thì phải xóa kết quả lần trước cho khỏi lẫn lộn.
Mục đích thử nhiều lần là để phát hiện lỗi nếu có, nhất là các vòng lặp phức tạp có thể chỉ đúng trong phạm vi nhỏ mà sai ở phạm vi rộng hơn.
Hơn nữa, nếu có bạn nào quên đặt 1 câu lệnh đáng lẽ nằm ngoài For vào trong For, hoặc ngược lại, nhìn vào đó cũng phát hiện ra nguyên nhân.
Những điều này tớ đều ghi chú là nếu muốn nếu vẫn muốn, cảm phiền bạn xem kỹ lại.
Sau khi làm tốt rồi thì bỏ các dòng lệnh ấy đi. Anytime!
Mình đang soạn 1 bài for để đọc và tính kết quả 1 công thức dạng chuỗi thí dụ chuỗi "123+456-789" (không có dấu bằng). Mình phải làm nháp như vậy cả tuần nay mà chưa ra.
 
Lần chỉnh sửa cuối:
Upvote 0
Cảm ơn bạn... Giãi thích thế thì tôi hiểu rồi... (giống giống như khi tôi test công thức, thường hay dùng cột phụ đễ thử name và công thức mãng)... hi... hi... Rất thú vị... Tôi xem đây như là 1 cái mẹo...
Mong bạn post thêm nhiều bài khác nữa... Qua những ví dụ của bạn và các câu đố của anh SA_DQ, tôi đã tự làm dc 1 vòng lập FOR đầu tiên cho mình rồi.. ha.. ha.. Quá đã... Có thể các bạn chuyên gia trong lĩnh vực lập trình sẽ cười thầm "Ối dào, thế mà cũng mừng..." nhưng xin thưa: Cái mình chưa biết luôn là cái mới.. Và từ việc học, đến hiểu dc, rồi làm dc phải trải qua rất nhiều thử thách... Khi 1 sản phẩm đầu tiên ra lò, dù là sản phẩm ko có giá trị với mọi người đi chăng nữa, nhưng là chính tay ta làm ra nó mới cảm thấy thú vị biết dường nào... Và người dạy mình làm ra sản phẩm cũng sẽ thú vị ko kém, có phải ko? (tôi nghĩ vậy)
Một lần nữa chân thành cảm ơn sự trợ giúp của bạn!
Mến
ANH TUẤN
 
Upvote 0
Bài tập ôn

Các bạn đã xem qua 2 thí dụ, 1 về tính toán và 1 về xử lý chuỗi, tôi đã cố hướng dẫn chi tiết từng bước từ nghĩ ra thuật toán đến từng bước thực hiện, cả cách kiểm tra.
Hôm nay các bạn thử sức với 2 bài tập cũng đơn giản như vậy:

Bài 1: Cho 1 chuỗi tại cell A1, đảo ngược chuỗi từng ký tự một cho ra 1 chuỗi mới ở cell B1.

Bài 2: khó hơn 1 tí: Cho 1 chuỗi copy từ word sang gồm cả số (số nguyên thôi), dấu phân cách hàng ngàn (, hoặc . đều phải xử lý như nhau), lại còn kèm cả đơn vị, nhưng đồng VN thì ở cuối chuỗi, USD thì ở đầu chuỗi. vấn đề là bạn phải đổi thành dạng số để tính toán được trong Excel.
Gợi ý cho bạn nào làm chưa được: đọc chuỗi từng ký tự như bài #2 đến hết chuỗi, nếu là số từ 0 đến 9 thì kq = kq x 10 + trích, nếu là ký tự khác thì bỏ qua, kq = kq.

Sau dó chúng ta sẽ sang mức độ 2 là phải sử dụng kết quả trung gian.
 
Upvote 0
Câu 1:
Mã:
Sub DAO()
mystr = Range("sheet1!A1").Value
For i = 0 To Len(mystr)
Ch = Mid(mystr, Len(mystr) - i, 1)
Range("B1").Value = Range("B1").Value & Ch
Next i
End Sub
Tôi làm như trên nó ra kết quả đúng nhưng lại kèm theo lỗi "Invalid Procedure call or argument"
Là sao nhỉ? Sai ở chổ nào?
 
Upvote 0
Mức độ 2: phải dùng kết quả trung gian.

Sự đời không phải lúc nào cũng đơn giản, yêu cầu tính toán không phải lúc nào cũng ra ngay kết quả.
Đôi khi sau khi xử lý 1 phần, ta có kết quả 1, khi tính thêm 1 giai đoạn nữa, ta có kết quả 2, vân vân. Kết quả cuối cùng sẽ là tính toán hoặc xử lý các kết quả trung gian nói trên.
Trong vòng lặp For thường hay gặp trường hợp như vậy, đúng hơn là ta chọn for để xử lý 1 số trường hợp như vậy.
Thông thường trong lập trình người ta đặt những biến tạm để lưu trữ những kết quả trung gian đó, cuối cùng là xử lý giá trị các biến này.
Thí dụ bạn muốn đảo ngựoc chuỗi thành 1 chuỗi mới, nhưng không phải đảo từng ký tự mà là từng từ một; chẳng hạn như chuỗi "I and you love Excel" đảo ngược lại thành "Excel love you and I". Một thí dụ không có trong thực tế nhưng là 1 trướng hợp dùng biến tạm lưu giữ kết quả trung gian.
Trước tiên ta hãy nghĩ về thuật toán (bao giờ cũng thế). Hãy nhớ lại bài tập đếm từ trong chuỗi. Nếu là dự án khác, hãy nhớ về một bài nào đó tương tự mà chúng ta đã làm hoặc đã được xem đâu đó.
Trong bài đếm từ, ta duyệt chuỗi từ đầu đến cuối, khi gặp ký tự trắng là dấu hiệu cho biết là hết 1 từ. (nhớ trim chuỗi!). Vậy sao ta không lưu từ đó lại để dành? còn For thì cứ duyệt tiếp để có từ thứ 2, thứ 3?
Tiếp theo, khi có từng từ riêng lẻ thì ghép ngược thứ tự lại là ta có ngay đáp án.
Thực sự bạn không cần nhiều biến trung gian trong trường hợp này, mà nếu dùng mỗi biến lưu trữ 1 từ thì cũng không biết trước có bao nhiêu từ để mà định nghĩa biến. (vì ta muốn đảo từ 1 chuỗi bất kỳ trong cell A1). Ta có thể sử dụng 1 biến nhiều lần.
Sau đây là biện pháp:
Để có vẻ chuyên nghiệp hơn, ta định nghĩa 1 số biến trước khi làm bất cứ điều gì:

Dim Mystr as string
Dim lenght1 as integer
Dim trich as string
Dim kq as string
Dim kqtam as string

Sau đó gán kết quả ban đầu cho biến

Mystr = Application.Trim(range("sheet1!A1").value)
lenght1 = len(Mystr)
kg = ""
kqtam = ""


Tới xác dịnh số lần lặp:

For i = 1 to lenght1

Tới xác định câu lệnh:
- Trích từng ký tự:
trich = mid(Mystr,i,1)


- Nối dần các ký tự vào kqtam để có từ:

kqtam = kqtam & trich

- Nếu gặp ký tự trắng, lưu kqtam lại vào 1 chỗ, ở đây ta dùng luôn biến kq để lưu:
If trich = " " then
kq=kqtam

- Vậy thì nối từ thế nào? Ta nối luôn vào đầu kq trước đó, (khác với nối trich vào đuôi kqtam); sửa lại là:

If trich = " " then
kq = kqtam & kq


Tới đâyta giải phóng kqtam để xài lại cho từ kế tiếp

kqtam = ""

- Nếu không phải ký tự trắng thì duyệt tiếp, giữ nguyên các kết quả (Dùng Else hoặc khỏi cần Else vì Else có tính tùy biến có thể bỏ qua)
- Kết thúc điều kiện:

End If

- Kết thúc For:

Next i

-Đề phòng từ cuối bị bỏ sót:

kq=kqtam & kq

- Gán kết quả cuối cùng cho cell B1:

Range("sheet1!B1").value = kq

Giờ bạn nối các dòng màu đỏ lại là được 1 vòng lặp for hoàn chỉnh. Tất nhiên bạn vẫn có thể cho các dòng lệnh vào trong For để hiển thị các kết quả hoặc kểt quả tạm của mỗi vòng lặp lên các cell A2, A3, . . .; để xem và để tự kiểm tra, nếu muốn.
 
Upvote 0
anhtuan1066 đã viết:
Ah... phát hiện ra rồi... Tôi nhầm...
Lý ra phải là
For i = 0 To Len(mystr) -1
hi... hi...
Bạn cố thêm 1 tí nữa, không phải lỗi mà sơ sót ở đây là khi bạn nhấn nút lệnh nhiều lần hoặc chạy lại macro nhiều lần, kết quả cell B1 sẽ ngày càng dài ra khủng khiếp.
Nói thêm 1 chút về thuật toán. Mỗi người nghĩ ra 1 thuật toán khác nhau, đường nào cũng đến La mã nhưng có đường dài, đường ngắn; có đường bằng phẳng có đường gồ ghề. Bạn trích từ phải qua trái, còn tớ vẫn trích từ trái qua phải, có điều khi nối thì nối ngược. Bạn xem và so sánh thử.
Thân
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng rồi... tôi cũng có nghĩ ra cách tách chuổi từ trái sang phải... Hi.. hi... Vấn đề ở đây là thuật toán thế nào thôi... Khi đã có thuật toán, bắt đầu mới nghĩ đến chuyện tối ưu code, rồi "rào" các trường hợp gây lỗi, cuối cùng sẽ cho 1 code hoàn hảo... Đúng ko?
 
Upvote 0
Các Bạn thử tiếp bài 2 đi, rồi cho tớ biết kết quả cho tớ mừng.
Cả anhtuan1066 nữa, this topic is especially made for you as your demand.
Nhân tiện nhờ các bạn cao thủ tìm hoặc nghĩ ra vài thí dụ cho mức độ 2 giúp, tớ sắp cạn vốn rồi.
 
Lần chỉnh sửa cuối:
Upvote 0
Câu 2:
Mã:
Sub Tachso()
Range("B1").ClearContents
mystr = Range("A1").Value
For i = 1 To Len(mystr)
Tach = Mid(mystr, i, 1)
Select Case Tach
Case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Range("B1").Value = (Range("B1").Value & Tach) * 1
End Select
Next i
End Sub
Tạm thời tôi làm dc tới đây! Bạn xem thử có gì ko ổn.. Cảm ơn nhiều
ANH TUẤN
 
Upvote 0
anhtuan1066 đã viết:
Case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Câu trên bạn có thể dùng : Case 0 to 9
Ngắn hơn, tiết kiệm hơn. Phương châm là ngon, bổ, rẻ. Với lại khi dk từ 0 đến 50 cũng phải liệt kê hết như vậy sao?
Dấu phẩy liệt kê dùng trong trường hợp cách quãng thí dụ:
Case 0 to 9, 101 to 109

Hình như bạn rút kinh nghiệm bài trước, kết quả lần sau không còn nối vào kết quả lần trước nữa rồi ( dùng clear).
tuy nhiên, thông thường người ta không đưa kq ra hiển thị ngay để tiết kiệm thời gian, hiển thị kq lên là thêm 1 công đoạn phải làm, ngoài công đoạn nối và nhớ. Khi dùng vòng lặp for có nhiều vòng hơn, tốc độ sẽ chậm lại. Ngoài ra dùng biến ghi kết quả còn để dùng về sau, khi for chỉ là 1 bộ phận nhỏ của project.
Bạn có thể sẽ vượt qua mình đấy, sắp dốc túi rồi.
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn bạn về những bài học rất bổ ích...
Tôi xin đưa lên 1 ví dụ về việc trích xuất dữ liệu dựa vào 1 tiêu chí nào đó... Ở đây tôi cũng dùng FOR đễ quét tìm điều kiện
Bài toán là trích dự liệu bán hàng trong 1 tháng nào đó.. Bạn xem file và góp ý với...
Sub này chạy OK, tuy nhiên nếu tôi muốn dùng sự kiện WorkSheet_Change đễ phát hiện sử thay đỗi của Range("Sheet2!K8") thì phải làm sao? (chỉ khi K8 thay đỗi thì mới chạy Sub)
Tôi làm hoài vẫn có gì đó trục trặc... Dùng Intersect nó cứ trơ trơ ???
ANH TUẤN
 

File đính kèm

Upvote 0
Ái chà... sao mình ngu thế nhỉ? Assign Macro cho ComboBox liên kết với Sub Trich() là xong... Vậy mà nghĩ ko ra...
hi.. hi...
-----------------
Có 1 chuyện xin hỏi các cao thủ: Trong file này nếu chọn ComboBox từ Tháng 01 đến Tháng 11 thì ko có vấn đề... Nhưng nếu chọn Tháng 12 thì code chạy hơi lâu... tại sao vậy?
 
Lần chỉnh sửa cuối:
Upvote 0
Sub Tachso()
Range("B1").ClearContents
mystr = Range("A1").Value
For i = 1 To Len(mystr)
Tach = Mid(mystr, i, 1)
Select Case Tach
Case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Range("B1").Value = (Range("B1").Value & Tach) * 1
End Select
Next i
End SubBạn thử code trên chuyển thành Function Tachso(MyStr as string) cho pro
Áp dụng thử
If IsNumeric(tach) Then thế case 0 to 9
anhtuan1066 đã viết:
Cám ơn bạn về những bài học rất bổ ích...
Tôi xin đưa lên 1 ví dụ về việc trích xuất dữ liệu dựa vào 1 tiêu chí nào đó... Ở đây tôi cũng dùng FOR đễ quét tìm điều kiện
Bài toán là trích dự liệu bán hàng trong 1 tháng nào đó.. Bạn xem file và góp ý với...
Sub này chạy OK, tuy nhiên nếu tôi muốn dùng sự kiện WorkSheet_Change đễ phát hiện sử thay đỗi của Range("Sheet2!K8") thì phải làm sao? (chỉ khi K8 thay đỗi thì mới chạy Sub)
Tôi làm hoài vẫn có gì đó trục trặc... Dùng Intersect nó cứ trơ trơ ???
ANH TUẤN
Sao lại phải copy rồi paste, nó sẽ chạy chậm hơn là
Range("Sheet1!A" & i & ":E" & i).Copy
Range("Sheet2!A" & k).Select
ActiveSheet.Paste
Nên như thế này
Range("Sheet2!A" & k & ":E" & k).Value = Range("Sheet1!A" & i & ":E" & i).Value2
Bây giờ bạn thử tại sheet1 chạy code thử.
Nói chung là phải có chọn sheet và từ đâu thực thi công việc.
Chúc thành công. Vậy là quá giỏi rồi. Nếu là bắt đầu, tôi khuyên là nên khai biến đã.
 
Lần chỉnh sửa cuối:
Upvote 0
Cảm ơn ThuNghi... Đúng là nhanh thật... he... he... Chưa có kinh nghiệm mà, nên mới học từng chút từng chút 1 từ các bạn...
Cho hỏi đoạn code sau cùng tại sao là .Value2
Tôi sửa thành .Value thì nó vẫn chạy nhưng bị sai kết quả
Giãi thích giùm với
-----------------------------
Về món khai báo biến tôi vẫn lờ mờ lắm... các cao thủ cho thêm 1 vài ví dụ và có thể cho biết tại sao phải làm thế? Lợi ích gì?
Tôi phát hiện ra việc nghĩ ra thuật toán là tùy theo quan điểm của mỗi người, nhưng còn chuyện phát biểu thuật toán đó thành code thì có cái khó khăn... Với công thức bình thường ta còn nhận dc sự trợ giúp từ trong danh sách hàm: biết trong Excel có hàm nào, cách dùng ra sao... Còn với code, đễ biết điều tương tự phải tìm ở đâu?
Mong dc chỉ dẩn thêm!
ANH TUẤN
 
Lần chỉnh sửa cuối:
Upvote 0
The only difference between this property and the Value property is that the Value2 property doesn’t use the Currency and Date data types. You can return values formatted with these data types as floating-point numbers by using the Double data type.
Tôi cũng chả biết thế nào, cứ có đụng đến ngày là tôi dùng value2
Còn muốn xem hàm nào là VBA thì tại cửa sổ VBA nhấn F2, muốn help thì nhấn F1. Còn lợi ích của khai biến thì nhờ cao thủ hơn diễm đạt hộ.
 
Upvote 0
Tôi cũng chả biết thế nào, cứ có đụng đến ngày là tôi dùng value2
Nhưng sao nếu là .VALUE thì code chạy sai nhỉ? Chắc phải có cái gì đó khác nhau chứ... Nếu biết rõ hơn sẽ giúp mình chủ động hơn
 
Upvote 0
anhtuan1066 đã viết:
Còn với code, đễ biết điều tương tự phải tìm ở đâu?

ANH TUẤN
Trong cửa code, nhấn nút Object Browser trên tool bar, gõ từ cần tìm rồi search.
Hướng dẫn ngắn gọn nằm dòng dưới cùng giống như dòng status bar.
Chi tiết thì phải Help thôi.
 
Upvote 0
anhtuan1066 đã viết:
Cảm ơn ThuNghi... Đúng là nhanh thật... he... he... Chưa có kinh nghiệm mà, nên mới học từng chút từng chút 1 từ các bạn...
Cho hỏi đoạn code sau cùng tại sao là .Value2
Tôi sửa thành .Value thì nó vẫn chạy nhưng bị sai kết quả
Giãi thích giùm với
-----------------------------
Về món khai báo biến tôi vẫn lờ mờ lắm... các cao thủ cho thêm 1 vài ví dụ và có thể cho biết tại sao phải làm thế? Lợi ích gì?
Tôi phát hiện ra việc nghĩ ra thuật toán là tùy theo quan điểm của mỗi người, nhưng còn chuyện phát biểu thuật toán đó thành code thì có cái khó khăn... Với công thức bình thường ta còn nhận dc sự trợ giúp từ trong danh sách hàm: biết trong Excel có hàm nào, cách dùng ra sao... Còn với code, đễ biết điều tương tự phải tìm ở đâu?
Mong dc chỉ dẩn thêm!
ANH TUẤN

1) oh, hình như đ/k trong if của atuan... có vấn đề riêng với tháng 12?

If Month(Range("Sheet1!A" & i).Value) = Range("Sheet2!K8").Value Then
đó là lý do khi chọn tháng 12 chạy chậm,

Để kiểm tra điều này

cụ thể atuan sửa lại cái macro đó như sau:
Sub TRICH()
Range("Sheet2!A9:G2000").ClearContents
k = 9
For i = 9 To 2000
If Month(Range("Sheet1!A" & i).Value) = Range("Sheet2!K8").Value Then
'Range("Sheet1!A" & I & ":E" & I).Copy
'Range("Sheet2!A" & k).Select
'ActiveSheet.Paste
Range("Sheet2!A" & k & ":E" & k).Value = Range("Sheet1!A" & i & ":E" & i).Value2


Range("Sheet2!F" & k) = "Sheet1!A" & i
Range("Sheet2!G" & k) = Month(Range("Sheet1!A" & i).Value2)
k = k + 1
End If
Next i
Range("Sheet2!A9:G2000").Sort Key1:=Range("A9"), Order1:=xlAscending

Range("Sheet2!A8").Select
End Sub
mục đích là thêm thông tin phụ trong 2 cột F(giá trị là địa chỉ ô ở sheet1), và G (giá trị tháng giống trong đ/k if - atuan viết)

chạy macro này sẽ thấy ứng với tháng 12 mặc dù từ dòng 501,502, ...2000 ko có số liệu nhưng nó vẫn thỏa mãn đ/k - la wa - dù chạy với lệnh gán mới của Thu Nghi hay Copy kiểu atuan... thì vẫn bị

tigertiger cố tìm ra nguyên nhân nhưng chưa thấy sai ở đâu -lạ thật???

nhưng có thể do ô trống -> có thể khắc phục được bằng cách, chúng ta tìm ra vị trí hàng 500 đó là ngay mở đầu chương trình chúng ta thêm 2 lệnh này
Dim n As Integer
n = Range("Sheet1!A9").End(xlDown).Row

'và thay vị trí của 2000 trong for =n
For i = 9 To n
............

(để hiểu Range.End xem tại đây http://www.giaiphapexcel.com/forum/showpost.php?p=44326&postcount=5)
2) còn về Value2 thì tigertiger nghĩ về lý thuyết thì Value (ko có 2) thì vẫn đúng - trường hợp của atuan... sai có thể do số liệu gốc ở sheet1 chưa chuẩn hóa dữ liệu ngày, nhưng nếu dùng value2 thì sẽ cho tốc độ nhanh hơn (khi dữ liệu là dữ liệu liên quan ngày tháng, tiền tệ)

cụ thể, với dùng .Value thì VBA cố gắng đổi số liệu ở cell định dạng ngày chứa vào biến có kiểu VBA Date, và cũng như cố gắng đổi số liệu ở cell định dạng tiền tệ chứa vào biến có kiểu VBA Currentcy
trong khi đó .Value2 đổi số liệu ở cell định dạng ngày hay tiền tệ đều chứa vào biến có kiểu Double trong VBA

đây là dẫn chứng về điều này, tigertiger sưu tầm trên internet:
'NGuyên văn bằng English

It is faster (15-20%) to use the Range.Value2 property rather than the (default) Range.Value property. The Range.Value property attempts to convert cells formatted as Dates to a variant containg a VBA date type, and cells formatted as currency to a variant containing a VBA Currency type. Range.value2 attempts to convert date and Currency formatted cells into Variants containing Doubles.

3) Việc khai báo biến có ích sau atuan ah:
- Khi khai báo biến MT sẽ xác định biến - kiểu biến (integer, hay double, hay ...) đồng thời cấp phát bộ nhớ cho biến này - xđ đúng loại biến;
và dễ kiểm soát khi dữ liệu sai kiểu - do gán do nhập / đọc (khi đó CT sẽ
báo lỗi sai kiểu)
Vì thế Việc khai báo biến giúp qtr lập trình của chúng ta chuyên nghiệp hơn


mong nhận được góp ý
 
Lần chỉnh sửa cuối:
Upvote 0
Upvote 0
Thấy rồi! Từ A501 trở xuống là trống, nhưng code cứ lấy value hoặc value2, nghĩa là lấy month(A501) = month(0) = month(00/01/1900). Nhưng VBA lấy month khác kiểu Excel chăng? month(31/12/1899) ?????
Không hiểu nổi.
 
Upvote 0
Uh... cái vụ này tôi đã từng thấy khi lập công thức... Tôi cũng đã dự tính trước nên chỉ đễ ý phần tháng 1 (trong công thức thường hiểu làm cell trống là tháng 1)... Thế nhưng tháng 1 ko sai, mà tháng 12 lại sai nên tôi mất cảnh giác khúc này...
Nếu làm bài toán này bằng công thức thì tôi sẽ có 2 cách:
Cách 1: Cho thêm điều kiện (Month(Range)=12)*(Range<>"")
Cách 2: Tạo công thức tìm dòng cuối cùng có dử liệu
Với code này tôi nghĩ dùng thêm đoạn nhận biết End row nữa sẽ ko có vấn đề...
Mến
ANH TUẤN
 
Upvote 0
Rất có lý... Đã sửa xong, đưa lên cho bà con coi thử... File nhẹ ghê, chỉ 88K..hi..hi...
Tạm thời làm dc tới đó... Hy vọng sẽ có thể cải tiến thêm nữa nhờ vào sự trợ giúp nhiệt tình của các bạn... Tôi đang nghĩ đến 1 hướng có thể tăng tốc tính toán:
Thứ 1: Khi chạy code sẽ sort sheet 1 trước theo ngày tháng
Thứ 2: Khi gọi yêu cầu từ ComboBox, vòng lập chỉ cần quét nội trong tháng nào đó thôi (ko quét hết bảng tính) bằng cách tính Row đầu cuối thỏa mãn điều kiện tháng
Tôi đang phân vân: Nếu chạy code đễ sort sheet 1 thì hơi phí, lý ra chỉ sort khi nào có sự thay đỗi ở đây, nhưng nếu dùng sự kiện WorkSheeet_change trong sheet 1 thì rất có khả năng dẩn đến sai lầm (chẳng hạn khi dử liệu chưa nhập đủ 5 cột)... Vậy có cách nào nhận biết dc sự thay đổi dử liệu trong sheet 1, nhưng đừng làm gì cả, Chỉ khi gọi yêu cầu từ ComboBox thì mới sort... Tóm lại code sẽ làm việc như sau:
-Nếu sheet 1 dử liệu chưa thay đổi thì ko cần sort
-Nhận biết dc thay đổi trong sheet 1 nhưng nếu ta chưa gọi yêu cầu cũng ko cần sort
-Code sẽ sort khi chạy lần đầu tiên (tính từ lúc mở file) và sẽ sort tiếp nếu gọi yêu cầu mà trước đó có sự thay đỗi dử liệu ở sheet 1
Mong các bạn góp ý thêm!
Chân thành cảm ơn
ANH TUẤN
 

File đính kèm

Upvote 0
Ôi for ..... dành cho người mới bắt đầu của bạn PTM0412 đã dành cho người mưới bắt đầu được 1*n năm mất rồi....!
 
Upvote 0
Nguyễn Xuân Sơn đã viết:
Ôi for ..... dành cho người mới bắt đầu của bạn PTM0412 đã dành cho người mưới bắt đầu được 1*n năm mất rồi....!
Đó là lỗi của tớ, tớ không nghĩ ra thí dụ nên nhờ các bạn nghĩ hộ, tớ đang mong có nhiều thí dụ tương tự. Còn các bạn mới bắt đầu cứ theo dõi các bài tranh luận chung quanh thí dụ của họ, đối chiếu với phần mà tớ đã giới thiệu cũng rút ra được vô khối. Nếu 1 số câu lệnh không hiểu, bạn cũng hãy gắng tìm hiểu cấu trúc For các cao thủ làm, các nhắc nhở kèm theo của họ, cũng bổ ích lắm chứ. Bạn anhtuan1066 đấy, tự đặt thí dụ rồi làm, làm sai thì sửa, thế mới tiến bộ.

To everyone,
Rất cám ơn các vị cao thủ đã giúp cho topic này sôi nổi.
Nhờ quý bạn giúp cho 1 số thí dụ nữa, để cùng làm và cùng tranh luận.
Nhưng để cho khỏi loãng đề tài, xin quý bạn vui lòng cho thí dụ thuộc 1 mức độ nào đó trong các mức mà tôi có bài hướng dẫn, và nếu có thể, nói cho biết thí dụ dó thuộc mức độ nào nhằm giúp các bạn mới bắt đầu tiện theo dõi.
Một lần nữa xin cám ơn tất cả các bạn, mới bắt đầu cũng như đã bắt đầu n*1 năm!
 
Upvote 0
Hôm nay rất cám ơn sự tìm tòi của anhtuan, và sự giúp đỡ của 2 bạn tigertiger và PTM412, tôi học 1 điều rất quạn trọng khi viết code về ngày.
Còn xin góp ý code của anhtuan như sau:
Sub TRICH()
Range("Sheet2!A9:E500").ClearContents
k = 9
eR = Range("Sheet1!A8").End(xlDown).Row
For i = 9 To eR
If Month(Range("Sheet1!A" & i).Value) = Range("Sheet2!K8").Value And Range("Sheet1!B" & i).Value = Range("Sheet2!L8").Value Then
Range("Sheet2!A" & k & ":E" & k).Value = Range("Sheet1!A" & i & ":E" & i).Value2
k = k + 1
End If
Next i
Range("Sheet2!A9:E500").Sort Key1:=Range("A9"), Order1:=xlAscending
Range("Sheet2!A8").Select
End Sub
1/ Nên khai biến dim eR ...
2/ Nên là eR = Range("Sheet1!A5000").End(xlUp).Row
thay cho
eR = Range("Sheet1!A8").End(xlDown).Row sợ rằng tại sh 1 có cell để trống.
3/If Month(Range("Sheet1!A" & i).Value) = Range("Sheet2!K8").Value And Range("Sheet1!B" & i).Value = Range("Sheet2!L8").Value Then
Nên thêm
And Range("Sheet1!A" & i).Value <> "".
Nói tóm lại làm 1 if kiểm tra trước
If Range("Sheet1!A" & i).Value = "" then msgbox "...."
4/ Bạn để ý khi chọn tháng 12 và mặt hàng 1 thì định dạng ngày sai, nên chọn .value2. Còn không thì tách ra
Range("Sheet2!A" & k).Value = Range("Sheet1!A" & i).Value2
hay là
Range("Sheet2!A" & k) = dateserial(year(Range("Sheet1!A" & i)),month(Range("Sheet1!A" & i)),day(Range("Sheet1!A" & i)))

Range("Sheet2!B" & k & ":E" & k).Value = Range("Sheet1!B" & i & ":E" & i).Value
5/ Bạn để ý số đếm K cuối cùng, thay vì
Range("Sheet2!A9:E500").Sort
Bạn dùng
Range("Sheet2!A9:E" & k).Sort
Còn nhiều thủ thuật nữa mà cũng chưa nên biết, từ từ thôi. Với lại tôi cũng chưa biết.
 
Upvote 0
Mình cũng xin góp vài í với Trich()

PHP:
Sub TRICH()
 Range("Sheet2!A9:E500").ClearContents
 k = 9
 Er = Range("Sheet1!A8").End(xlDown).Row
 For i = 9 To Er
    If Month(Range("Sheet1!A" & i).Value) = Range("Sheet2!K8").Value And Range("Sheet1!B" _
        & i).Value = Range("Sheet2!L8").Value Then
        Range("Sheet2!A" & k & ":E" & k).Value = Range("Sheet1!A" & i & ":E" & i).Value
        k = k + 1
    End If
 Next i
 Range("Sheet2!A9:E500").Sort Key1:=Range("A9"), Order1:=xlAscending
 Range("Sheet2!A8").Select
End Sub

1'/ Nên khai báo tất cả các biến sẽ sử dụng;
Nên có dòng Option Explicit Dòng lệnh này trên phần Declarations của CS VBA
2'/ Không nên khai báo biến chỉ là 1 ký tự, tại vì, lúc cần thay thế tên biến này sẽ sinh chuyện ngay . . . Thêm nữa, trong tên biến nên có cả chử hoa & chử thường, để VBA giúp ta kiểm soát lỗi chính tả khi nhập tên biến trong các dòng lệnh; Tuy đã khai như vậy, nhưng lúc nhập ta cứ nhập chử thường (hay chữ hoa); VBA sẽ sửa cho ta; Cái nào nó không chịu sửa là mình sai chính tả!
3'/ Các câu lệnh nên thục đầu dòng giống phương án ['php]. . . ['/php] nêu trên, để dễ bề kiểm soát thừa thiếu :
* Hay thừa các từ If, For . . . , Select Case
* Ngược với nó là thiếu các từ : End If, End Select, Next . . .
** Riêng với For . . . Next thì nên Next <Biến lặp>
VD: Next ij hay Next Rng
4'/ Tên Sub cũng nên có cả chữ hoa lẫn chữ thường; để khi gọi nó (như gọi biến nêu trên) ta không sai chính tả một cách oan mạng!

Tất nhiên điều này là không bắt buộc, nhưng là điều nên làm, nhất là những người mới bắt đầu chinh phục VBA; (Cũng như ngược lại ta không nên đi bộ bằng cách 1 chân đi trên vĩa hè, một chân dưới lòng đường, tuy không ai cấm!)

+++ Ở đây chúng ta nghiên cứu vòng lặp, chứ bài toán này nên dùng Adv Filter thì hay hơn!
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn tất cả mọi người về những góp ý bổ ich..
Nhân đây tôi xin nêu 1 bài toán thuộc loại dễ:
Mã:
Cho 1 số tự nhiên tùy ý, hảy tìm số chính phương lớn nhất nhỏ hơn hoặc bằng với số đã cho
(Tôi nghĩ bài toán này ko nên quét số chạy i mà nên quét theo bình phương của i đễ tiết kiệm thời gian)
Các bạn cho ý kiến với
Mã:
Sub TimsoCP()
So = Range("A1").Value
For i = 1 To So
    If i * i <= So And (i + 1) * (i + 1) > So Then
    Range("B1").Value = "So can tim la " & i
    Range("C1").Value = "Binh phuong cua no la " & i * i
    End If
Next i
End Sub
ANH TUẤN
 
Lần chỉnh sửa cuối:
Upvote 0
anhtuan1066 đã viết:
Cám ơn tất cả mọi người về những góp ý bổ ich..
Nhân đây tôi xin nêu 1 bài toán thuộc loại dễ:
Mã:
Cho 1 số tự nhiên tùy ý, hảy tìm số chính phương lớn nhất nhỏ hơn hoặc bằng với số đã cho
(Tôi nghĩ bài toán này ko nên quét số chạy i mà nên quét theo bình phương của i đễ tiết kiệm thời gian)
Các bạn cho ý kiến với
Mã:
Sub TimsoCP()
So = Range("A1").Value
For i = 1 To So
    If i * i <= So And (i + 1) * (i + 1) > So Then
    Range("B1").Value = "So can tim la " & i
    Range("C1").Value = "Binh phuong cua no la " & i * i
    End If
Next i
End Sub
ANH TUẤN

Em xin đóng góp

PHP:
Function TimsoCP(So As Double) As Double
    Application.Volatile (False)
    TimsoCP = Int(Application.WorksheetFunction.Power(So, 1 / 2))
End Function

Còn của bác thì chỉ cần :
PHP:
Sub TimsoCPT()
    Dim i As Double
    So = Range("A1").Value
    i = Int(Application.WorksheetFunction.Power(So, 1 / 2))
    Range("B1").Value = "So can tim la " & i
    Range("C1").Value = "Binh phuong cua no la " & i * i
End Sub


Thân!
 
Upvote 0
Có lẽ Anh Tuấn cũng đã biết hàm Power() của Excel, nhưng bạn ấy muốn cho thí dụ về For trong chủ đề này. Rất cám ơn Anh Tuấn và mọi người.

anhtuan1066 đã viết:
(Tôi nghĩ bài toán này ko nên quét số chạy i mà nên quét theo bình phương của i đễ tiết kiệm thời gian)
ANH TUẤN
To Anh Tuấn:
Rối cuộc bạn vẫn dùng For quét từ 1 đến So, đâu phải quét theo i^2. Trong trường hợp này (nếu vẫn dùng For) quét từ 1 đến Power(So, 1/2) mới là tiết kiệm.
 
Upvote 0
Rối cuộc bạn vẫn dùng For quét từ 1 đến So, đâu phải quét theo i^2. Trong trường hợp này (nếu vẫn dùng For) quét từ 1 đến Power(So, 1/2) mới là tiết kiệm
Đồng ý... Thì do chưa rõ phải viết thế nào nên mới gữi lên nhờ mọi người... Tôi biết chắc quét theo i^2 là ngon nhất nhưng ko biết "nói" như thế nào cho thằng Excel nó hiểu (ko biết cú pháp)... hi... hi...
Bạn xem còn ví dụ gì thì post lên tiếp nhé... (tạm thời tôi chưa nghĩ ra)
Cảm ơn...
ANH TUẤN
 
Upvote 0
Hồi nãy vội quá nên chưa kịp nói hết ý, Bạn muốn tiết kiệm thời gian thì tìm cách giới hạn số vòng lặp lại là 1 trong các cách. Lấy thí dụ của bạn có 2 cách giới hạn số vòng lặp:
1. Làm như tớ đã nói: For i = 1 to Application.Power(So,1/2)
2. Trong vòng lặp bạn đã có đk if để lấy ngay kết quả, vậy bạn đặt luôn 1 câu lệnh trong đk if để thoát ra ngoài vòng lặp.

Cụ thể là nếu So = 7, For vẫn là For i = 1 to 7
nếu không có đk thoát ra thì quá trình làm việc của For có thể diễn giải như sau:
i = 1: không thoả
i = 2: thỏa --> nhận kết quả
i = 3: không thoả
i = 4 đến i = 7 cũng không thoả.
như vậy For xách xe không chạy 5 vòng cuối. Nếu So = 100, For chạy không 90 vòng (90% thời gian).

Khi bạn cho lệnh thoát vào điều kiện thì quá trình ngắn lại:
i = 1: không thoả
i = 2: thỏa --> nhận kết quả --> thoát

Câu lệnh thoát hình như là Exit Sub, để tớ kiểm tra lại rồi trả lời bạn.
Biện pháp này rất có ích trong nhiều trường hợp khác.
 
Lần chỉnh sửa cuối:
Upvote 0
Xin mạn phép các bạn đang tham gia chủ đề này, tớ trích của các bạn 1 số gợi ý và lời khuyên, tổng hợp lại cho các bạn khác đang làm quen với VBA. Các đoạn trích theo thứ tự bài viết, không theo mức độ quan trọng, vì tớ thấy tất cả quan trọng như nhau.
Trích:
Nguyên văn bởi anhtuan1066
Case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9


Câu trên bạn có thể dùng : Case 0 to 9
Ngắn hơn, tiết kiệm hơn. Phương châm là ngon, bổ, rẻ. Với lại khi dk từ 0 đến 50 cũng phải liệt kê hết như vậy sao?
Dấu phẩy liệt kê dùng trong trường hợp cách quãng thí dụ:
Case 0 to 9, 101 to 109


Bạn thử code trên chuyển thành Function Tachso(MyStr as string) cho pro
Áp dụng thử
If IsNumeric(tach) Then thay thế cho case 0 to 9



Trích:
Range("Sheet1!A" & i & ":E" & i).Copy
Range("Sheet2!A" & k).Select
ActiveSheet.Paste Sao lại phải copy rồi paste, nó sẽ chạy chậm hơn là

nên như thế này

Trích:
Range("Sheet2!A" & k & ":E" & k).Value = Range("Sheet1!A" & i & ":E" & i).Value2

Range("Sheet2!F" & k) = "Sheet1!A" & i
Range("Sheet2!G" & k) = Month(Range("Sheet1!A" & i).Value2)
mục đích là thêm thông tin phụ trong 2 cột F(giá trị là địa chỉ ô ở sheet1), và G

2) còn về Value2 thì tigertiger nghĩ về lý thuyết thì Value (ko có 2) thì vẫn đúng - trường hợp của atuan... sai có thể do số liệu gốc ở sheet1 chưa chuẩn hóa dữ liệu ngày, nhưng nếu dùng value2 thì sẽ cho tốc độ nhanh hơn (khi dữ liệu là dữ liệu liên quan ngày tháng, tiền tệ)

cụ thể, với dùng .Value thì VBA cố gắng đổi số liệu ở cell định dạng ngày chứa vào biến có kiểu VBA Date, và cũng như cố gắng đổi số liệu ở cell định dạng tiền tệ chứa vào biến có kiểu VBA Currentcy
trong khi đó .Value2 đổi số liệu ở cell định dạng ngày hay tiền tệ đều chứa vào biến có kiểu Double trong VBA

đây là dẫn chứng về điều này, tigertiger sưu tầm trên internet:

Trích:
'NGuyên văn bằng English

It is faster (15-20%) to use the Range.Value2 property rather than the (default) Range.Value property. The Range.Value property attempts to convert cells formatted as Dates to a variant containg a VBA date type, and cells formatted as currency to a variant containing a VBA Currency type. Range.value2 attempts to convert date and Currency formatted cells into Variants containing Doubles.

3) Việc khai báo biến có ích sau atuan ah:
- Khi khai báo biến MT sẽ xác định biến - kiểu biến (integer, hay double, hay ...) đồng thời cấp phát bộ nhớ cho biến này - xđ đúng loại biến;
và dễ kiểm soát khi dữ liệu sai kiểu - do gán do nhập / đọc (khi đó CT sẽ
báo lỗi sai kiểu)
Vì thế Việc khai báo biến giúp qtr lập trình của chúng ta chuyên nghiệp hơn


1'/ Nên khai báo tất cả các biến sẽ sử dụng;
Nên có dòng Option Explicit Dòng lệnh này trên phần Declarations của CS VBA
2'/ Không nên khai báo biến chỉ là 1 ký tự, tại vì, lúc cần thay thế tên biến này sẽ sinh chuyện ngay . . . Thêm nữa, trong tên biến nên có cả chử hoa & chử thường, để VBA giúp ta kiểm soát lỗi chính tả khi nhập tên biến trong các dòng lệnh; Tuy đã khai như vậy, nhưng lúc nhập ta cứ nhập chử thường (hay chữ hoa); VBA sẽ sửa cho ta; Cái nào nó không chịu sửa là mình sai chính tả!
3'/ Các câu lệnh nên thục đầu dòng giống phương án ['php]. . . ['/php] nêu trên, để dễ bề kiểm soát thừa thiếu :
* Hay thừa các từ If, For . . . , Select Case
* Ngược với nó là thiếu các từ : End If, End Select, Next . . .
** Riêng với For . . . Next thì nên Next <Biến lặp>
VD: Next ij hay Next Rng
4'/ Tên Sub cũng nên có cả chữ hoa lẫn chữ thường; để khi gọi nó (như gọi biến nêu trên) ta không sai chính tả một cách oan mạng!

Tất nhiên điều này là không bắt buộc, nhưng là điều nên làm, nhất là những người mới bắt đầu chinh phục VBA; (Cũng như ngược lại ta không nên đi bộ bằng cách 1 chân đi trên vĩa hè, một chân dưới lòng đường, tuy không ai cấm!)

Xin cám ơn mọi người. Xin thứ lỗi nếu tớ trích sót ý kiến của bạn nào đó.
 
Lần chỉnh sửa cuối:
Upvote 0
Một bài tập cho mức độ 2

Có một người tình cờ sưu tầm được 1 lá thư tình của 1 nhân vật nổi tiếng. Anh ta đọc và thấy khá nhiều lần ông này gọi người yêu là "darling" và anh muốn biết cụ thể có bao nhiêu lần từ này được sử dụng trong bức thư trên.
Yêu cầu: Theo bạn thì dùng For như thế nào để giúp anh chàng tò mò kia?

Ghi chú: Khi làm xong và chạy thử bạn có thể dùng 1 đoạn văn bất kỳ để thử, nhưng nhớ là hoặc tiếng Anh, hoặc tiếng Việt không dấu. TỪ cần tìm có thể tùy chọn bằng cách lấy từ cần tìm tại 1 cell nào đó trên bảng tính. Lúc đó có thể xài tiếng Việt có dấu.
Mời các bạn tham gia.

Ghi chú: Các bạn mới học vẫn có thể gởi thắc mắc hoặc khó khăn khi thực hiện bất kỳ bài nào, do sưu tầm hoặc do nghĩ ra, hoặc trong thực tế, mà bạn cho rằng có thể sử dụng For mà chưa làm được, vào topic này. Tớ nghĩ mọi người sẽ giúp các bạn.
 
Upvote 0
Giờ ta làm bài toán theo điều kiện dễ nhất: Chuổi nằm trọn trong 1 cell
Mã:
Sub Tim()
Chuoi = UCase(" " & Range("A1").Value) 'Them khoang trang dau chuoi
Tu = UCase(Range("A2").Value) 'Tu can tim
Dai = Len(Chuoi) - 1 'Do dai chuoi
For i = 2 To Dai
   If Mid(Chuoi, i - 1, 1) = " " And Mid(Chuoi, i, Len(Tu)) = Tu Then
          k = k + 1
          Range("A3").Value = k
   End If
Next i
End Sub
Tôi nghĩ nếu tìm chuổi trong nhiều cell thì chỉ cần lồng thêm 1 vòng lập nữa nằm bên ngoài, quét từ A1 đến An nào đó (ko biết đúng ko)
Mến
ANH TUẤN
 
Lần chỉnh sửa cuối:
Upvote 0
anhtuan1066 đã viết:
Mã:
Sub Tim()
Chuoi = UCase(" " & Range("A1").Value) 'Them khoang trang dau chuoi
Tu = UCase(Range("A2").Value) 'Tu can tim
Dai = Len(Chuoi) - 1 'Do dai chuoi
For i = 2 To Dai
   If Mid(Chuoi, i - 1, 1) = " " And Mid(Chuoi, i, Len(Tu)) = Tu Then
          k = k + 1
          Range("A3").Value = k
   End If
Next i
End Sub

Tuấn thử với hàm InStr(Num,String1,String2) xem sao; sẽ nhanh hơn, mình nghỉ vậy!
Tham khảo bài tìm & tô màu các chữ í!
 
Upvote 0
Cải tiến thêm 1 chút... Tô màu chử cần tìm cho dễ nhìn thấy:
Mã:
Sub Tim()
Chuoi = UCase(" " & Range("A1").Value) 'Them khoang trang dau chuoi
Tu = UCase(Range("A2").Value) 'Tu can tim
Dai = Len(Chuoi) - 1 'Do dai chuoi
Range("A1").Font.ColorIndex = Automatic
For i = 2 To Dai
   If Mid(Chuoi, i - 1, 1) = " " And Mid(Chuoi, i, Len(Tu)) = Tu Then
          k = k + 1
          Range("A1").Characters(Start:=i - 1, Length:=Len(Tu)).Font.ColorIndex = 3
          Range("A3").Value = k
   End If
Next i
End Sub
Các bạn góp ý thêm với (tôi cảm giác có gì đó ko ổn trong thuật toán này)
ANH TUẤN
 

File đính kèm

Upvote 0
SA_DQ đã viết:
Tuấn thử với hàm InStr(Num,String1,String2) xem sao; sẽ nhanh hơn, mình nghỉ vậy!
Tham khảo bài tìm & tô màu các chữ í!
Hàm này dùng đễ làm gì? Anh Sa giới thiệu sơ cho em hiểu với
Và còn 1 câu hỏi nữa... Mình dùng FOR... NEXT... giã sử ngay câu lênh Next i em ko muốn cho i nhảy theo bước 1 mà muốn "nhảy cóc" đến 1 giá trị nào đó thì phải làm sao? Ví dụ đầu tiên i = 1.. tiếp theo "đụng" điều kiện nào đó thì nhảy lên 5 đơn vị chẳng hạn, lúc này i sẽ ko tăng lên 2 mà tăng thành 6... Có vụ đó ko?
ANH TUẤN
 
Upvote 0
anhtuan1066 đã viết:
Giờ ta làm bài toán theo điều kiện dễ nhất: Chuổi nằm trọn trong 1 cell
Mã:
Sub Tim()
Chuoi = UCase(" " & Range("A1").Value) 'Them khoang trang dau chuoi
Tu = UCase(Range("A2").Value) 'Tu can tim
Dai = Len(Chuoi) - 1 'Do dai chuoi
For i = 2 To Dai
   If Mid(Chuoi, i - 1, 1) = " " And Mid(Chuoi, i, Len(Tu)) = Tu Then
          k = k + 1
          Range("A3").Value = k
   End If
Next i
End Sub
Tôi nghĩ nếu tìm chuổi trong nhiều cell thì chỉ cần lồng thêm 1 vòng lập nữa nằm bên ngoài, quét từ A1 đến An nào đó (ko biết đúng ko)
Mến
ANH TUẤN

Em góp với bác nhé :

PHP:
Function TimChuoi(Chuoi As String, CanTim As String, Optional KyTu As String)
    Application.Volatile (False)
    Dim i1 As Integer, i As Integer, k As Integer
    i1 = Len(CanTim)
    Chuoi = " " & Chuoi
    For i = 1 To Len(Chuoi) - i1
        If Mid(Chuoi, i, 1) = KyTu Or Len(KyTu) = 0 Then
            If Mid(Chuoi, i + 1, i1) = CanTim Then k = k + 1
    End If: Next
    TimChuoi = k
End Function

Optional KyTu As String : Khi tìm ký tự bất kỳ thì không khai báo cũng được, còn khi tìm một từ thì Kytu chính là ký tự đặc biệt ngăn cách giữa các từ (bình thường là khoảng trắng), khi đó việc tìm kiếm sẽ nhanh hơn.

Thân!
 
Upvote 0
Cảm ơn Bắp... Nhưng món For còn chưa đi tới đâu thì món Function còn lâu tôi mới tiêu hóa nỗi... Giờ chỉ có nước lưu code của Bắp lại... từ từ tìm hiểu sau... Hic...
ANH TUẤN
 
Upvote 0
anhtuan1066 đã viết:
Hàm này dùng đễ làm gì? Anh Sa giới thiệu sơ cho em hiểu với
Và còn 1 câu hỏi nữa... Mình dùng FOR... NEXT... giã sử ngay câu lênh Next i em ko muốn cho i nhảy theo bước 1 mà muốn "nhảy cóc" đến 1 giá trị nào đó thì phải làm sao? Ví dụ đầu tiên i = 1.. tiếp theo "đụng" điều kiện nào đó thì nhảy lên 5 đơn vị chẳng hạn, lúc này i sẽ ko tăng lên 2 mà tăng thành 6... Có vụ đó ko?
ANH TUẤN
1/ Help của hàm InStr()
Returns a Variant (Long) specifying the position of the first occurrence of one string within another.

Syntax

InStr([start, ]string1, string2[, compare])

The InStr function syntax has these arguments:

Part Description
start Optional. Numeric expression that sets the starting position for each search. If omitted, search begins at the first character position. If start contains Null, an error occurs. The start argument is required if compare is specified.
string1 Required. String expression being searched.
string2 Required. String expression sought.
compare Optional. Specifies the type of string comparison. If compare is Null, an error occurs. If compare is omitted, the Option Compare setting determines the type of comparison. Specify a valid LCID (LocaleID) to use locale-specific rules in the comparison.



Settings

The compare argument settings are:

Constant Value Description
vbUseCompareOption -1 Performs a comparison using the setting of the Option Compare statement.
vbBinaryCompare 0 Performs a binary comparison.
vbTextCompare 1 Performs a textual comparison.
vbDatabaseCompare 2 Microsoft Access only. Performs a comparison based on information in your database.



Return Values

If InStr returns
string1 is zero-length 0
string1 is Null Null
string2 is zero-length start
string2 is Null Null
string2 is not found 0
string2 is found within string1 Position at which match is found
start > string2 0

Remarks

The InStrB function is used with byte data contained in a string. Instead of returning the character position of the first occurrence of one string within another, InStrB returns the byte position.

2/ Tuấn coi lại bài mình đố Tuấn cộng đó;
Nó chuyển từ 51 đến ngay 79 còn gì!
 
Upvote 0
anhtuan1066 đã viết:
Cảm ơn Bắp... Nhưng món For còn chưa đi tới đâu thì món Function còn lâu tôi mới tiêu hóa nỗi... Giờ chỉ có nước lưu code của Bắp lại... từ từ tìm hiểu sau... Hic...
ANH TUẤN
Thì em cũng dùng For đó chứ.

Của bác nên là :

PHP:
Sub Tim()
    Dim Chuoi As String, Tu As String
    Dim Dai1 As Integer, Dai2 As Integer
    Chuoi = UCase(" " & Range("A1").Value) 'Them khoang trang dau chuoi
    Tu = UCase(Range("A2").Value) 'Tu can tim
    Dai1 = Len(Chuoi) 'Do dai chuoi
    Dai2 = Len(Tu) 'Do dai chuoi
    For i = 1 To Dai - Len(Tu)
       If Mid(Chuoi, i, Len(Tu)) = Tu Then
            k = k + 1
            Range("A3").Value = k
       End If
    Next i
End Sub

Thân!
 
Upvote 0
Chạy đua tốc độ không BAB?

PHP:
Option Explicit

Sub DemTu()
 Dim Cau As String, Tu As String
 Dim SoTu As Integer, iJ As Integer, BDau As Integer
 
 Cau = UCase$(Cells(4, 1)):   Tu = UCase$(Cells(2, 2))
 For iJ = 1 To 99
    BDau = InStr(BDau + 1, Cau, Tu)
    If BDau < 1 Then
        Exit For
    Else
        SoTu = 1 + SoTu
    End If
 Next iJ
 MsgBox Str(SoTu)
End Sub
 
Upvote 0
Ah... tôi đễ ý thấy các cao thủ hay dùng mấy hàm MID, Ucase có thêm ký tự $.. như Mid$ hoặc Ucase$ ... Nó nghĩa là gì vậy? Sao tôi ko xài dấu $ mà nó vẫn chạy?

Tôi áp dụng vòng lập FOR cho việc tô màu chử theo điều kiện... Code chạy tốt, tuy nhiên khi tôi kết hợp với ComboBox đễ lấy chuổi cần tìm từ nó thì chẳng hiểu sao nó lại ko cập nhật kịp thời... Ví dụ tôi gõ chử Q vào ComboBox, tôi phải gõ chử Q thêm lần thứ 2 thì mới thấy code chạy
Các bạn xem file và sửa giùm với!
ANH TUẤN
 

File đính kèm

Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
anhtuan1066 đã viết:
Tôi áp dụng vòng lập FOR cho việc tô màu chử theo điều kiện... Code chạy tốt, tuy nhiên khi tôi kết hợp với ComboBox đễ lấy chuổi cần tìm từ nó thì chẳng hiểu sao nó lại ko cập nhật kịp thời... Ví dụ tôi gõ chử Q vào ComboBox, tôi phải gõ chử Q thêm lần thứ 2 thì mới thấy code chạy
Các bạn xem file và sửa giùm với!
ANH TUẤN

Sai chỗ này:
Mã:
Tu = UCase(Range("F1").Value) 'Tu can tim
Range("F1").Value = ComboBox1
dòng 1 biến Tu lấy giá trị cũ từ F1, dòng 2 mới cập nhật F1 từ ComboBox1. Nên đảo thứ tự 2 câu trên lại để lấy giá trị thay đổi từ ComboBox trước khi ghi vào biến Tu:
Mã:
Range("F1").Value = ComboBox1
Tu = UCase(Range("F1").Value) 'Tu can tim
 
Upvote 0
Cảm ơn... chạy dc rồi... nhưng còn 1 rắc rối nữa đối với Number... Ví dụ gõ vào số 5 nó tô màu tầm bậy chẳng đúng gì cả...
Lý do tại sao nhỉ?

ôi... tôi thử kỹ rồi... Hình như ko có giãi pháp cho trường hợp cell là Number thì phải... Thí nghiệm bằng cách gõ vào 1 cell nào đó số 123456 chẳng hạn thì ko có cách nào tô màu từng số dc.. ví dụ tô kiểu 123456
Chỉ còn có nước chuyển toàn bộ dử liệu cái nào là Number sang Text... Nhưng điều này lại gây khó khăn cho việc tính toán... Hic...
ANH TUẤN
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
ThuNghi đã viết:
Lá thư này viết trện Excel? Text Box hay là?
Vậy viết for... trên word?
Các Bác làm em xấu hổ quá. Lâu lâu nổi hứng văn chương bậy bạ bị bắt gặp. Cứ viết như thế này là được rồi:
Cho 1 câu văn dài ở cell A1 và 1 từ ở cell A2, đếm....
Rủi ai có lá thư thật trong word mà không biết copy paste vào thì mình mang tội với trời đất.
 
Upvote 0
Tôi áp dụng vòng lập FOR cho việc tô màu chử theo điều kiện... Code chạy tốt, tuy nhiên khi tôi kết hợp với ComboBox đễ lấy chuổi cần tìm từ nó thì chẳng hiểu sao nó lại ko cập nhật kịp thời... Ví dụ tôi gõ chử Q vào ComboBox, tôi phải gõ chử Q thêm lần thứ 2 thì mới thấy code chạy
Các bạn xem file và sửa giùm với!
- oh, sao atuan... ko dùng textbox mà lại dùng commbobox nhỉ??? chỉ khi có nhiều giá trị lựa chọn thì ng ta mới dùng combobox

- atuan... làm thế này, ng khác học for cơ bản làm sao theo kịp ... hic

- Ah function thì cũng tương tự sub thôi, nhưng nó tiện lợi là trả về 1 giá trị và có thể nhập các tham số cho Function, và có ở thường ở cuối trước "End Function" có lệnh gán <tên hàm"> =<giá trị>, thế thôi,
đơn giản và tiện ích vì Function luôn động với dữ liệu (giống hàm của Excel) trong khi đó sub chỉ thực hiện khi chạy.

Cho 1 dãy số thực n phần tử (được chứa vào các ô của 1 cột - chẳng hạn 10 số từ A1,A2,...,A10).
Tìm 3 phần tử liên tiếp mà có tổng là max nhất (tổng 3 phần tử này)?


Vd dãy số 3 4 5 8 1 6 7 6 5 2
kQ: 3 phần tử cần tìm là 6 7 6 có tổng =19

Mở rộng: Tìm 5,7,...,hay k phần tử liên tiếp (k<n - nhập từ 1 ô nào đó) có tổng max

các BẠN thử giải xem
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Bài của TigerTiger tôi giãi như sau:
Mã:
Sub TongMax()
Dim Tong As Integer
Dim Tong1 As Integer
Dim Ki As Integer
For i = 1 To 8
    Tong1 = Cells(i, 1).Value + Cells(i + 1, 1).Value + Cells(i + 2, 1).Value
    If Tong < Tong1 Then
       Tong = Tong1
       Ki = i
    End If
Next i
Range("B1").Value = "3 so lien tiep can tim la"
Range("C1").Value = Cells(Ki, 1).Value
Range("C2").Value = Cells(Ki + 1, 1).Value
Range("C3").Value = Cells(Ki + 2, 1).Value
Range("B4").Value = "Tong la"
Range("C4").Value = Tong
End Sub
-------------------
Với bài mở rộng tôi đang mường tượng sẽ có thêm 1 vòng lập nữa quét theo K đễ tính TONG1... nhưng tạm thời chưa nghĩ ra dc giãi thuật.. chắc cũng ko đến nỗi khó lắm nhỉ? Hi.. hi..
ANH TUẤN

Tôi làm dc bài toán dạng tổng quát rồi, ko cần thêm vòng lập gì cả (bám vào điều kiện số liên tiếp)
PHP:
Sub TongMax()
Dim Tong As Integer
Dim Tong1 As Integer
Dim Ki As Integer
Dim Soo As Integer
Dim Er As Integer
Soo = Range("D1").Value
Er = Range("A1000").End(xlUp).Row
Columns("C:C").ClearContents
Range("B1").ClearContents
If Soo > Er Then
MsgBox "Nhap so nho hon " & Er
Range("D1").ClearContents
Exit Sub
End If
For i = 1 To Er - Soo + 1
Tong1 = Application.WorksheetFunction.Sum(Range("A" & i & ":A" & i + Soo - 1))
If Tong < Tong1 Then
Tong = Tong1
Ki = i
End If
Next i
Range("B1").Value = "Tong can tim la " & Tong
Range("C1:C" & Soo).Value = Range("A" & Ki & ":A" & Ki + Soo - 1).Value
End Sub
File đính kèm ở đây! Mọi người xem thử có gì ko ổn ko? Tôi test thấy chạy tốt và chưa phát hiện lỗi nào cả!
Mến
ANH TUẤN
 

File đính kèm

Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Oh, atuan... giải nhanh quá

chỉ có mấy ý kiến góp ý với a thôi:

1) Đoạn lệnh sau
anhtuan1066 đã viết:
PHP:
 For i = 1 To Er - Soo + 1
Tong1 = Application.WorksheetFunction.Sum(Range("A" & i & ":A" & i + Soo - 1))
If Tong < Tong1 Then
Tong = Tong1
Ki = i
End If
Next i
Giá trị của biến Tong chưa được tính / gán, vì thế nó sẽ có giá trị mặc định sau khi khai báo là 0. Vậy giả định nếu dãy số của chúng ta có các số âm hay đặc biệt toàn âm tưc là khi đó có thể Tong1 dù max nhất cũng <0 thì khi đó kết quả sẽ ko có ->sai vì đ/k Tong < Tong1 ko xảy ra. Giải pháp cho vấn đề là chúng ta nên tính Tong trước có nghĩa là trước lệnh vòng For thêm dòng lệnh sau, và chỉ số của vòng for sẽ bắt đầu từ 2:
PHP:
 Tong1 = Application.WorksheetFunction.Sum(Range("A1:A" & Soo))
For i = 2 To Er - Soo + 1
  <........>
2)

anhtuan1066 đã viết:
....., ko cần thêm vòng lập gì cả (bám vào điều kiện số liên tiếp)
tigertiger thiết nghĩ: Nên dùng vòng for để tính Tong1 để tính cho tổng quảt; Thường thì để chương trình nhanh hơn chúng ta nên hạn chế số lần đọc số liệu từ ô (cell) của bảng tính. trong Sub của atuan thì sẽ những ô sẽ phải truy xuất dữ liệu đến Soo lần, vì thế nên:
+ tạo 1 biến mảng -> đọc số liệu dãy số từ các cell chỉ một lần vào biến mảng này -> rồi chương trình xử lý biến mảng này
+ Dĩ nhiên khi đó không dùng được hàm SUM của Excel nữa -> ta phải dùng thêm 1 vòng FOR tính tổng (đang nói về for - chúng ta cũng cố gắng dùng for chứ...)
VD:
PHP:
Dim DaySo() as Double

<...........>

'sau khi xác định được Er -> khai báo độ lượng thực của mảng DaySo

ReDim DaySo(Er)

'Nhập dãy số từ các ô cho mảng
For i = 1 To Er
   DaySo(i) = Range("A" & i).Value
Next i
và đoạn tìm tổng max
PHP:
Tong = 0
For j = 1 To Soo
    Tong = Tong + DaySo(j)
Next j
Ki = 1
   
For i = 2 To Er - Soo + 1
    Tong1 = 0
    For j = i To i + Soo - 1
        Tong1 = Tong1 + DaySo(j)
    Next j
    
    If Tong < Tong1 Then
       Tong = Tong1
       Ki = i
    End If
Next i
và đoạn hiển thị KQ

PHP:
Range("B1").Value = "Tong can tim la " & Tong
i = 0
For j = Ki To Ki + Soo - 1
    i = i + 1
    Range("C" & i).Value = DaySo(j)
Next j
---------------------------
Nội dung đầy đủ, để các bạn có thể ứng dụng lun:

PHP:
Sub TongMax()
Dim Tong As Integer
Dim Tong1 As Integer
Dim Ki As Integer
Dim Soo As Integer
Dim Er As Integer
Dim i As Integer, j As Integer

Dim DaySo() As Double

Soo = Range("D1").Value
Er = Range("A1000").End(xlUp).Row

Columns("C:C").ClearContents
Range("B1").ClearContents

If Soo > Er Then
    MsgBox "Nhap so nho hon " & Er
    Range("D1").ClearContents
    Exit Sub
End If

ReDim DaySo(Er)
For i = 1 To Er
   DaySo(i) = Range("A" & i).Value
Next i

Tong = 0
For j = 1 To Soo
    Tong = Tong + DaySo(j)
Next j
Ki = 1
   
For i = 2 To Er - Soo + 1
    Tong1 = 0
    For j = i To i + Soo - 1
        Tong1 = Tong1 + DaySo(j)
    Next j
    
    If Tong < Tong1 Then
       Tong = Tong1
       Ki = i
    End If
Next i

Range("B1").Value = "Tong can tim la " & Tong
i = 0
For j = Ki To Ki + Soo - 1
    i = i + 1
    Range("C" & i).Value = DaySo(j)
Next j

End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Uh... đúng... tôi sơ ý cái vụ số âm... cảm ơn Tigertiger đã nhắc nhở... (ra dc tới đó là lùng bùng lỗ tai lắm rồi.. hi.. hi...)
Mong bạn góp thêm nhiều bài tập nữa cho tôi và các bạn mới học có điều kiện thực tập..
Mến
ANH TUẤN
 
Upvote 0
anhtuan1066 đã viết:
Uh... đúng... tôi sơ ý cái vụ số âm... cảm ơn Tigertiger đã nhắc nhở... (ra dc tới đó là lùng bùng lỗ tai lắm rồi.. hi.. hi...)
Mong bạn góp thêm nhiều bài tập nữa cho tôi và các bạn mới học có điều kiện thực tập..
Mến
ANH TUẤN
oh, viết vậy để atuan... chú ý : nếu chất bài này ứng dụng vào chọn số liệu mưa 3 ngày max, 5 ngày max, ... thì chắc chắn số liệu mưa này là >=0 tất.

Nhưng khi viết Chương trình thì chúng ta phải chú ý tất cả các trường hợp, hi hi hi
 
Lần chỉnh sửa cuối:
Upvote 0
Giải bài tập của bạn Tiger (tạm gọi là BT2 mức đô 2)

Giải bài tập của bạn Tiger tớ gợi ý là dùng biến kiểu mảng.
Nói thêm 1 chút:
Biến kiểu mảng là biến có dạng: Bien(1 to n) với n là 1 hằng cụ thể. Biến này có n giá trị lần lượt Bien(1) , Bien(2) ... Bien(n).
Cao cấp hơn có biến mảng 2 chiều Bien(1 to n, 1 to m) với m, n là các hằng cụ thể. Biến 2 chiều thích hợp để xử lý bảng tính m dòng và n cột. Tương tự là biến nhiều chiều sử dụng trong lĩnh vực khác mà theo trính độ của mình là hơi trừu tượng.
Ở bài tập của Ban Tiger, mình đưa luôn trường hợp tổng quát của dãy số vào (chưa có trường hợp tổng quát của số cell liên tiếp cần tính tổng):
- Giá trị cần tính nằm ở cột A bắt đầu là A2 xuống ước chừng dưới 100 dòng. nếu nhiều thì ước nhiều hơn.
- số cell liên tiếp để tính tổng là 3.
Mã:
[COLOR=black]Sub Tongmax()[/COLOR]
[COLOR=black]' Khai báo biến[/COLOR]
[COLOR=black]Dim Sok(1 to 100) , Tam , Sum3 as Single[/COLOR]
[COLOR=black]Dim Rcell(1 to 3) , nRow as Integer[/COLOR]
[COLOR=black]Dim Add(1 to 3) as String[/COLOR]
[COLOR=black]' Gán giá trị cho biến[/COLOR]
[COLOR=black]Sum3 = 0[/COLOR]
[COLOR=black]nRow=Range("A2").End(xldown).Row()[/COLOR]
[COLOR=black]For k = 1 to nRow[/COLOR]
[COLOR=black]Sok(k) = Range("A1").Offset(k,0).Value[/COLOR]
[COLOR=black]Next k[/COLOR]
[COLOR=black]' Phần code[/COLOR]
[COLOR=black]For 1 = 1 to nRow - 2 ' Giảm được 2 vòng lặp, nếu là m thì giảm m-1 vòng lặp[/COLOR]
[COLOR=black]Tam = Sok(i) + Sok(i+1) + Sok(i+2)[/COLOR]
[COLOR=black]If Tam > Sum3 Then[/COLOR]
[COLOR=black]Sum3 = Tam[/COLOR]
[COLOR=black]Rcell(1) = i + 1[/COLOR]
[COLOR=black]Rcell(2) = i + 2[/COLOR]
[COLOR=black]Rcell(3) = i + 3[/COLOR]
[COLOR=black]' Hoặc nếu số cell tình tổng là m lấy từ 1 cell trên bảng tính ([/COLOR][COLOR=red]m = Range("B1").Value[/COLOR][COLOR=black]):[/COLOR]
[COLOR=black]' For j = 1 to m[/COLOR]
[COLOR=black]' Rcell(j) = i + j[/COLOR]
[COLOR=black]' Next j[/COLOR]
[COLOR=black]End If[/COLOR]
[COLOR=black]Next i[/COLOR]
 
[COLOR=black]' Địa chỉ các cell đã tìm được:[/COLOR]
[COLOR=black]Add(1) = Range("A" & Rcell(1)).Address[/COLOR]
[COLOR=black]Add(2) = Range("A" & Rcell(2)).Address[/COLOR]
[COLOR=black]Add(3) = Range("A" & Rcell(3)).Address[/COLOR]
[COLOR=black]' Hoặc nếu số cell tình tổng là m lấy từ 1 cell trên bảng tính ([/COLOR][COLOR=red]m = Range("B1").Value[/COLOR][COLOR=black]):[/COLOR]
[COLOR=black]' For iX = 1 to m[/COLOR]
[COLOR=black]' Add(iX) = Range("A" & Rcell(iX)).Address[/COLOR]
[COLOR=black]' Next iX[/COLOR]
 
[COLOR=black]' Đánh dấu chọn các cell vừa tìm được (hoặc tô màu tùy bạn)[/COLOR]
[COLOR=black]Range(Add(1) &":" & Add(m)).Select[/COLOR]
[COLOR=black]Range("B2").Value = "Tong lon nhat là " & Sum3 [/COLOR]
 
[COLOR=black]End Sub[/COLOR]
Tớ đã dự trù có thể mở rộng ra số cell cần tính tổng là cho trước ở 1 cell B1, các dòng lệnh tổng quát là các dòng có dấu nháy; nhưng lại bí ở dòng tính tổng Sum3 = Sok(1) + Sok(2) +... + Sok(m). Nhờ các bạn góp ý cho theo hướng này.
 
Lần chỉnh sửa cuối:
Upvote 0
Oh! Post bài lên rồi mới đọc kỹ lại, Bài #85 của Tiger không những giống mà còn hay hơn bài mình, nhất là phần ReDim. Chí mình bé may gặp chí lớn rồi.
Nhân tiện nhờ Tiger chỉ hộ, mình khai báo biến mảng Dim Sok(1 to nRow) thì không được, nhưng bạn khai báo Dim Sok(nRow) lại được là lý do gì?
 
Lần chỉnh sửa cuối:
Upvote 0
ptm0412 đã viết:
...chí mình bé gặp chí lớn ...
........
Nhân tiện nhờ Tiger chỉ hộ, mình khai báo biến mảng Dim Sok(1 to nRow) thì không được, nhưng bạn khai báo Dim Sok(nRow) lại được là lý do gì?

* oh ptm0412 - khiêm tốn quá, tigertiger cũng đang học hỏi thôi,

* Cách khai báo như của ptm0412 là khai báo mảng tĩnh (tức là cố định số phần tử)

Cách khai báo như của tigertiger là khai báo mảng động (tức là số phần tử mảng chỉ được xác định khi dùng lệnh ReDim)

PHP:
Dim <ten mang>() As <kiểu dữ liệu>   ' chưa xác định số p.tử

         'sau đoạn lệnh chúng ta xác định số phần tử chính xác rồi 
         'thì mới dùng lệnh ReDim
         
         ReDim <ten mang>( <biến lưu số phân tử> )
Sơ sơ như vậy chắc bạn hiểu,
 
Lần chỉnh sửa cuối:
Upvote 0
Giải bài tập đếm từ trong thư (BT1 mức độ 2) bằng biến tạm

Mặc dù bài Đếm từ Darling trong bức thư tình các bạn khác đã giải quyết xong nhưng vì lỡ ra bài trong mức độ 2 là dùng biến tạm ,nay tớ gởi lên bài giải theo hướng này để các bạn mới bắt đầu học theo kịp.

Mã:
Sub Demtu()
Mystr = LCase(Trim(Range("A1").Value))
Word = LCase(Trim(Range("A2").Value))
Kq = 0
L1 = Len(Mystr)
For i = 1 To L1
Trich = Mid(Mystr, i, 1)
Tam = Tam & Trich
If Trich = " " Then
If Trim(Tam) = Word Then
Kq = Kq + 1
Tam = ""
Else
Tam = ""
End If
End If

Next i
Range("B1").Value = "So tu " & Word & " la " & Kq
End Sub

Lưu ý: Các bạn thấy biến tạm được sử dụng lại nhiều lần, trước khi dùng lại phải giải phóng nó: Tam = "".
 
Upvote 0
Bài tập FOR tiếp theo

bài B3 (mức độ 2):

Cho 1 dãy số nguyên (được chứa vào các ô của 1 cột - chẳng hạn 10 số từ A1,A2,...,A10).
Tìm phần tử max của dãy số


Vd dãy số 18 15 5 88 70 16 45 26 25 30
kQ: max = 88 tại vị trí 4 trong dãy

* Tương tự bài toán tìm min của dãy



và bài sau là tìm max thỏa mãn đ/k,

bài B4 (mức (mức độ 2):

Cho 1 dãy số nguyên (được chứa vào các ô của 1 cột - chẳng hạn 10 số từ A1,A2,...,A10).
Tìm phần tử max (và cả vị trí của nó) trong các phần tử thỏa mãn điều kiện nào đó - chẳng hạn điều kiện chia hết cho 3 và chia hết cho 5


Vd dãy số 18 15 5 88 70 16 45 26 25 30
kQ: max = 45 tại vị trí 7 trong dãy


* Tương tự bài toán tìm min trong các phần tử thỏa mãn đ/k....



Đây là các ví dụ cơ bản ứng dụng FOR và IF hy vọng đáp ứng cho những người mới tìm hiểu FOR và IF - dĩ nhiên là chúng ta có thể dễ dàng tìm max, min bằng hàm công thức của Excel - nhưng đôi khi trong 1 công đoạn nào đó của Chương trinhg VBA chúng ta phải xác định điều này. và trên hết đây là mục đích hiểu ứng dụng FOR và IF hơn,


**
MỞ RỘNG

+ Nếu có đ/k chúng ta có thể Mở Rộng hơn (B3m, B4m): các bài này cho MA TRẬN - bảng 2 chiều -có m hàng n cột (max/min và max/min trong các phần tử thỏa mãn đ/k) - khi đó sd 2 vòng FOR lồng nhau rất hấp dẫn.

+ Bên cạnh đó ta cũng thể tự nghĩ ra các đ/k hay hơn (mong các bạn sáng tạo)



mong góp ý kiến và hoàn thiện, Thanks a lot!

 
Lần chỉnh sửa cuối:
Upvote 0
Tạm thời giãi bài 4 như sau:
PHP:
Sub Solonnhat()
Dim Er As Integer
Dim Max As Integer
Dim chia1 As Integer
Dim chia2 As Integer
Dim Sochia As Integer
Er = Range("A1000").End(xlUp).Row
Max = Range("A1").Value
chia1 = Range("B2").Value
chia2 = Range("B3").Value
For i = 1 To Er
Sochia = Cells(i, 1).Value
If Sochia Mod chia1 = 0 And Sochia Mod chia2 = 0 And Max <= Cells(i, 1).Value Then
Max = Cells(i, 1).Value
End If
Next i
If Max Mod chia1 <> 0 Or Max Mod chia2 <> 0 Then
MsgBox "Khong co so nao thoa dieu kien"
Range("B1").ClearContents
Exit Sub
End If
Range("B1").Value = Max
End Sub
Hic... hic... Giờ lại thêm cái món ReDim gì gì đó.. nhức đầu quá... Học như chạy giặc...
 
Upvote 0
atuan... làm nhanh nhỉ, mới chỉ góp ý nhỏ sau:

PHP:
 Max = Range("A1").Value
lệnh này ko ổn aTuan... ah, vì giả sử phần tử đầu tiên không thỏa thỏa mãn đ/k đề bài và có giá trị rất lớn - lớn hơn tất các phần tử còn lại -> khi đó đ/k sau k bao giờ xảy ra vì Max <= Cells(i, 1).Value luôn là false
PHP:
 If Sochia Mod chia1 = 0 And Sochia Mod chia2 = 0 And Max <= Cells(i, 1).Value Then
-> KQ sẽ sai (KQ là "Khong co so nao thoa dieu kien")

Để dễ hỉu bài B4 chúng ta xem vd công việc cụ thể tương tự như sau: chẳng hạn 1 trường ĐH dán điểm thi ĐH (điểm thi đầu vào) tại bảng tin (có đông thí sinh hơn 1000 ) , bây giờ cần tìm thí sinh có quê ở Quảng Ngãi mà điểm cao nhất (max - cao nhất trong những thí sinh cùng quê Quảng Ngãi) -> vậy ta phải làm thế nào?

ta sẽ
+ lần lượt dò tìm từ Thí sinh (TS) đầu tiên trong danh sách nếu gặp TS ko phải quê Quảng Ngãi (QN) thì bỏ qua, đến khi gặp TS có quê là Quảng Ngãi (QN) thì ghi nhận điểm của TS này - giả thiết là max nhất

+ và tiếp tục rà tìm TS tiếp theo nếu gặp TS ko phải quê Quảng Ngãi (QN) thì bỏ qua, đến khi gặp TS có quê là Quảng Ngãi (QN) thì so sánh max (ghi nhận trên) và điểm TS này, nếu max nhỏ hơn thì ghi nhận max mới là điểm của TS này trái lại bỏ qua. cứ như vậy đến kết thúc bảng thì chúng ta sẽ biết điểm max bằng bao nhiêu, và trong quá trình ghi nhận max như vậy chúng ta nhớ thêm số báo danh (SBD) nữa -> kết q chúng ta còn thêm là TS số SBD là bao nhiêu quê ở QN có điểm cao nhất là ....


Từ bài toán trên thấy tương tự bài B4 ở chỗ là tìm max (điểm cao nhất) của TS thỏa mãn đ/k quê QN,
áp dụng vào bài B4: chúng ta thấy chúng ta phải xét sao cho:

+ tìm được phần tử đầu tiên thuộc dãy (kể từ đầu dãy) thỏa mãn <đ/k> thì gán max = giá trị phần tử này> và đồng thời ghi nhận vị trí của phần tử này iV=i (iV là i Vị trí của phần tử max)
+ tiếp theo dò tìm các phần tử tiếp nếu gặp phần tử nào thỏa mãn đ/k thì lấy max so sánh với giá trị đó nếu max nhỏ hơn thì gán max = <giá trị phần tử này> và ghi nhận iV=i; trái lại bỏ qua, cứ như vậy đến hết dãy
->kQ cần tìm max

vấn đề đặt ra cần lưu ý là dãy có thể ko có phần tử nào thỏa mãn <d/k> và làm sao để nhận dạng phần tử đầu tiên thỏa mãn đ/k: giải pháp là chúng ta thêm 1 biến điếm d chẳng hạn để điếm số phần tử thỏa mãn đ/k, như thế: sẽ kiểm tra được khi d=1 là tương ứng phần tử đầu tiên. còn cuối cùng xuất kQ thì nếu d-0 -> ko có p tử t/m đ/k,....

hy vọng giải thích qua như vậy giúp mọi ng hiểu hơn - các bạn có cách giải thích nào hay hơn - đóng góp nhé



 
Lần chỉnh sửa cuối:
Upvote 0
Cảm ơn, tôi hiểu rồi... Nhưng nếu tôi gán giá trị Max đầu tiên chính là số nhỏ nhất trong dảy thì sao? Có vấn đề gì ko?
Mã:
Max = WorksheetFunction.Min(Range("A1:A" & Er))
ANH TUẤN

Nhân đây tôi gữi lên 2 bài tập tìm bội số chung nhỏ nhất và ước số chung lớn nhất... Các bạn xem thử có chổ nào ko ổn nha:
BSCNN:
PHP:
Sub BSCNN()
So1 = Range("C1").Value
So2 = Range("C2").Value
TICH = So1 * So2
If So1 = 0 Or So2 = 0 Then
   MsgBox "Luu y nhap so khac 0"
   Range("C1:C2").ClearContents
   Exit Sub
End If
For i = 1 To TICH
    If i Mod So1 = 0 And i Mod So2 = 0 Then
       Range("C3").Value = i
       Exit Sub
    End If
Next i
End Sub
USCLN:
PHP:
Sub USCLN()
So1 = Range("C1").Value
So2 = Range("C2").Value
If So1 = 0 Or So2 = 0 Then
   So = 1
ElseIf So1 <= So2 Then
       So = So1
Else: So = So2
End If
For i = 1 To So
    If So1 Mod i = 0 And So2 Mod i = 0 Then
       Range("C3").Value = i
    End If
Next i
End Sub
ANH TUẤN
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
ptm0412 đã viết:
Giải bài tập của bạn Tiger tớ gợi ý là dùng biến kiểu mảng.
Nói thêm 1 chút:
Biến kiểu mảng là biến có dạng: Bien(1 to n) với n là 1 hằng cụ thể. Biến này có n giá trị lần lượt Bien(1) , Bien(2) ... Bien(n).
Cao cấp hơn có biến mảng 2 chiều Bien(1 to n, 1 to m) với m, n là các hằng cụ thể. Biến 2 chiều thích hợp để xử lý bảng tính m dòng và n cột. Tương tự là biến nhiều chiều sử dụng trong lĩnh vực khác mà theo trính độ của mình là hơi trừu tượng.
Ở bài tập của Ban Tiger, mình đưa luôn trường hợp tổng quát của dãy số vào (chưa có trường hợp tổng quát của số cell liên tiếp cần tính tổng):
- Giá trị cần tính nằm ở cột A bắt đầu là A2 xuống ước chừng dưới 100 dòng. nếu nhiều thì ước nhiều hơn.
- số cell liên tiếp để tính tổng là 3.
Mã:
[COLOR=black]Sub Tongmax()[/COLOR]
[COLOR=black]' Khai báo biến[/COLOR]
[COLOR=black]Dim Sok(1 to 100) , Tam , Sum3 as Single[/COLOR]
[COLOR=black]Dim Rcell(1 to 3) , nRow as Integer[/COLOR]
[COLOR=black]Dim Add(1 to 3) as String[/COLOR]
[COLOR=black]' Gán giá trị cho biến[/COLOR]
[COLOR=black]Sum3 = 0[/COLOR]
[COLOR=black]nRow=Range("A2").End(xldown).Row()[/COLOR]
[COLOR=black]For k = 1 to nRow[/COLOR]
[COLOR=black]Sok(k) = Range("A1").Offset(k,0).Value[/COLOR]
[COLOR=black]Next k[/COLOR]
[COLOR=black]' Phần code[/COLOR]
[COLOR=black]For 1 = 1 to nRow - 2 ' Giảm được 2 vòng lặp, nếu là m thì giảm m-1 vòng lặp[/COLOR]
[COLOR=black]Tam = Sok(i) + Sok(i+1) + Sok(i+2)[/COLOR]
[COLOR=black]If Tam > Sum3 Then[/COLOR]
[COLOR=black]Sum3 = Tam[/COLOR]
[COLOR=black]Rcell(1) = i + 1[/COLOR]
[COLOR=black]Rcell(2) = i + 2[/COLOR]
[COLOR=black]Rcell(3) = i + 3[/COLOR]
[COLOR=black]' Hoặc nếu số cell tình tổng là m lấy từ 1 cell trên bảng tính ([/COLOR][COLOR=red]m = Range("B1").Value[/COLOR][COLOR=black]):[/COLOR]
[COLOR=black]' For j = 1 to m[/COLOR]
[COLOR=black]' Rcell(j) = i + j[/COLOR]
[COLOR=black]' Next j[/COLOR]
[COLOR=black]End If[/COLOR]
[COLOR=black]Next i[/COLOR]
 
[COLOR=black]' Địa chỉ các cell đã tìm được:[/COLOR]
[COLOR=black]Add(1) = Range("A" & Rcell(1)).Address[/COLOR]
[COLOR=black]Add(2) = Range("A" & Rcell(2)).Address[/COLOR]
[COLOR=black]Add(3) = Range("A" & Rcell(3)).Address[/COLOR]
[COLOR=black]' Hoặc nếu số cell tình tổng là m lấy từ 1 cell trên bảng tính ([/COLOR][COLOR=red]m = Range("B1").Value[/COLOR][COLOR=black]):[/COLOR]
[COLOR=black]' For iX = 1 to m[/COLOR]
[COLOR=black]' Add(iX) = Range("A" & Rcell(iX)).Address[/COLOR]
[COLOR=black]' Next iX[/COLOR]
 
[COLOR=black]' Đánh dấu chọn các cell vừa tìm được (hoặc tô màu tùy bạn)[/COLOR]
[COLOR=black]Range(Add(1) &":" & Add(m)).Select[/COLOR]
[COLOR=black]Range("B2").Value = "Tong lon nhat là " & Sum3 [/COLOR]
 
[COLOR=black]End Sub[/COLOR]
Tớ đã dự trù có thể mở rộng ra số cell cần tính tổng là cho trước ở 1 cell B1, các dòng lệnh tổng quát là các dòng có dấu nháy; nhưng lại bí ở dòng tính tổng Sum3 = Sok(1) + Sok(2) +... + Sok(m). Nhờ các bạn góp ý cho theo hướng này.

Dài thế nhỉ, em xin phép đóng góp 1 UF nhé :

PHP:
Function TongMax(Mang As Range, SoPhanTu As Byte) As String
    On Error Resume Next
    Application.Volatile (False)
    Dim Tong As Long, i As Long, i1 As Long, Temp As Long, ViTri As Long
    If Mang.Rows.Count < SoPhanTu Or Mang.Columns.Count > 1 Then Exit Function
    For i = 1 To Mang.Rows.Count - SoPhanTu + 1
        If i = 1 Then
            For i1 = i To i + SoPhanTu - 1
                Tong = Tong + Mang(i1)
            Next
            ViTri = 1
        Else
            Temp = 0
            For i1 = i To i + SoPhanTu - 1
                Temp = Temp + Mang(i1)
            Next
            If Tong < Temp Then Tong = Temp: ViTri = i
        End If
    Next
    TongMax = "Max la: " & Format(Tong, "#,##0") & " bat dau tu vi tri thu " & Format(ViTri, "#,##0")
    Set Mang = Nothing
End Function
Thân!
 

File đính kèm

Upvote 0
anhtuan1066 đã viết:
Cảm ơn, tôi hiểu rồi... Nhưng nếu tôi gán giá trị Max đầu tiên chính là số nhỏ nhất trong dảy thì sao? Có vấn đề gì ko?
Mã:
Max = WorksheetFunction.Min(Range("A1:A" & Er))
ANH TUẤN
ok thế thì k vấn đề gì, nhưng không nên làm thế, ta đang đi xd bài toán tìm max, giờ ta lại dùng hàm excel tìm min (hic)- quan trọng hơn làm như vậy ta lại làm bài toán tìm min (dĩ nhiên sẽ mất thời gian thêm - dù điều này excel làm và cùng với bài toán nhỏ nên chúng ta k thấy).
 
Upvote 0
anhtuan1066 đã viết:
Nhân đây tôi gữi lên 2 bài tập tìm bội số chung nhỏ nhất và ước số chung lớn nhất... Các bạn xem thử có chổ nào ko ổn nha:
Đọc nhanh mới phát hiện mấy điểm sau a tuấn ah

1) bài BSCNN

đoạn này
PHP:
'<....>
TICH = So1 * So2
If So1 = 0 Or So2 = 0 Then
   MsgBox "Luu y nhap so khac 0"
   Range("C1:C2").ClearContents
   Exit Sub
End If
'<....>
đã tính tích rui, sao a ko đổi đ/k thành:
PHP:
If TICH=0 Then
như thế ngắn gọn và nhanh hơn,

2) còn bài USCLN thì phải đổi chiều vòng FOR sẽ hay hơn (dĩ nhiên khi đó phải thêm Exit sub) atuan ah

từ
PHP:
For i = 1 To So
    If So1 Mod i = 0 And So2 Mod i = 0 Then
       Range("C3").Value = i
    End If
Next i
thành như sau:

PHP:
 For i = so To 1 Step -1
    If So1 Mod i = 0 And So2 Mod i = 0 Then
       Range("C3").Value = i
       Exit Sub
    End If
Next i
và thêm Exit sub

Tuy vậy thuật toán Giải thuật Euclid tìm USCLN hơn quả: cụ thể

Giải thuật Euclid gốc
PHP:
USCLN(a,b) = a      nếu   a=b
USCLN(a,b) = USCLN(a-b, b)      nếu   a>b
USCLN(a,b) = USCLN(a, b-a)      nếu    a<b
hoặc tham khảo thêm nữa về giải thuật Euclid tìm USCLN hiệu quả:
Nếu a mod b = 0 thì USCLN = b trái lại thì USCLN(a,b) = USCLN(b, a mod b) tại đây

(Nhưng như thế lại sang vòng lặp không xác định rùi - không phải FOR)


và có tính chất USCLN và BSCNN

USCLN(a, b)·BSCNN(a, b) = a*b -> BSCNN(a, b)=a*b/ USCLN(a, b)

nên BSCNN lại thường tính qua UCSLN


hy vọng atuan... và các TV tham khảo thêm
 
Lần chỉnh sửa cuối:
Upvote 0
Mr Okebab đã viết:
Dài thế nhỉ, em xin phép đóng góp 1 UF nhé :
Thực ra cũng không quá dài đến thế. Sau khi học ReDim của bạn TigerTiger, lấy doạn code tính tổng mở rộng của bạn ấy thì được kết quả sau: (Giữ nguyên thuật toán và các câu lệnh)

Dim RCelli() As Integer
Dim Add() As String
Dim Sok(), Tam, Sum3 As Single
n = Range("A2").End(xlDown).Row()
m = Range("C1").Value
ReDim Sok(n), RCelli(m), Add(m)

For k = 1 To n
Sok(k) = Range("A1").Offset(k, 0).Value
Next k

For i = 1 To n - m + 1
For i2 = i To i + m - 1
Tam = Tam + Sok(i2)
Next i2
If Tam > Sum3 Then
Sum3 = Tam
For j = 1 To m
RCelli(j) = i + j
Add(j) = Range("A" & RCelli(j)).Address
Next j
End If
Tam = 0
Next i

Range(Add(1) & ":" & Add(m)).Select
Range("B1:B20").Clear
Range("B1").Value = "Tong lon nhat la " & Sum3

For ix = 1 To m
Range("B1").Offset(ix, 0).Value = Add(ix)
Next ix
End Sub


Trong đó phần chữ đỏ là code chính, 13 dòng
Phần chữ xanh dương là phần xuất kết quả trong đó có liệt kê địa chỉ các cell vừa tìm được. Nếu không cần liệt kê thì bỏ 5 dòng xanh và bỏ luôn dòng đỏ in đậm --> code chính còn 12 dòng.
Phần chữ xanh đậm là khai báo biến và gán giá trị cho biến, khi chuyển thành UDF thì giảm được một mớ nữa.
Không dài, không dài.
Tuy nhiên về vốn câu lệnh vẫn còn phải học nhiều ở các bạn.
 
Lần chỉnh sửa cuối:
Upvote 0
Giải bài tập 3, mức độ 2 phần mở rộng

Bài này tớ mở rộng cho cả Max, Min, tính cho bảng m dòng n cột:
- Áp dụng biến mảng 2 chiều
- Áp dụng biến mảng động
- Dùng biến tạm
- Dùng hàm iif() của VBA thay cho If then
- biến đổi thành Funtion
- nếu tính Max thì gán Tam = 0, tính Min thì Tam = tham số max dự đoán
- hàm có 3 tham số: Dịa chỉ cell đầu bên trái của bảng dữ liệu, min hoặc max, max dự đoán. Tham số Maxguess có thể bỏ trống nếu tính max và cần phải có nếu tính min.
Mã:
Function tim(Begcell As String, Ask As String, Optional MaxGuess As Double) As String
Dim RAdd, CAdd, Rcount, Ccount As Integer
Dim Data1() As Single
Dim Add As String
Rcount = Range(Begcell).End(xlDown).Row - Range(Begcell).Row + 1
Ccount = Range(Begcell).End(xlToRight).Column - Range(Begcell).Column + 1
Ask = LCase(Ask)
ReDim Data1(Rcount, Ccount)
Tam = IIf(Ask = "max", 0, MaxGuess)
For i = 1 To Rcount
For j = 1 To Ccount
Data1(i, j) = Range(Begcell).Offset(i - 1, j - 1).Value
Select Case Ask
Case "max"
If Tam > Data1(i, j) Then
Tam = Tam
Else
Tam = Data1(i, j)
RAdd = i - 1
CAdd = j - 1
End If
Case "min"
If Tam < Data1(i, j) Then
Tam = Tam
Else
Tam = Data1(i, j)
RAdd = i - 1
CAdd = j - 1
End If
End Select
Next j
Next i
Add = Range(Begcell).Offset(RAdd, CAdd).Address
tim = IIf(Ask = "max", "Gia tri lon nhat la: ", "Gia tri nho nhat la: ") & Tam
tim = tim & " tai cell " & Add
End Function
Nhờ các bạn góp ý, hình như dài, dài thật. Mà còn rối rắm những là -1 với +1.
Tớ đoán là do thuật toán chưa tối ưu, khi chạy thử thì lỗi, sửa tới sửa lui mới bị như vậy.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
anhtuan1066 đã viết:
Cảm ơn, tôi hiểu rồi... Nhưng nếu tôi gán giá trị Max đầu tiên chính là số nhỏ nhất trong dảy thì sao? Có vấn đề gì ko?
Sao không cho Max = zero?
Đến cuối nếu Max vẫn bằng zero thì
-Một là không tìm được.
-Hai là trong dãy có chứa số 0.
-->Đưa ra kết luận.

Hôm qua Post bài xong, kèm theo câu hỏi cho bạn Tigertiger là 12h đêm, đi ngủ. Bữa nay coi lại, Tiger soạn bài tập, post lên, sửa nội dung, trả lời tớ :`1h08!
Thế mới biết nhiệt tình của các bạn đối với topic này như thế nào.
Còn bạn Anhtuan, làm hết mọi bài tập, thảo luận, hăng say như trai trẻ tập yêu, cũng thật hết lòng vì mình và vì mọi người.
Còn những bạn khác nữa, sẵn sàng góp ý, sửa chữa, cho lời khuyên . . .
Thật là vui phải không? Mong rằng mọi người chung sức như vậy sẽ giúp được cho các bạn khác. Không biết sao, chứ nhìn topic: 101 lần trả lời, 1277 lần đọc là phấn khởi lắm.
Một lần nữa, cám ơn tất cả mọi người.
Tớ dự định mức độ 3 sẽ là nhiều vòng For lồng nhau, nhưng các bạn đã đẩy tiến độ nhanh hơn tớ tưởng và ứng dụng 2 vòng lồng nhau từ lâu!
Vui thật đấy.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Bạn ptm0412 ơi! Tôi cám ơn sự nhiệt tình giúp đở của bạn... Nhưng mong rằng từ từ, đừng chuyển sang mức độ 3 vội... Bạn hảy nhìn đi... Lớp học 5, 6 thầy nhưng chỉ có 1 học sinh... ??? Sao ko ai vào tham gia nhỉ?
-Bài giãng quá cao cấp khó hiểu ?
-Mọi người đã biết tất cả rồi ?
Là nguyên nhân nào đây ???
Dù là nguyên nhân gì đi chăng tôi cũng mong rằng tất cả các bạn, những người mới học hảy mạnh dạn tham gia, sai thì sửa.. (cũng như tôi bước vào bài học này tôi có biết tí gì về FOR đâu)
Bạn ptm0412 có thể nói thêm 1 tí về vòng lập FOR dc ko? Gặp trường hợp thế nào thì xài nó? Còn tôi thì qua các bài học về FOR tôi rút ra kết luận rằng: Ta sẽ dùng FOR đối với những bài toán có tính kiểm tra loại trừ trong 1 mãng dử liệu đễ lấy ra 1 hoặc vài em thỏa điều kiện
(ko biết đúng ko?)
ANH TUẤN
 
Upvote 0
đoạn này có vẻ ko ổn
ptm0412 đã viết:
PHP:
If Tam > Data1(i, j) Then
Tam = Tam
Else
Tam = Data1(i, j)
RAdd = i - 1
CAdd = j - 1
End If
vì ko bao giờ tam=tam như thế nó lại nhận chính giá trị của nó (không thay đổi gì và ko có ý nghĩa gì - trong trường hợp này bạn dùng if khuyết - khi đó thì đổi lại đ/k ngược lạicụ thể là

PHP:
If Tam < Data1(i, j) Then
      Tam = Data1(i, j)
       RAdd = i - 1
       CAdd = j - 1
End If

tương tự đoạn min cũng vậy
mới xem qua -> có ý kiến vậy thôi
 
Lần chỉnh sửa cuối:
Upvote 0
ptm0412 đã viết:
Thực ra cũng không quá dài đến thế. Sau khi học ReDim của bạn TigerTiger, lấy doạn code tính tổng mở rộng của bạn ấy thì được kết quả sau: (Giữ nguyên thuật toán và các câu lệnh)


Trong đó phần chữ đỏ là code chính, 13 dòng
Phần chữ xanh dương là phần xuất kết quả trong đó có liệt kê địa chỉ các cell vừa tìm được. Nếu không cần liệt kê thì bỏ 5 dòng xanh và bỏ luôn dòng đỏ in đậm --> code chính còn 12 dòng.
Phần chữ xanh đậm là khai báo biến và gán giá trị cho biến, khi chuyển thành UDF thì giảm được một mớ nữa.
Không dài, không dài.
Tuy nhiên về vốn câu lệnh vẫn còn phải học nhiều ở các bạn.
Vâng, quả thực là không dài, tuy nhiên tớ chỉ có mấy góp ý thế này
Dù là Sub hay là Function thì ta đều phải
  1. Khai báo biến (Nếu có sử dụng)
  2. Bẫy lỗi
  3. Chạy chương trình chính (khi không có lỗi)
  4. Xuất ra kết quả
Vì vậy, dù làm thế nào cũng được, tuy nhiên phải đảm bảo các yếu tố đó.
Như vậy không thể nói : Bỏ đi phần này hay bỏ đi phần khác được.
Còn Sub trên : Chú ý hơn nữa phần bẫy lỗi. Tớ chưa thây được cái này.
Cả trong bài 101 cũng vậy, mà Function đặc biệt cần cái này

Thân!
 
Upvote 0
Mr Okebab đã viết:
Dù là Sub hay là Function thì ta đều phải
  1. Khai báo biến (Nếu có sử dụng)
  2. Bẫy lỗi
  3. Chạy chương trình chính (khi không có lỗi)
  4. Xuất ra kết quả
Vì vậy, dù làm thế nào cũng được, tuy nhiên phải đảm bảo các yếu tố đó.
Như vậy không thể nói : Bỏ đi phần này hay bỏ đi phần khác được.
Còn Sub trên : Chú ý hơn nữa phần bẫy lỗi. Tớ chưa thây được cái này.
Cả trong bài 101 cũng vậy, mà Function đặc biệt cần cái này

Thân!
Cám ơn bạn đã nhắc nhở. Thú thật đây là Function đầu tiên mình tự làm ra, học ở câu giải thích sau:
TigerTiger đã viết:
. . . function thì cũng tương tự sub thôi, nhưng nó tiện lợi là trả về 1 giá trị và có thể nhập các tham số cho Function, và có ở thường ở cuối trước "End Function" có lệnh gán <tên hàm"> =<giá trị>, thế thôi,
đơn giản và tiện ích vì Function luôn động với dữ liệu (giống hàm của Excel) trong khi đó sub chỉ thực hiện khi chạy.
và mình nghĩ rằng 1 số biến vẫn phải khai, một số biến lấy từ các tham số của hàm, giảm là giảm chỗ đó thôi.
Với lại, mình nghĩ, với hàm (function) sẽ chỉ trả vế 1 kết quả, phần liệt kê kết quả là dư, nên giảm nó là phải, Bạn Bab xem có đúng không.

Phần bẫy lỗi mình cũng biết sơ qua, nhưng chưa hề có kinh nghiệm, phải học hỏi nhiều, mong bạn nếu có thể viết cho 1 vài bài post lên, mình và mọi người có thêm cơ hội học hỏi.
Thân
 
Upvote 0
Xin dịch sang ngôn ngữ đời thường giúp đoạn mã sau

Mã:
Option Explicit[b]

Sub Tich2SoCong1ChiaSoThu3()[/b]
 Dim ij As Integer
 Dim bChuc As Byte, bDVi As Byte, bTram As Byte
 
 On Error Resume Next
 For ij = 100 To 999
    bChuc = (ij \ 10) Mod 10
    bDVi = ij Mod 10:               bTram = ij \ 100
    If (1 + bTram * bChuc) Mod bDVi = 0 Then
        Chep ij:            GoTo Het
    End If
    If (1 + bTram * bDVi) Mod bChuc = 0 Then
        Chep ij:            GoTo Het
    End If
    If (1 + bChuc * bTram) Mod bDVi = 0 Then
        Chep ij:                    GoTo Het
    End If
    If (1 + bChuc * bDVi) Mod bTram = 0 Then
            Chep ij:            GoTo Het
    End If
    If (1 + bDVi * bTram) Mod bChuc = 0 Then
            Chep ij:            GoTo Het
    End If
    If (1 + bDVi * bChuc) Mod bTram = 0 Then
            Chep ij:            GoTo Het
    End If
Het:  Next ij[b]
End Sub[/b]
PHP:
[b]
Sub Chep(iZ As Integer)
    Dim Rng As Range
    Set Rng = Range("B" & Range("B65432").End(xlUp).Row + 1)
    With Rng
        .Value = iZ
        If .Offset(-1, -1) <> "TT" Then
            .Offset(, -1) = .Offset(-1, -1) + 1
        Else
            .Offset(, -1) = 1
        End If
    End With
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
ptm0412 đã viết:
Cám ơn bạn đã nhắc nhở. Thú thật đây là Function đầu tiên mình tự làm ra, học ở câu giải thích sau:

và mình nghĩ rằng 1 số biến vẫn phải khai, một số biến lấy từ các tham số của hàm, giảm là giảm chỗ đó thôi.
Với lại, mình nghĩ, với hàm (function) sẽ chỉ trả vế 1 kết quả, phần liệt kê kết quả là dư, nên giảm nó là phải, Bạn Bab xem có đúng không.

Phần bẫy lỗi mình cũng biết sơ qua, nhưng chưa hề có kinh nghiệm, phải học hỏi nhiều, mong bạn nếu có thể viết cho 1 vài bài post lên, mình và mọi người có thêm cơ hội học hỏi.
Thân

Function có thể đưa ra một kết quả hoặc 1 nhóm kết quả (hàm mảng)
Vì vậy phần liệt kê cũng như nhau thôi.
Function cần khai báo rất nhiều tham số, trong khi đó sub đã có cell trợ giúp. vậy nên ngắn hơn là phải.

Tuy nhiên Function thường ngắn hơn bởi vì khi làm thì các tác giả đều cố gắng tối ưu các dòng code, thuật toán của mình, còn với SUB thì mọi người thường có quan niệm : nghĩ sao làm vậy, tức là mô tả lại công việc của một người không biết gì về VBA (tất nhiên có thể mô tả bằng thuật toán, bằng vòng lặp . . )

Hay là tớ thử làm Function trên thành Sub xem có dài hơn không nhé ??? Đùa vậy thôi, tự cậu làm được mà.

Thân!
 
Upvote 0
Mr Okebab đã viết:
Hay là tớ thử làm Function trên thành Sub xem có dài hơn không nhé ??? Đùa vậy thôi, tự cậu làm được mà.

Thân!
PHP:
Sub TongMax()
    '' Mang DuLieu : Tu A2 tro di _ _ _  So phan tu : C1 _ _ _ Ketqua : B1
    On Error Resume Next
    Dim Tong As Long, i As Long, i1 As Long, Temp As Long, ViTri As Long
    If Range("A65000").End(xlUp) < Range("C1").Value + 1 Or Range("C1").Value < 1 Then Exit Sub
    For i = 2 To Range("A65000").End(xlUp) - SoPhanTu + 1
        If i = 2 Then
            For i1 = i To i + Range("C1").Value - 1
                Tong = Tong + Range("A" & i).Value
            Next
            ViTri = 2
        Else
            Temp = 0
            For i1 = i To i + Range("C1").Value - 1
                Temp = Temp + Range("A" & i).Value
            Next
            If Tong < Temp Then Tong = Temp: ViTri = i
        End If
    Next
    Range("B1").Text = "Max la: " & Format(Tong, "#,##0") & " bat dau tu hang A" & Format(ViTri, "#,##0")
End Sub
Thân!
 
Upvote 0
Cám ơn bạn nhiều, mình thực không biết nhiều như bạn tưởng. Tối ưu hóa thuật toán là tùy thuộc vào khí chất thông minh hoặc tính chịu khó suy luận, cái này mình không bằng giới trẻ. Các thủ thuật tối ưu code mình còn phải học nhiều các câu lệnh và cấu trúc lệnh.

Bạn làm giúp sub() cho cái function dởm của mình về tìm giá trị min hoặc max trong mảng, cho mình học hỏi. Cái bạn vừa làm là tính tổng lớn nhất của 3 hoặc 5 cell liên tiếp.
 
Upvote 0
ptm0412 đã viết:
Cám ơn bạn nhiều, mình thực không biết nhiều như bạn tưởng. Tối ưu hóa thuật toán là tùy thuộc vào khí chất thông minh hoặc tính chịu khó suy luận, cái này mình không bằng giới trẻ. Các thủ thuật tối ưu code mình còn phải học nhiều các câu lệnh và cấu trúc lệnh.

Bạn làm giúp sub() cho cái function dởm của mình về tìm giá trị min hoặc max trong mảng, cho mình học hỏi. Cái bạn vừa làm là tính tổng lớn nhất của 3 hoặc 5 cell liên tiếp.
Hàm của em thì không giới hạn số phần tử là 3 hay 5 bác ạ, bao nhiêu cũng được mà.


Đây là hàm tính tổng max các phần tử liên tiếp trong cùng 1 cột của 1 mảng. Số phần tử được tính sẽ tùy người dùng khai báo.

PHP:
Function TongMM(Mang As Range, SoPhanTu As Byte, Optional Loai As String) As String
    On Error Resume Next
    Application.Volatile (False)
    Dim Tong As Double, Hang As Long, Cot As Byte, iC As Byte, _
        iR As Long, i1 As Long, Temp As Double
    If Mang.Rows.Count < SoPhanTu Then Exit Function
    For Cot = 1 To Mang.Columns.Count
        For Hang = 1 To Mang.Rows.Count - SoPhanTu + 1
            If Hang = 1 And Cot = 1 Then
                For i1 = Hang To Hang + SoPhanTu - 1
                    Tong = Tong + Mang(i1, Cot)
                Next
                iC = Cot: iR = Hang
            Else
                Temp = 0
                For i1 = Hang To Hang + SoPhanTu - 1
                    Temp = Temp + Mang(i1, Cot)
                Next
                If (Tong < Temp And UCase(Loai) = "MAX") Or (Tong > Temp And UCase(Loai) <> "MAX") Then
                    Tong = Temp: iC = Cot: iR = Hang
            End If: End If: Next Hang: Next Cot
    iR = iR + Mang.Row - 1: iC = iC + Mang.Column - 1
    TongMM = IIf(UCase(Loai) = "MAX", "Max", "Min") & " la :" & Format(Tong, "#,##0.0000") & _
            " bat dau tu vi tri " & Cells(iR, iC).Address
    Set Mang = Nothing
End Function
Còn Sub thì tương tự thôi bác ạ.

Còn hàn của bác : Bác xem File sẽ thấy khác nhau như thế nào :
  1. Khai báo biến chưa đủ
  2. Khi có số âm trong bảng thì sẽ bị sai
  3. Chưa có tham số cho tùy chọn số phần tử được tính (3, hay 5, hay 6), của bác mới chỉ là 1
Thân!


P/S : Xin lỗi bác, em cứ nghĩ rằng bác trẻ hơn em nên xưng hô như vậy.
Thành thật xin lỗi.
 

File đính kèm

Upvote 0
"Lơ canh chua" rồi...
Theo tới đây thì xem như "hết xăng"... Ko theo nỗi các đại ca... Hic...
Vòng lập FOR cơ bản còn đang tiêu hóa như thời bao cấp bị ép ăn bo bo... giờ thêm mấy chiêu mới lạ:
-REDIM ??? Hỏng biết
-ON ERROR RESUME NEXT ??? Hỏng biết
-APPLICATION.VOLATILE ??? Lại càng hỏng biết
Vân vân và vân vân... Một trời cái hỏng biết...
 
Upvote 0
Cạnh tranh với BAB cái, nha!

PHP:
Function MaxInColumn(CRng As Range, Optional PhanTu As Byte = 1) As String
 Dim lRow As Long, lCuoi As Long, iJ As Long
 Dim TTemp As Double, TTong As Double
 Dim Rng As Range, StrC As String
 
 lRow = CRng.Rows.Count
 If lRow < PhanTu Then
    MaxInColumn = "KHONG THE TIM!":         Exit Function
 ElseIf lRow = PhanTu Then
    MaxInColumn = "CAN TIM SAO?":           Exit Function
 Else
    lRow = lRow - PhanTu + 1:               TTemp = -999999999999#
    Set CRng = CRng.Resize(lRow, 1)
    For Each Rng In CRng
        With Rng
            TTong = Application.Sum(.Resize(PhanTu, 1))
            If TTong > TTemp Then
                TTemp = TTong:              StrC = .Resize(PhanTu, 1).Address
            End If
        End With
    Next Rng
 End If
 MaxInColumn = StrC
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
anhtuan1066 đã viết:
"Lơ canh chua" rồi...
Theo tới đây thì xem như "hết xăng"... Ko theo nỗi các đại ca... Hic...
Vòng lập FOR cơ bản còn đang tiêu hóa như thời bao cấp bị ép ăn bo bo... giờ thêm mấy chiêu mới lạ:
-REDIM ??? Hỏng biết
-ON ERROR RESUME NEXT ??? Hỏng biết
-APPLICATION.VOLATILE ??? Lại càng hỏng biết
Vân vân và vân vân... Một trời cái hỏng biết...

Hiểu một cách "toàn diện" (bắt chước phim)
  1. REDIM : Khai báo lại mảng (Mà mảng thì sẽ có Số Cột và Hàng)
  2. On Error Resume Next : Nếu gặp lỗi thì . . cứ chạy tiếp
  3. Application.Volatile(True or False) : Khi có một sự thay đổi nào trong bảng tính thì công thức sẽ tính lại hay chỉ khi có sự thay đổi trong đến tham số khai báo thì nó mới tính lại
Trình độ VBA của bác tiến rất nhanh, cố lên bác ạ.
Thân!
 
Upvote 0
SA_DQ đã viết:
PHP:
Function MaxInColumn(CRng As Range, Optional PhanTu As Byte = 1) As String
 Dim lRow As Long, lCuoi As Long, iJ As Long
 Dim TTemp As Double, TTong As Double
 Dim Rng As Range, StrC As String
 
 lRow = CRng.Rows.Count
 If lRow < PhanTu Then
    MaxInColumn = "KHONG THE TIM!":         Exit Function
 ElseIf lRow = PhanTu Then
    MaxInColumn = "CAN TIM SAO?":           Exit Function
 Else
    lRow = lRow - PhanTu + 1:               TTemp = -999999999999#
    Set CRng = CRng.Resize(lRow, 1)
    For Each Rng In CRng
        With Rng
            TTong = Application.Sum(.Resize(PhanTu, 1))
            If TTong > TTemp Then
                TTemp = TTong:              StrC = .Resize(PhanTu, 1).Address
            End If
        End With
    Next Rng
 End If
 MaxInColumn = StrC
End Function
Học được của bác chiêu Resize (mặc dù chưa hiểu lắm)
Còn cách tính thì cũng tương tự thôi ạ.
Bác làm thêm phần tùy chọn max, min và cho nhiều cột nữa bác ạ.

Giả sử trong mảng có tổng các số (theo số phần tử được cộng) nhỏ hơn - 999.999.999.999 thì sao nhỉ (híc, chắc không có đâu)

Tuy nhiên em đã nói rồi, về giải thuật thì em còn đi sau bác xa lắm.

Thân!
 
Upvote 0
Ko biết thì bực mình... biết thêm thì càng thấy.. quá ngu...
Giá như các cao thủ khi post code lên có thể nói sơ qua về thuật toán (ý tưỡng tạo code dựa trên cơ sở nào)... Và giãi thích từng cụm lệnh dùng đễ làm cái gì thì... ngàn lần cảm ơn!
Tôi đễ ý thấy rằng rất khó khăn khi cố gắng tìm hiểu code của người khác (Của mình viết xong, lâu lâu xem lại có khi còn hỏng hiểu)... Vì thế mà nếu như nắm bắt dc thuật toán, rồi tự mình làm, sau đó so sánh với mẫu của các đại ca thì sẽ rút ra dc rất nhiều kinh nghiệm quý báu.. lại còn có thể tùy biến cao...
Chứ copy code mang về chạy thì chả khác nào có tiền ra siêu thị mua đồ về xài... Với bản thân tôi thì tôi chẳng khoái vụ này lắm... Tôi vẫn thích tự mình làm ra (ngon dở cũng dc).. xong thì so sánh cái của mình với của người khác.. như thế mới nghiệm ra dc code của các bạn nó "xịn" ở điểm nào chứ... he.. he... Mà muốn dc như thế thì phải hiểu thuật toán của các đại ca trước đã.. đúng ko?
ANH TUẤN
 
Upvote 0
Mr Okebab đã viết:
Hàm của em thì không giới hạn số phần tử là 3 hay 5 bác ạ, bao nhiêu cũng được mà.
Ý mình là Sub Tongmax() của bạn nhằm giải bài tập số 2 mức độ 2 của TigerTiger (tính tổng lớn nhất của 3 số liên tiếp lớn nhất, mở rộng cho 5, 7 hoặc số bất kỳ). Sub của bạn lấy số này từ cell C1 vào biến sophantu thì mình thấy rôi.

Còn hàm của bác : Bác xem File sẽ thấy khác nhau như thế nào :
  1. Khai báo biến chưa đủ
  2. Khi có số âm trong bảng thì sẽ bị sai
  3. Chưa có tham số cho tùy chọn số phần tử được tính (3, hay 5, hay 6), của bác mới chỉ là 1
Bạn xem lại, cái hàm dởm mà mình khoe là giải bài tập số 3:
Cho 1 dãy số nguyên (được chứa vào các ô của 1 cột - chẳng hạn 10 số từ A1,A2,...,A10).
Tìm phần tử max của dãy số
Vd dãy số 18 15 5 88 70 16 45 26 25 30
kQ: max = 88 tại vị trí 4 trong dãy

* Tương tự bài toán tìm min của dãy
Mình cho min, max chung vào 1 hàm, mở rộng tìm kiếm trong 1 mảng 2 chiều, tìm ra min, max, xác định vị trí, đúng yêu cầu đấy chứ. Số âm cũng tính min max (1 giá trị) đâu có vấn đề.
Còn bài số 2, mình cũng cho tổng max của n cell liên tiếp mà. Cái bài mà mình đưa lên 3 màu xanh đỏ tím vàng ấy. Bài ấy đúng là chưa tính tới số âm.
Và cả 2 bài đều chưa khai báo biến đầy đủ.


P/S : Xin lỗi bác, em cứ nghĩ rằng bác trẻ hơn em nên xưng hô như vậy.
Thành thật xin lỗi.
Hì hì, không sao, không sao. À mà tớ giấu tuổi kỹ lắm, sao bạn biết vậy?
 
Lần chỉnh sửa cuối:
Upvote 0
ptm0412 đã viết:
[/list]Bạn xem lại, cái hàm dởm mà mình khoe là giải bài tập số 3:

Mình cho min, max chung vào 1 hàm, mở rộng tìm kiếm trong 1 mảng 2 chiều, tìm ra min, max, xác định vị trí, đúng yêu cầu đấy chứ. Số âm cũng tính min max (1 giá trị) đâu có vấn đề.
Có phải hàm ở bài 101 không bác ???

Bạn xem lại, cái hàm dởm mà mình khoe là giải bài tập số 3:

Cho 1 dãy số nguyên (được chứa vào các ô của 1 cột - chẳng hạn 10 số từ A1,A2,...,A10).
Tìm phần tử max của dãy số
Vd dãy số 18 15 5 88 70 16 45 26 25 30
kQ: max = 88 tại vị trí 4 trong dãy

* Tương tự bài toán tìm min của dãy

Cái này thì theo em có 3 cách :
  1. Dùng For . . Next
  2. Dùng hàn Max, Min của excel kết hợp với Match
  3. Copy ra vùng tạm, dùng chức năng Sort rồi nhận diện Min, Max
Hì hì, không sao, không sao. À mà tớ giấu tuổi kỹ lắm, sao bạn biết vậy?
ptm0412 đã viết:
Tối ưu hóa thuật toán là tùy thuộc vào khí chất thông minh hoặc tính chịu khó suy luận, cái này mình không bằng giới trẻ.
Bác xem File đính kèm của em nhé (File số 3 ấy), sẽ thấy nếu là số âm nó sẽ bị sai. (thử với -999.999.999)

Thân!
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom