Bài tập vòng lặp VBA cho người mới bắt đầu: (1 người xem)

  • Thread starter Thread starter SA_DQ
  • Ngày gửi Ngày gửi
Liên hệ QC

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

SA_DQ

/(hông là gì!
Thành viên danh dự
Tham gia
8/6/06
Bài viết
14,689
Được thích
23,043
Nghề nghiệp
U80
Hãy giúp tôi lập danh sách các ngày trong thế kỹ này đã, đang & sẽ có ngày 31 (của tháng) rơi vô chủ nhật.
 
Bài tập "giải bài tập" cho người mới bắt đầu:
1. Đặt câu hỏi: mục đich của bài tập này là gì?
2. Tìm dữ liệu đầu vào và mẫu mã/điều kiện đầu ra
3. Xét lại xem mẫu mã/điều kiện đầu ra có đầy đủ để làm việc hay không? Nếu cần thì xem lại câu 1 ở trên.
4. Nếu mọi thứ ổn thỏa, bắt đầu động não làm việc.
 
Upvote 0
Bác nhỉ.
- Chủ nhật sẽ dựa vào 1 ngày gốc sẽ phải cho biết trước?
- Hay là dùng hàm WEEKDAY?
- Hay là +-*/, giả sử, giả định khác để tính ngày chủ nhật?
Em đang nghĩ đến for từ ảnh đến ảnh, rồi WEEKDAY. Nhưng mà không hay.
1703042579705.png
mục đich của bài tập
Bác ấy đang tính xem trong thế kỷ này sẽ uống rượu được bao nhiêu ngày chủ nhật nữa. Với điều kiện là ngày ấy phải đặc biệt chứ không đại trà. Tức là rượu có chọn lọc.
_)(#; _)(#; _)(#;
 
Upvote 0
Hãy giúp tôi lập danh sách các ngày trong thế kỹ này đã, đang & sẽ có ngày 31 (của tháng) rơi vô chủ nhật.
PHP:
Sub SundaysWith31st()
    Dim startDate As Date
    Dim endDate As Date
    Dim currentDate As Date
   
    startDate = DateSerial(2000, 1, 1)
    endDate = DateSerial(2099, 12, 31)
    currentDate = startDate
    Do While currentDate <= endDate
        If Day(currentDate) = 31 And Weekday(currentDate) = vbSunday Then
            Debug.Print currentDate
        End If
        currentDate = DateSerial(Year(currentDate), Month(currentDate), Day(currentDate) + 1)
    Loop
End Sub

Dạ em xin trả bài ạ. Nhờ bác chữa giúp em.
 
Upvote 0
Lần chỉnh sửa cuối:
Upvote 0
Ý bác @SA_DQ là vòng lặp thì chắc dùng VBA.
Thì cứ thuần túy 01/01/2000 + 1 cho đến khi 31/12/2099;
Nếu Weekday(checkDate, vbSunday) = 1 && Day(checkDate) = 31
thì count = count +1
Chạy hết in màn hình thôi :D
 
Upvote 0
Vòng lặp thì đúng, nhưng nên có cách hạn chế số lần lặp, thay vì lặp đủ 3675 lần
Chỉ đếm các tháng 1, 3, 5, 7, 8, 10, 12; và ghép giá trị luôn hả anh
này thành bài nâng cao;

JavaScript:
Sub Count31Sundays()
    Dim ArrMonth()
    Dim checkDate As Date
    Dim List31Sunday() As Date
   
    ArrMonth = Array(1, 3, 5, 7, 8, 10, 12)
    count = 0
    For yr = 2000 To 2099
        For Each mth In ArrMonth
       
            checkDate = DateSerial(yr, mth, 31)
            If Weekday(checkDate, vbSunday) = 1 Then
                count = count + 1
                ReDim Preserve List31Sunday(1 To count)
                List31Sunday(count) = checkDate
            End If
            cnt = cnt + 1
        Next mth
    Next yr
    Range("A1").Resize(count, 1) = Application.Transpose(List31Sunday)
End Sub
** Cập nhật theo đề bài là "lập danh sách";
 
Lần chỉnh sửa cuối:
Upvote 0
Kế thừa và DateSerial nó tự động nhảy lên mùng 1 tháng sau nên lại phải If 31. :wallbash: :wallbash: :wallbash:

Có cách nào không dùng Weekday các bác nhỉ?
Mã:
Sub zzz()
For y = 2000 To 2099 Step 1
    For m = 1 To 12 Step 1
        currentDate = DateSerial(y, m, 31)
        If Day(currentDate) = 31 And Weekday(currentDate) = vbSunday Then
            Debug.Print currentDate
        End If
    Next
Next
End Sub
 
Upvote 0
Các bài trả lời đều bỏ qua điều kiện "mẫu mã đầu ra" tôi đã nêu trên. Tất cả đều tự đặt rằng tháng trình bày theo kiểu 7 ngày ngang 5 hàng dọc, với cột đầu tiên là ngày chủ nhật.

Bài toán này không dễ như chủ thớt tưởng. Nó có hai vòng lặp:

1. Vòng lặp do ngữ pháp (For...Next). Cái này dễ bẹt, đúng là bài tập căn bản, trình độ các bạn ra bài trên không cần lý tới nữa.

2. Vòng lặp do lô gic Toán số. Mục đích để tránh sự lặp lại vô ích. Tương tự như lấy tổng các số từ 1 đến n. Ta có thể làm vòng lăp đơn giản công lũy tiền n lần. Nhưng lô gic toán số cho biết chỉ cần làm con toán n*(n+1)/2.
Vì vậy, trước khi làm, phải đặt và giải quyết câu hỏi:
Trong 100 năm, có thể có chu lỳ lặp lại? Và nếu có thì chu kỳ ra sao, giải quyết cách nào theo ngữ pháp?
 
Upvote 0
NgàyThứDelta
12/29/20006
3/29/20026455
8/29/20036518
10/29/20046427
7/29/20056273
12/29/20066518
8/29/20086609
5/29/20096273
1/29/20106245
10/29/20106273
7/29/20116273
3/29/20136609
8/29/20146518
5/29/20156273
1/29/20166245
7/29/20166182
12/29/20176518
3/29/20196455
5/29/20206427
1/29/20216245
10/29/20216273
7/29/20226273
12/29/20236518
3/29/2024691
8/29/20256518
5/29/20266273
1/29/20276245
10/29/20276273
12/29/20286427
3/29/20306455
8/29/20316518
10/29/20326427
7/29/20336273
12/29/20346518
8/29/20366609
5/29/20376273
1/29/20386245
10/29/20386273
7/29/20396273
3/29/20416609
8/29/20426518
5/29/20436273
1/29/20446245
7/29/20446182
12/29/20456518
3/29/20476455
 
Upvote 0
2. Vòng lặp do lô gic Toán số. Mục đích để tránh sự lặp lại vô ích.
Logic thông thường: 36525 ngày, lặp 36525 lần.
Thêm "logic toán số":
Logic 1: Một tuần chỉ có 1 chủ nhật. Nếu lặp theo tuần thì chỉ 5200 tuần & 5200 vòng lặp
Logic 2: Một tháng chỉ có 1 ngày cuối tháng. Nếu lặp theo tháng thì chỉ lặp 1200 vòng lặp
Logic 3: 1 năm chỉ có 7 tháng có ngày 31. Nếu tìm cách lặp theo logic này thì chỉ lặp 700 vòng lặp.

Vậy nên chọn theo logic 3.
 
Upvote 0
M. Bing hướng dẫn mình xài 3 vòng lặp . . . (!)
& hắn ta lấy ngày 0 của tháng kế tiếp (2, 4, . . . ,1)

Xin chúc các bạn vui & khỏe!
 
Upvote 0
Logic thông thường: 36525 ngày, lặp 36525 lần.
Thêm "logic toán số":
Logic 1: Một tuần chỉ có 1 chủ nhật. Nếu lặp theo tuần thì chỉ 5200 tuần & 5200 vòng lặp
Logic 2: Một tháng chỉ có 1 ngày cuối tháng. Nếu lặp theo tháng thì chỉ lặp 1200 vòng lặp
Logic 3: 1 năm chỉ có 7 tháng có ngày 31. Nếu tìm cách lặp theo logic này thì chỉ lặp 700 vòng lặp.

Vậy nên chọn theo logic 3.
Mượn code của bạn cantl và theo gợi ý của anh @ptm0412 tôi code lại thế này.
Mã:
Option Explicit

Sub zzz()
Dim Thang, t&, y&, m&, CurrentDate As Date
Thang = Array(1, 3, 5, 7, 8, 10, 12)
For y = 2000 To 2099
    For m = LBound(Thang) To UBound(Thang)
        CurrentDate = DateSerial(y, Thang(m), 31)
        If Weekday(CurrentDate) = vbSunday Then
            t = t + 1
            Sheet2.Range("A" & t) = CurrentDate
        End If
    Next
Next
End Sub
kết quả chạy code được 98 đáp án.
 
Upvote 0
Em nhặt nhạnh và chắp vá lại các ý kiến luôn.
Array nhanh thật!!!

Mã:
Option Explicit

Sub zzz()
    Dim Thang, t&, y&, m, CurrentDate As Date
    Dim List31Sunday()
    Dim Count&, i&
    Dim tmr#
    tmr = Timer
    Count = 0
    Thang = Array(1, 3, 5, 7, 8, 10, 12)
    For y = 2001 To 2100
        For Each m In Thang
            i = i + 1
            CurrentDate = DateSerial(y, m, 31)
            If CurrentDate Mod 7 = 1 Then
                Count = Count + 1
                ReDim Preserve List31Sunday(1 To Count)
                List31Sunday(Count) = CurrentDate
            End If
        Next
    Next
    Range("A1").Resize(Count, 1) = Application.Transpose(List31Sunday)
    MsgBox "lap " & i & " vong lap trong " & ((Timer - tmr) * 1000) & " miligiay"
End Sub
 
Upvote 0
Nếu xài hàm của office 365 em sẽ duyệt như sau
Mã:
=LET(a,SEQUENCE(100,,2001),c,MAP(a,LAMBDA(x,TEXTJOIN(",",1,CHOOSE(WEEKDAY(DATE(x,3,31),1),3,8,{5,13},10,7,12,"")+12*(x-2000)))),TEXT(DATE(2000,TEXTSPLIT(TEXTJOIN(",",1,IF(WEEKDAY(MIN(a)&"/1/31",1)=1,1,""),IFERROR(c,"")),,","),31),"ddd dd mm yyyy"))

Chỗ này em chưa biết cách xử lý mảng 2 chiều nên dùng textsplit, textjoin để nối và tách chuỗi.
 
Upvote 0
Còn đây là ChartGPT 4 dạy cho mình:
PHP:
Sub ListSundays()
    Dim i As Integer, J As Integer, K As Integer, Year As Integer
    Dim Month As Integer, LastDay As Integer, DayOfWeek As Integer, Count As Integer
    Dim sundays() As String
    ReDim sundays(0 To 0)
   
    Count = 0
    For i = 20 To 99
        Year = i * 100
        For J = 1 To 12
            Month = J
            LastDay = Day(DateSerial(Year, Month + 1, 0))
            For K = 1 To LastDay
                DayOfWeek = Weekday(DateSerial(Year, Month, K))
                If K = 31 And DayOfWeek = 1 Then
                    ReDim Preserve sundays(0 To Count)
                    sundays(Count) = Format(DateSerial(Year, Month, K), "dd/mm/yyyy")
                    Count = Count + 1
                End If
            Next K
        Next J
    Next i
    If Count = 0 Then
        MsgBox "Không có ngày 31 nào trong các tháng của thế kỷ này roi vào chủ nhật."
    Else
        MsgBox "Các ngày 31 trong các tháng của thế kỷ này roi vào chủ nhật là: " & Join(sundays, ", ")
    End If
End Sub
 
Upvote 0
(/ậy bạn nghỉ Y2K là như thế nào?
 
Upvote 0
Bi giờ bài này là "Bài tập về ngày tháng cho lập trình nâng cao" chứ đâu phải là "Vòng lặp cho người mới bắt đầu" nữa.

Bài tập căn bản phải đại khái như vầy:
- Đặt mảng a(1 To 100)
- Ghi số ngẫu nhiên 1~1000 cho 100 phần tử này (gợi ý: dùng hàm Application.RandBetween(1,1000))
- Tìm vị trí số lớn nhất trong mảng
 
Upvote 0
Em nhặt nhạnh và chắp vá lại các ý kiến luôn.
Array nhanh thật!!!
If CurrentDate Mod 7 = 1 Then
Làm sao biết CurrentDate Mod 7 = 1 chắc chắn là chủ nhật?
(/ậy bạn nghỉ Y2K là như thế nào?
Y2K là sự cố năm 2000, không liên quan gì đến thế kỷ, nó là năm kế tiếp năm cuối cùng của hệ thống máy tính có 2 số đầu là 19. Qua năm 2000 thì máy tính nhầm 2000 với 1900 sinh ra rối chuyện gọi là sự cố Y2K.
Nếu thế kỷ 21 bắt đầu từ năm 2000, đến năm 2099 là đủ 100 năm; vậy thế kỷ 1 bắt đầu từ năm nào? Hay là bắt đầu từ năm 0 đến hết năm 99 mà vẫn đủ 100 năm? Nghĩa là thế gian này có năm 0 và năm 0 có đủ 365 ngày như những năm khác á?
 
Lần chỉnh sửa cuối:
Upvote 0
Upvote 0
Làm sao biết CurrentDate Mod 7 = 1 chắc chắn là chủ nhật?
Em thử với hàm mod trên bảng tính. Vô tình nó ứng thứ 2 đến thứ 6 là 2->6, thứ 7 là 0, và 1 là chủ nhật.

Vậy phát sinh vấn đề: có phải "thứ trong tuần" ra đời là do được chế biến từ phép chia dư 7 ngày hay không mà lại trùng hợp thế. :wallbash: :wallbash: :wallbash:
 
Upvote 0
Mã:
Sub DanhSachNgay31ChuNhatCuoiThang()
    Dim yearStart As Integer
    Dim yearEnd As Integer
    Dim currentYear As Integer
    Dim currentDate As Date
    
    ' Thiết lập khoảng năm (từ năm 2000 đến 2099)
    yearStart = 2000
    yearEnd = 2099
    
    ' Xóa nội dung trong cột A, B, C nếu có
    Columns("A:C").ClearContents
    
    ' Thiết lập tiêu đề
    Range("A1").Value = "Năm"
    Range("B1").Value = "Tháng"
    Range("C1").Value = "Ngày 31"
    
    ' Bắt đầu từ hàng thứ 2
    Dim row As Integer
    row = 2
    
    ' Lặp qua mỗi năm từ yearStart đến yearEnd
    For currentYear = yearStart To yearEnd
        ' Lặp qua mỗi tháng từ tháng 1 đến tháng 12
        For currentMonth = 1 To 12
            ' Tạo ngày 1 của tháng tiếp theo
            currentDate = DateSerial(currentYear, currentMonth + 1, 1)
            ' Trừ đi 1 ngày để lấy ngày cuối cùng của tháng hiện tại
            currentDate = currentDate - 1
            ' Kiểm tra xem ngày cuối cùng của tháng đó rơi vào Chủ nhật và có phải là ngày 31 không
            If Weekday(currentDate) = vbSunday And Day(currentDate) = 31 Then
                ' Đưa thông tin vào cột A, B, C
                Cells(row, 1).Value = currentYear
                Cells(row, 2).Value = currentMonth
                Cells(row, 3).Value = Format(currentDate, "dd/mm/yyyy")
                row = row + 1
            End If
        Next currentMonth
    Next currentYear
End Sub
Chuẩn không phải chỉnh
 
Upvote 0
vậy em sửa lại đoạn này là được
yearStart = 2001
yearEnd = 2100

Mã:
Sub DanhSachNgay31ChuNhatCuoiThang()
    Dim yearStart As Integer
    Dim yearEnd As Integer
    Dim currentYear As Integer
    Dim currentDate As Date
   
    ' Thiết lập khoảng năm (từ năm 2001 đến 2100)
    yearStart = 2001
    yearEnd = 2100
   
    ' Xóa nội dung trong cột A, B, C nếu có
    Columns("A:C").ClearContents
   
    ' Thiết lập tiêu đề
    Range("A1").Value = "Năm"
    Range("B1").Value = "Tháng"
    Range("C1").Value = "Ngày 31"
   
    ' Bắt đầu từ hàng thứ 2
    Dim row As Integer
    row = 2
   
    ' Lặp qua mỗi năm từ yearStart đến yearEnd
    For currentYear = yearStart To yearEnd
        ' Lặp qua mỗi tháng từ tháng 1 đến tháng 12
        For currentMonth = 1 To 12
            ' Tạo ngày 1 của tháng tiếp theo
            currentDate = DateSerial(currentYear, currentMonth + 1, 1)
            ' Trừ đi 1 ngày để lấy ngày cuối cùng của tháng hiện tại
            currentDate = currentDate - 1
            ' Kiểm tra xem ngày cuối cùng của tháng đó rơi vào Chủ nhật và có phải là ngày 31 không
            If Weekday(currentDate) = vbSunday And Day(currentDate) = 31 Then
                ' Đưa thông tin vào cột A, B, C
                Cells(row, 1).Value = currentYear
                Cells(row, 2).Value = currentMonth
                Cells(row, 3).Value = Format(currentDate, "dd/mm/yyyy")
                row = row + 1
            End If
        Next currentMonth
    Next currentYear
End Sub
 
Upvote 0
Cỡ trình độ quý vị mà viết code như vậy xoàng quá, thiếu tư duy ưu hóa thuật toán.

1. Kiểu 1 - dùng chủ nhật làm mốc.
Tìm chủ nhật đầu tiên, vòng lặp đi từ ngày ấy đến ngày cuối của năm cuối, step 7.
2001->2100: vòng lặp chạy 5200 lượt. Nhiều hơn hiện tại (hiện tại là 1200 lượt), nhưng tránh được nhiều con toán tính.

2. Kiểu 2 - dùng ngày 31 làm mốc.
Đăt a = Array(1, 2, 2, 2, 1, 2, 2) ---> số thàng 12>1>3>5>7>8>10>12
Đặt ngày bắt đầu là 31/12 của năm trước.
Vòng lặp Do While thay vì For
Vòng lăp For Each st In a
Dùng hàm EDate của WorkSheet để nhảy sang tháng kế
Nếu quá năm cuối thì thoát cả hai vòng lặp
Nếu là chủ nhật thì in ra
Next st
Loop
Như vậy code chạy 700 lượt cho 100 năm và code giảm được một vài con toán.
 
Upvote 0

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

Back
Top Bottom