Thử lập trình (1 người xem)

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

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

VetMini

Đang đi tìm hòn đá
Tham gia
21/12/12
Bài viết
17,868
Được thích
24,782
Nghề nghiệp
Thầy bói bài ta
Lâu quá không thấy ai đố lập trình.
Ăn cắp được đề bài này đem ra thử xem:

Làm cách nào, cho một nguyên không lớn lắm vẽ được một hình vuông bằng số. Số lớn lồng số nhỏ.
Ví dụ ln = 5 thì hình vẽ ra như sau:

1647005348792.png

Xin lỗi tôi không thể diễn giải thêm vì diễn giải nhiều hơn sẽ trở thành gợi ý cách giải.

Quý vị có thể giải bằng công thức hay VBA.

Điều kiện: giả sử cấu hình máy rất siêu và số n không lớn lắm. Tức là quý vị có thể thoải mái về bất cứ giải thuật nào mà quý vị có thể tưởng tượng ra, không nhất thiết phải tối ưu. Nếu bạn nào chậm chân bị người khác đưa ra giải thuật giống mình thì hãy bình tĩnh "think outside the box", tìm giải thuật khác, hoặc tìm cách cải tiến code/công thức kia. Đây là bài toán thu gom các phương pháp tư duy lô gic chứ không phải thách đố tìm giải thuật tối ưu.
Hy vọng là với chi tiết này chúng ta có thể học thêm được vài giải thuật lạ đời.
.
 
Lần chỉnh sửa cuối:
Nếu VBA thì tôi nghĩ ra 3 cách:
- Chạy từ trong ra (hơi khó)
- Chạy từ ngoài vô (dễ hơn)
- Chạy từng hàng ngang (dễ nhất)
 
Xin lỗi, tôi vừa nhận ra cái đề bài trên chỉ nên dùng cho công thức.

Nếu VBA thì bị kẹt chỗ nó liên hệ đến range của bảng tính. Range của bảng tính có thể chạy ngược chạy xuôi. Và như thế thì mất đi cái thú vị của ghi số: lúc vẽ thì từ trái sang phải (hoặc cả dòng như một chuỗi); và xong một dòng mới được sang dòng kế tiếp.

Xin chỉnh lại:
Nếu viết bằng VBA thì cả cái hình vuông được vẽ trong 1 ô (cell), hoặc trên cửa sổ immediate, tuỳ chọn.

1647007905379.png
Chỉnh sửa 12/03/2022: hình trên tôi gõ bị lỗi hơi nhiều. Cột 8 dòng 3 đángb lẽ phải là số 4, thay vì 3. Và cần chèn thêm dòng 544444445 trước dòng cuối.
.
 
Lần chỉnh sửa cuối:
Nếu quanh năm suốt tháng chỉ lặp đi lặp lại vd. Nhập-Xuất-Tồn thì cái Nhập-Xuất-Tồn đó dù làm trong thời gian rảnh, ở bàn nhậu, đâu có phải là "Thư giãn".
 
Sub test(n As Long) Dim ar(): ReDim ar(1 To 2 * n - 1, 1 To 2 * n - 1) Dim r As Long, c As Long, x As Long For x = n To 1 Step -1 For r = n - x + 1 To n + x - 1 For c = n - x + 1 To n + x - 1 ar(r, c) = x Next c Next r Next x End Sub
Em nghĩ là như thế này
 
Em góp cách nghĩ của em:
Mã:
Sub test()
Call ThuLapTrinh(5)
End Sub
Sub ThuLapTrinh(n As Long)
      Dim a As Long, b As Long
      Dim i As Long, j As Long
      Dim Arr
      Range("A1").Resize(1000, 1000).Value = vbNullString
      a = n * 2 - 1
      b = n * 2 + 1
      ReDim Arr(1 To a, 1 To a)
      For i = 1 To n
            For j = 1 To b - (i * 2)
                  Arr(j + (i - 1), i) = n + (1 - i)
                  Arr(i, j + (i - 1)) = n + (1 - i)
                  Arr((n * 2) - i, (n * 2) - j - (i - 1)) = n + (1 - i)
                  Arr((n * 2) - j - (i - 1), (n * 2) - i) = n + (1 - i)
            Next j

      Next i
      Range("A1").Resize(a, a).Value = Arr
End Sub
 
Ở bài #3 tôi đã sửa đề bài. Mục đích chính là để quý vị suy nghĩ thoát ra giải pháp quen thuộc của bảng tính là mảng 2 chiều.
 
Ở bài #3 tôi đã sửa đề bài. Mục đích chính là để quý vị suy nghĩ thoát ra giải pháp quen thuộc của bảng tính là mảng 2 chiều.
Thêm 1 lần chết đau chết đớn. Đúng là không gươm không đao mà!
----------
Bài #5: Ghi nhiều lần đè lên nhau từ ngoài vào trong: Mỗi hình vuông ghi từng dòng và ghi đầy các ô. Hình vuông trong (nhỏ hơn) ghi đè bên trong hình vuông ngoài. Bạn này giỏi suy luận và giỏi về tà đạo :D
Bài #6: Ghi từ ngoài vào trong, không ghi đè. Mỗi vòng hình vuông là 1 số, ghi từ 2 góc đối diện tỏa ra 2 góc còn lại. Chỉ ghi chung quanh, không ghi ruột. Bạn này giỏi toán số cộng trừ :p
 
Dùng công thức thì tôi dùng thế này ở ô A1, kéo xuống dưới, sang ngang

=IF(OR(ROW(1:1)>5*2-1;COLUMN(A:A)>5*2-1);"";5-MIN(ROW(1:1)-1;COLUMN(A:A)-1;5*2-ROW(1:1)-1;5*2-COLUMN(A:A)-1))
Thay số 5 thành số tùy ý.
 
Lần chỉnh sửa cuối:
Ở bài #3 tôi đã sửa đề bài. Mục đích chính là để quý vị suy nghĩ thoát ra giải pháp quen thuộc của bảng tính là mảng 2 chiều.
Em nghĩ thầy đang có sự nhầm lẫn ở đây, ở bài #3 của thầy em thấy cái hình nó hiển thị dãy số không giống với hình bài #1.
 
Gọi n là cạnh hình vuông, a là chỉ số dòng, b là chỉ số cột.
Số tại vị trí (a, b) bằng [Phần nguyên của (n+1)/2] - [Số nhỏ nhất trong (a, b, n+1-a, n+1-b)] + 1
--
Rich (BB code):
A1=INT((9+1)/2)-MIN(ROW(),COLUMN(),9+1-ROW(),9+1-COLUMN())+1
 
Lần chỉnh sửa cuối:
----------
Bài #5: Ghi nhiều lần đè lên nhau từ ngoài vào trong: Mỗi hình vuông ghi từng dòng và ghi đầy các ô. Hình vuông trong (nhỏ hơn) ghi đè bên trong hình vuông ngoài. Bạn này giỏi suy luận và giỏi về tà đạo :D
Bài #6: Ghi từ ngoài vào trong, không ghi đè. Mỗi vòng hình vuông là 1 số, ghi từ 2 góc đối diện tỏa ra 2 góc còn lại. Chỉ ghi chung quanh, không ghi ruột. Bạn này giỏi toán số cộng trừ :p
Tôi giải thích như vầy:
Thuật toán bài #5 là loại thuật toán trâu bò. Cho trị đi từ số n đến 1 (1n đến n cũng đại khái vậy) và tìm đúng vị trí chúng trong mảng, ghi vào.
Thuật toán bài #5 dựa trên đúng lô gic đề bài. Vẽ từng hình vuông theo số, hình vuông số lớn bọc ngoài hình vuong số nhỏ. Lô gic này tôi nêu ra trong đề bài, thực tế đề bài cũng có thể diễn đạt bằng lô gic khác. Làm theo code của bài ấy là từ ngoài vào trong. Cách làm từ trong ra ngoài đại khái cũng vậy thôi.
Cả hai thuật toán trên, vì cần phải nhảy tùm lum cho nên bắt buộc phải dùng mảng 2 chiều.

Như vậy, thuật toán bài #5 đã giải quyết vấn đề 1 và 2 của bài #2. Vấn đề 3, dựng từng hàng ngang thì sao?
@ tác giả bài #3: tiễn điểu kinh cung. Chưa hoàn toàn chết, thuật toán hàng ngang chưa chắc đã cần mảng 2 chiều.

Và như đã nói ở đầu bài, tôi tránh không gợi ý thêm.

@các tác giả công thức bảng tính: cảm ơn các bạn góp sức. Tôi đang nghiên cứu.

Chỉnh sửa:
Tôi lại gõ nhầm.
Câu 3, "Thuật toán bài #5 dựa trên đúng lô gic đề bài" nên đọc là "Thuật toán bài #6 dựa trên đúng lô gic đề bài".
Cảm ơn tác giả bài #15 đã nhắc nhở.
 
Lần chỉnh sửa cuối:
Tôi giải thích như vầy:
Thuật toán bài #5 là loại thuật toán trâu bò. Cho trị đi từ số n đến 1 (1n đến n cũng đại khái vậy) và tìm đúng vị trí chúng trogn mảng, ghi vào.
Thuật toán bài #5 dựa trên đúng lô gic đề bài. Vẽ từng hình vuông theo số, hình vuông số lớn bọc ngoài hình vuong số nhỏ.
---------
@ tác giả bài #3: tiễn điểu kinh cung. Chưa hoàn toàn chết, thuật toán hàng ngang chưa chắc đã cần mảng 2 chiều.
Chữ đỏ chắc là #6?
tác giả #3 là ai nhỉ?
À, hình bài 3 chỉ có 8 dòng, và dòng 3 hơi sai sai
 
Cách làm từ trong ra ngoài đại khái cũng vậy thôi.
Vấn đề 3, dựng từng hàng ngang thì sao?
Code bài #5 không chạy từ trong ra ngoài được, vì thủ thuật là ghi đè. Code bài #6 thì từ trong ra ngoài được.
Dựng từng hàng ngang thì đang làm dở dang, thấy bài 3 "chỉ nên dùng cho công thức" nên té và chết (1 trong vô số lần).
 
Giải thuật cơ bản: toán Giải Tích.
Ở diễn đàn này có ít nhất là 10 người đủ khả năng toán Giải Tích để làm bài này. Sao chưa thấy ra mặt. Có lẽ do bận.

Tôi chưa xem kỹ bài #9 và #13. Những nếu họ đã viết công thức như vậy thì có lẽ đã biết được hàm số liên hệ giữa toạ độ điểm và trị số ở điểm đó: trị = f(x, y)

Hàm số f được suy ra như sau:
Nếu gọi tâm của hình vuông là điểm mốc (x0, x0) thì trị ở điểm (xi, yi) là MAX(Abs(xi-x0), Abs(yi-y0)) + trị ở điểm mốc (tức là 1). Dùng hàm dịch toạ độ điểm gốc để cộng/trừ độ lệch. Cứ vậy mà tính ra.

Tuy nhiên lập trình thì được hơn công thức ở chỗ có những mẹo nhỏ để áp dụng. Mẹo này là dùng tâm (0,0). Khỏi phải tính độ dịch gốc.

Sub HinhVuong()
' mảng là chỉ để đồng bộ với các bài trên thôi
' nếu in thẳng ra thì không cần mảng
n = 5 ' trị cần vẽ
n = n - 1 ' trung điểm là (0,0) nên các toạ độ là từ 0 đến n-1
Dim a(): ReDim a(-n To n, -n To n)
For i = -n To n
For j = -n To n
a(i, j) = Application.Max(Abs(i), Abs(j)) + 1
Next j
Next i
[a1].Resize(UBound(a) - LBound(a) + 1, UBound(a, 2) - LBound(a, 2) + 1) = a
End Sub

Vẫn còn vài thuật toán hàng ngang khác. Lão đai môn thử xem.
 
Tựa là thử lập trình, nhưng trong mục Thư Giãn, nên cháu cũng thử hàm excel góp vui :D
 

File đính kèm

...
Dựng từng hàng ngang thì đang làm dở dang, thấy bài 3 "chỉ nên dùng cho công thức" nên té và chết (1 trong vô số lần).
Dựng từng hàng ngang nè. Chú ý là nếu không có điều kiện trái qua phải, trên xuống dưới thì code có thể viết ngắn hơn. Như code bài #18, mảng trong bài này chỉ là để đồng bộ. Chính bản thân giải thuật thì có thể in thẳng ra.

Sub tt()
n = 5
n = n - 1
Dim a(): ReDim a(-n To n, -n To n)
tt2 a, n, n
[a1].Resize(UBound(a) - LBound(a) + 1, UBound(a, 2) - LBound(a, 2) + 1) = a
End Sub

Sub tt2(a(), n, ni)
For i = -n To n
a(-ni, i) = IIf(Abs(i) <= ni, ni, Abs(i)) + 1
Next i
If ni <= 0 Then Exit Sub
tt2 a, n, ni - 1
For i = -n To n
a(ni, i) = a(-ni, i)
Next i
End Sub
 
Em xin được góp chút xíu. Mong các bác chỉ giáo!!
Mã:
Sub test()
Dim n As Long
n = 8

Dim i As Long
For i = 1 To n

ThisWorkbook.Sheets(1).Range(Cells(i, i), Cells(i, n * 2 - i)).Value = n - i + 1

ThisWorkbook.Sheets(1).Range(Cells(n * 2 - i, i), Cells(n * 2 - i, n * 2 - i)).Value = n - i + 1

ThisWorkbook.Sheets(1).Range(Cells(i, i), Cells(n * 2 - i, i)).Value = n - i + 1

ThisWorkbook.Sheets(1).Range(Cells(i, n * 2 - i), Cells(n * 2 - i, n * 2 - i)).Value = n - i + 1

Next i

End Sub
 
Bài 5
Dựng từng hàng ngang nè. Chú ý là nếu không có điều kiện trái qua phải, trên xuống dưới thì code có thể viết ngắn hơn. Như code bài #18, mảng trong bài này chỉ là để đồng bộ. Chính bản thân giải thuật thì có thể in thẳng ra.

Sub tt()
n = 5
n = n - 1
Dim a(): ReDim a(-n To n, -n To n)
tt2 a, n, n
[a1].Resize(UBound(a) - LBound(a) + 1, UBound(a, 2) - LBound(a, 2) + 1) = a
End Sub

Sub tt2(a(), n, ni)
For i = -n To n
a(-ni, i) = IIf(Abs(i) <= ni, ni, Abs(i)) + 1
Next i
If ni <= 0 Then Exit Sub
tt2 a, n, ni - 1
For i = -n To n
a(ni, i) = a(-ni, i)
Next i
End Sub
Có cách nào chỉ cần 1 For và khó hơn không cần For nào :)
 
Bài 5

Có cách nào chỉ cần 1 For và khó hơn không cần For nào :)
Trên nguyên tắc, tất cả vòng lặp for, while, do,... đều có thể thay thế bằng đệ quy. :p

' Code căn bản là bài #20.
' chỉ dùng đệ quy để thay thế vòng lặp.


Sub tt()
n = 5
n = n - 1
Dim a(): ReDim a(-n To n, -n To n)
tt3 a, n, n
[a1].Resize(UBound(a) - LBound(a) + 1, UBound(a, 2) - LBound(a, 2) + 1) = a
End Sub

Sub tt3(a(), n, ni)
tt3x a, -n, n, ni, True
If ni <= 0 Then Exit Sub
tt3 a, n, ni - 1
tt3x a, -n, n, ni, False
End Sub

Sub tt3x(a(), i, n, ni, firstHalf)
If i > n Then Exit Sub
If firstHalf Then
a(-ni, i) = IIf(Abs(i) <= ni, ni, Abs(i)) + 1
Else
a(ni, i) = a(-ni, i)
End If
tt3x a, i + 1, n, ni, firstHalf
End Sub

Tuy nhiên, đệ quy cũng là một hình thức vòng lặp. Nếu bạn hỏi code mà không phải lặp lại thì tôi chịu thua.
 
Dùng FormulaR1C1 hoặc Formula, dùng công thức ở bài 9, quất 1 phát 1.
Lần này lại "kick the bucket". -.,\;
Quất 1 phát xong nó vẫn còn nằm trong bảng tính. Muốn in phải copy ra lại mảng rồi duyệt.

The wabbit kicked the bucket. The wabbit kicked the bucket.
Elmer Fudd nhảy múa ăn mừng Bugs Bunny (Looney Tunes/Merrie Melodies)
.
 
Lần chỉnh sửa cuối:
Em cũng thử đệ quy dựa vào bài #6 của em.
Mã:
Sub test3()
      Range("A1").Resize(1000, 1000).Value = vbNullString
      Call Dequy(6)
End Sub
Sub Dequy(n As Long)
      Dim Arr
      Dim i As Long, j As Long
      Dim k As Long
      k = n * 2 - 1
      ReDim Arr(1 To k, 1 To k)
      For i = 1 To k
            Arr(i, k) = n
            Arr(k, i) = n
            Arr(1, (k + 1 - i)) = n
            Arr((k + 1 - i), 1) = n
      Next i
      j = Range("A1").Value - n + 1
      If j <= 0 Then j = 1
      Cells(j, j).Resize(k, k).Value = Arr
      If n > 1 Then
            n = n - 1
            Call Dequy(n)
      End If
End Sub
 
Em cũng thử đệ quy dựa vào bài #6 của em.
...

Cái sub tt2 ở bài #20, và cái sub tt3 ở bài #23 đệ quy là có lý do của chúng.
Vì dụ (n =3 cho ngắn)
33333 -> lần chạy thứ nhất
32223 -> lần thứ nhất gọi lần thứ nhì
32123 -> lần thứ nhì gọi lẩn thứ ba : gặp số 1, không gọi tiếp nữa.
32223 -> bước tiếp theo của lần nhứ nhì, sau khi được lộn về từ lần thứ ba
33333 -> bước tiếp theo của lần nhứ nhất, sau khi được lộn về từ lần thứ nhì

Code hơi luộm thuộm là do tôi ép nó dùng mảng 2 chiều cho đồng bộ với các bài khác.
Nếu chỉ cần xuất kết quả ra cửa sổ immediate thì nó dùng mảng 1 chiều. Gọn hơn.
- tạo một mảng 2*n-1 phần tử
- lần chạy thứ nhất copy mảng và ghi n vào các phần tử
- lần chạy thứ nhì copy mảng trên và ghi n-1 vào các phần tử, chừa lại 1 phần tử ở mỗi đầu (đã sẵn ghi n)
- lần chạy thứ ba copy mảng của lần thứ nhì và ghi n-2 vào, chừa lại 2 phần tử ở mỗi đầu.
- bước tiếp theo của lần thứ nhì đã có sẵn mảng, khỏi cần tính thêm gì hết
- tương tự cho bước tiếp theo của lần thứ nhất.

Giải thuật đệ quy này đáp ứng điều kiện tính từ trái sang phải, trên xuống dưới.
 
Lần này lại "kick the bucket". -.,\;
Quất 1 phát xong nó vẫn còn nằm trong bảng tính. Muốn in phải copy ra lại mảng rồi duyệt.
Kick gì mà kick. In gì nữa mà in. Theo các bài giải bằng code phía trên thì xuống sheet là xong rồi.
 
Giải thuật đệ quy này đáp ứng điều kiện tính từ trái sang phải, trên xuống dưới.
Theo yêu cầu đề bài #3 của thầy "lúc vẽ thì từ trái sang phải (hoặc cả dòng như một chuỗi); và xong một dòng mới được sang dòng kế tiếp." thì bài #6 của em không đúng yêu cầu đề bài, #26 em cũng dựa vào #6 nên cũng không đúng yêu cầu đề bài mà thầy đưa ra rồi. Em chỉ làm để các thành viên có thêm 1 cách để tham khảo.
 
Tôi bổ sung thêm một cách dùng công thức tạo ra 1 mảng ảo trong giá trị 1 cell luôn ( không phải trong range nxn)
Ví dụ trong ô A1 điền giá trị:
=IF(ABS(ROW($1:$9)/ROW($1:$9) + TRANSPOSE(ROW($1:$9)-6))+1>TRANSPOSE(ABS(ROW($1:$9)/ROW($1:$9) + TRANSPOSE(ROW($1:$9)-6))+1),ABS(ROW($1:$9)/ROW($1:$9) + TRANSPOSE(ROW($1:$9)-6))+1,TRANSPOSE(ABS(ROW($1:$9)/ROW($1:$9) + TRANSPOSE(ROW($1:$9)-6))+1))
Ví dụ ở đây là ma trận 9x9 ( với 9=5x2-1) và 6=5+1 do vậy thay thế số 9 và 6 tương ứng với số mong muốn.

Nếu áp dụng vào VBA thì như vậy có thể coi là không dùng tí nào vòng For:
Mã:
Function RollMatrix(n As Integer)
Dim tmpMtr, myStr As String
Dim d As Integer, i As Integer
d = n * 2 - 1
i = n + 1
myStr = "IF(ABS(ROW($1:$" & d & ")/ROW($1:$" & d & ") + TRANSPOSE(ROW($1:$" & d & ")-" & i & "))+1>ABS(ROW($1:$" & d & ")-" & i & "+TRANSPOSE(ROW($1:$" & d & ")/ROW($1:$" & d & ")))+1,ABS(ROW($1:$" & d & ")/ROW($1:$" & d & ") + TRANSPOSE(ROW($1:$" & d & ")-" & i & "))+1,ABS(ROW($1:$" & d & ")-" & i & "+TRANSPOSE(ROW($1:$" & d & ")/ROW($1:$" & d & ")))+1)"
If Len(myStr) > 256 Then
MsgBox "UnSuccess"
RollMatrix = False
Exit Function
End If
tmpMtr = Evaluate(myStr)
RollMatrix = tmpMtr
End Function
 
Lần chỉnh sửa cuối:
Kick gì mà kick. In gì nữa mà in. Theo các bài giải bằng code phía trên thì xuống sheet là xong rồi.
Xuống sheet là để test các con số.

Điển hình, code giải theo toán giải tích ở bài #18, không liên quan gì đến worksheet thì như sau:

Sub HinhVuong()
n = 5 ' trị cần vẽ
pW = Space(Len(CStr(n)) + 1) ' độ rộng để in mỗi số
n = n - 1 ' trung điểm là (0,0) nên các toạ độ là từ 0 đến n-1
For i = -n To n
For j = -n To n
Rset pW = Application.Max(Abs(i), Abs(j)) + 1
Debug.Print pW;
Next j
Debug.Print
Next i
End Sub

1647096920383.png
 
Em chỉ biết cộng trừ nhân chia thôi nên mò ra được cái này chắc cũng đáp ứng được yêu cầu bài #3 của thầy.
Mã:
Sub test4()
      Call ThuLapTrinh(150)
End Sub
Sub ThuLapTrinh(n As Long)
      Dim t As Double
      t = Timer
      Dim i As Long, j As Long
      Dim a As Long, b As Long
      Dim x As Long, y As Long
      Dim Dong As String
      a = n * 2 - 1
      b = n
      For i = 1 To a + 2
            GoSub InRaNe
            Dong = ""
            If b = i Then i = i + 2
            n = Abs(b - i)
      Next i
      t = Timer - t
      MsgBox t
      Exit Sub
InRaNe:
      For j = 0 To b - 1
            x = n - j
            y = n + j
            If x = y Then
                  Dong = " " & n & " "
            ElseIf (y - x) / 2 < n Then
                  Dong = " " & n & Dong & n & " "
            ElseIf (y - x) / 2 < b Then
                  Dong = " " & (y - x) / 2 + 1 & Dong & (y - x) / 2 + 1 & " "
            End If
      Next j
      Debug.Print Dong
      Return
End Sub
 
Lần chỉnh sửa cuối:
Tác giả bài #30 giỏi tư duy trừu tượng quá.
 
Em chỉ biết cộng trừ nhân chia thôi nên mò ra được cái này chắc cũng đáp ứng được yêu cầu bài #3 của thầy.
...
Code khá lủng củng.

For i = 1 To a + 2
GoSub InRaNe
Dong = "" ' đặt reset Dong ở đây khá nguy hiểm. Tại sao không đặt nó ngay dòng đầu tiên của vòng lặp?
If b = i Then i = i + 2
n = Abs(b - i)
Next i
-----

' code này chỉ bảo đảm có một dấu cách giữa các số. Nếu n >= 10 thì hình vuông lệch hết.
' ở bài #13 tôi dùng biến pW để bảo đảm mõi số có khoảng in bằng nhau. (pW = print width)

If x = y Then
Dong = Dong & n & " "
ElseIf (y - x) / 2 < n Then
Dong = n & " " & Dong & n & " "
ElseIf (y - x) / 2 < b Then
Dong = (y - x) / 2 + 1 & " " & Dong & (y - x) / 2 + 1 & " "
End If
' nếu tôi viết code này thì tôi dùng quách một mảng. Lúc in ra thì Join thành một chuỗi.
-----

Hình như code này tính chuỗi in ra n*2 - 1 + 2 - 2, tức n*2 -1 lần. Như vậy là không lợi dụng được dạng đối xứng của hình vuông.
Lưu ý tôi giải thích ở bài #27, đệ quy không hẳn là để tránh vòng lặp. Mục đích chính ở đây là để chỉ phải tính chuỗi in ra n lần.
 
Hình như code này tính chuỗi in ra n*2 - 1 + 2 - 2, tức n*2 -1 lần. Như vậy là không lợi dụng được dạng đối xứng của hình vuông.
Lưu ý tôi giải thích ở bài #27, đệ quy không hẳn là để tránh vòng lặp. Mục đích chính ở đây là để chỉ phải tính chuỗi in ra n lần.
Bài này không phải đệ quy và cũng không phải hình vuông đối xứng, nếu đối xứng thì không đáp ứng được yêu cầu bài #3 của thầy. Em mượn pw của thầy và rút gọn một số biểu thức trong vòng lặp để dễ nhìn hơn.
Mã:
Sub test4()
      Call ThuLapTrinh(10)
End Sub
Sub ThuLapTrinh(n As Long)
      Dim t As Double
      t = Timer
      Dim i As Long, j As Long
      Dim a As Long, b As Long
      Dim Dong As String
      Dim pw As String
      pw = Space(Len(CStr(n)) + 1)
      a = n * 2 - 1
      b = n
      For i = 1 To a + 2
            Dong = ""
            GoSub InRaNe
            If b = i Then i = i + 2
            n = Abs(b - i)
      Next i
      t = Timer - t
      MsgBox t
      Exit Sub
InRaNe:
      For j = 0 To b - 1
            If j = 0 Then
                  RSet pw = n
                  Dong = pw
            ElseIf j < n Then
                  RSet pw = n
                  Dong = pw & Dong & pw
            ElseIf j < b Then
                  RSet pw = j + 1
                  Dong = pw & Dong & pw
            End If
      Next j
      Debug.Print Dong
      Return
End Sub
 
Lần chỉnh sửa cuối:
Bài này không phải đệ quy và cũng không phải hình vuông đối xứng, nếu đối xứng thì không đáp ứng được yêu cầu bài #3 của thầy. Em mượn pw của thầy và rút gọn một số biểu thức trong vòng lặp để dễ nhìn hơn.
...

Vậy thì tôi nói thẳng ra. Lúc đầu tôi chỉ gợi ý để bạn tự suy nghĩ.

Bài của bạn, vì bạn dùng nguyên 1 chuỗi để in 1 dòng cho nên điều kiện "trên xuống dưới" thì thoả. Và điều kiện trái qua phải không còn áp dụng. Tương tự vậy, nếu có bạn nào dùng chỉ một chuỗi để chứa hết kết quả - mỗi dòng cách nhau bằng ký tự newline; thì điều kiện trái phải, trên xuống không áp dụng. Đã không thể áp dụng thì không thể gọi là không thoả. Bạn làm một việc mà luật pháp không thể áp dụng thì không thể coi là phạm pháp.

Bây giờ, với code trên, bạn làm thế nào để giảm thiểu số lượt tính?
 
Vậy thì tôi nói thẳng ra. Lúc đầu tôi chỉ gợi ý để bạn tự suy nghĩ.

Bài của bạn, vì bạn dùng nguyên 1 chuỗi để in 1 dòng cho nên điều kiện "trên xuống dưới" thì thoả. Và điều kiện trái qua phải không còn áp dụng. Tương tự vậy, nếu có bạn nào dùng chỉ một chuỗi để chứa hết kết quả - mỗi dòng cách nhau bằng ký tự newline; thì điều kiện trái phải, trên xuống không áp dụng. Đã không thể áp dụng thì không thể gọi là không thoả. Bạn làm một việc mà luật pháp không thể áp dụng thì không thể coi là phạm pháp.

Bây giờ, với code trên, bạn làm thế nào để giảm thiểu số lượt tính?
Theo như thầy Mỹ nói em giỏi cộng trừ nhân chia, cách giải của em chỉ quanh quẩn j<0, j<n, j<b, nên nhìn không chuyên bằng các bạn giỏi toán (em dốt toán). Yêu cầu bài #3 của thầy "lúc vẽ thì từ trái sang phải (hoặc cả dòng như một chuỗi); và xong một dòng mới được sang dòng kế tiếp", thì em nghĩ bài #35 em đã đáp ứng được rồi, nếu đề chỉ yêu cầu vẽ từ trái sang phải thì em sẽ tìm giải pháp khác.
Về hiệu suất thì em đã thử so sánh tốc độ tính toán code của thầy đưa ra ở bài #31 và code của em ở bài #35 thì em thấy code của em kết quả trả về nhanh hơn, do em tạo ra chuỗi rồi mới in, còn thầy in từng chữ từ trái sang phải. Thầy có thể cho em tham khảo dựa vào bài #31, nếu chuyển sang cả dòng như một chuỗi rồi in thì thầy code như thế nào được không?
 
...
Về hiệu suất thì em đã thử so sánh tốc độ tính toán code của thầy đưa ra ở bài #31 và code của em ở bài #35 thì em thấy code của em kết quả trả về nhanh hơn, do em tạo ra chuỗi rồi mới in, còn thầy in từng chữ từ trái sang phải. Thầy có thể cho em tham khảo dựa vào bài #31, nếu chuyển sang cả dòng như một chuỗi rồi in thì thầy code như thế nào được không?
Cái vụ timer là con đẻ của GPE. Tôi sẽ không nói đến nó ở đây.
Nếu bạn không thoát được nó thì có lẽ chủ tâm và mục đích của bạn chỉ là lập trình VBA. Chủ tâm tôi dẫn dắt các bạn muốn học lập trình là loại lập trình tổng quát, ước tính bằng độ phức tạp của thuật toán. Và chủ yếu của bài này là giảm số lần tính.

Hình vuông của đề bài rõ ràng là có tính đối xứng. Đối xứng qua trục, và đối xứng qua tâm. Đề bài bắt trái qua phải, trên xuống dưới là làm khó cho các giải pháp đối qua đường chéo và đối qua tâm. Đối qua tâm thì chỉ cần tính 1/8 hình và dùng phép chiếu copy ra chỗ còn lại; cao tay hơn thì chỉ cần tính nửa đường chéo, chiếu ra nguyên đường chéo, và dùng phép xoay, quét 90 độ để ra chỗ còn lại. Các ngôn ngữ có Lambda chơi cái chiêu này rất đẹp (tôi chưa thử nhưng nhìn lô gic thì thấy vậy).

Thuật toán trái qua phải, trên xuống dưới sẽ như sau:
- Dùng một mảng chuỗi sc( (1 to 2*n-1)
- Cộng thêm một mảng chuỗi sd(2 To n). Tức là nửa hình vuông, dưới đường trung tuyến ngang.
- Vòng lặp i1 = n to 1 step -1, dùng để in dòng và nạp mảng chuỗi
-- Vòng lặp i2 = 1 to n. Tính trị và nạp vào sc(i1) đến sc(n). Cứ mỗi trị thì chuyển thành chuỗi copy qua sc(n+1) đến sc(2*n -i1).
-- Dungn hàm join, chuyển sc thành chuỗi để in. Nếu i1 > 1 thì copy vào sd(i1)
- Vòng lặp i1 = 2 To n) in sd(i1)
Nếu dùng đệ quy thay cho vòng lặp i1 đầu thì không phải làm thêm cái vòng lặp i1 thứ hai, và chỉ cần 1 chuỗi không phải lập mảng chuỗi sd bởi vì chuỗi được chứa trong ngăn xếp (stack), không bị mất.
.
 
Lần chỉnh sửa cuối:

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

Back
Top Bottom