Kính nhờ giúp làm gọn vòng Do While Loop để macro chạy nhanh hơn (1 người xem)

Liên hệ QC

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

TayMonKhanh

Thành viên mới
Tham gia
9/11/08
Bài viết
34
Được thích
5
Em có 1 bảng tính như file đính kèm

Cấu trúc nó như sau:

Khi cho ô “Y chọn” chạy từ giá trị “min Y” đến giá trị “max Y” thì giá trị ô “Binh2” sẽ tăng dần lên, khi ô “Y chọn” tăng đến 1 giá trị nào đó thì giá trị ô “Binh2” = giá trị ô “Binh1”.

Mục đích của em là tìm giá trị ô “Y chọn” tại thời điểm giá trị ô “Binh2” = giá trị ô “Binh1”.

Mong muốn là tìm được “Binh2”= “Binh1”, nhưng em không rành macro lắm nên em chỉ mong tìm được vị trí “Binh2”- “Binh1” < 0.1 là mừng lắm rồi.

Em lập macro như sau:

Sheets("Sheet1").Range("N25").Value = Sheets("Sheet1").Range("N26").Value ‘Ymin

Do While Sheets("Sheet1").Range("S25").Value > 0.1 ‘sai so Binh 2 va Binh 1

Sheets("Sheet1").Range("N25").Value = Sheets("Sheet1").Range("N25").Value + 3 ‘buoc nhay

Loop

Lập như vầy thì nó có 2 vấn đề:

1. Bước nhảy lần lượt bằng 3 thì khi gặp bảng tính có biên độ Y lớn thì macro chạy rất lâu.

2. Khi gặp bảng tính có biên độ Y nhỏ thì bước nhảy Y lại quá lớn, đôi khi macro nhảy qua 1 nấc là giá trị Binh2 đã vượt xa độ sai số mà em mong muốn.


Em muốn chỉnh sửa sao cho:

Ban đầu cho vòng Do While chạy với bước nhảy thật lớn. Sau khi tìm được vị trí có “Binh2” gần bằng “Binh1” cho vòng Do While chạy với bước nhảy nhỏ hơn… cứ như vậy lặp lại vài lần để cho kết quả tính chính xác và thời gian tính được ngắn hơn.


Kính nhờ các Anh và các bạn hỗ trợ.


Trân trọng!
 

File đính kèm

Em có 1 bảng tính như file đính kèm

Cấu trúc nó như sau:

Khi cho ô “Y chọn” chạy từ giá trị “min Y” đến giá trị “max Y” thì giá trị ô “Binh2” sẽ tăng dần lên, khi ô “Y chọn” tăng đến 1 giá trị nào đó thì giá trị ô “Binh2” = giá trị ô “Binh1”.

Mục đích của em là tìm giá trị ô “Y chọn” tại thời điểm giá trị ô “Binh2” = giá trị ô “Binh1”.

Mong muốn là tìm được “Binh2”= “Binh1”, nhưng em không rành macro lắm nên em chỉ mong tìm được vị trí “Binh2”- “Binh1” < 0.1 là mừng lắm rồi.

Em lập macro như sau:

Sheets("Sheet1").Range("N25").Value = Sheets("Sheet1").Range("N26").Value ‘Ymin

Do While Sheets("Sheet1").Range("S25").Value > 0.1 ‘sai so Binh 2 va Binh 1

Sheets("Sheet1").Range("N25").Value = Sheets("Sheet1").Range("N25").Value + 3 ‘buoc nhay

Loop

Lập như vầy thì nó có 2 vấn đề:

1. Bước nhảy lần lượt bằng 3 thì khi gặp bảng tính có biên độ Y lớn thì macro chạy rất lâu.

2. Khi gặp bảng tính có biên độ Y nhỏ thì bước nhảy Y lại quá lớn, đôi khi macro nhảy qua 1 nấc là giá trị Binh2 đã vượt xa độ sai số mà em mong muốn.


Em muốn chỉnh sửa sao cho:

Ban đầu cho vòng Do While chạy với bước nhảy thật lớn. Sau khi tìm được vị trí có “Binh2” gần bằng “Binh1” cho vòng Do While chạy với bước nhảy nhỏ hơn… cứ như vậy lặp lại vài lần để cho kết quả tính chính xác và thời gian tính được ngắn hơn.


Kính nhờ các Anh và các bạn hỗ trợ.


Trân trọng!
Dùng định luật hàm liên tục, quên mất tên
Mã:
Sub GPE()
  Dim yMax As Double, yMin As Double, y As Double, dMax As Double, dMin As Double, d As Double, e As Double
  e = 0.000000000001  'Sai so cho phep
  d = e + 1
  Application.ScreenUpdating = False
  With Sheets("Sheet1")
    yMin = .Range("N26").Value
    yMax = .Range("N27").Value
 
    .Range("N25").Value = yMin
    dMin = .Range("S27").Value - .Range("S26").Value
    If Abs(dMin) <= e Then GoTo Thoat
 
    .Range("N25").Value = yMax
    dMax = .Range("S27").Value - .Range("S26").Value
    If Abs(dMax) <= e Then GoTo Thoat
 
    If dMin * dMax < 0 Then
Trolai:
      y = (yMax + yMin) / 2
      .Range("N25").Value = y
      d = .Range("S27").Value - .Range("S26").Value
      If Abs(d) <= e Then
        Exit Sub
      Else
        If dMin * d < 0 Then yMax = y Else yMin = y
        GoTo Trolai
      End If
    Else
      MsgBox ("Khong co nghiem thoa dieu kien")
      .Range("N25").Value = yMin
      GoTo Thoat
    End If
  End With
Thoat:
  Application.ScreenUpdating = True
End Sub
 

File đính kèm

Upvote 0
Dùng định luật hàm liên tục, quên mất tên
Mã:
Sub GPE()
  Dim yMax As Double, yMin As Double, y As Double, dMax As Double, dMin As Double, d As Double, e As Double
  e = 0.000000000001  'Sai so cho phep
  d = e + 1
  Application.ScreenUpdating = False
  With Sheets("Sheet1")
    yMin = .Range("N26").Value
    yMax = .Range("N27").Value
 
    .Range("N25").Value = yMin
    dMin = .Range("S27").Value - .Range("S26").Value
    If Abs(dMin) <= e Then GoTo Thoat
 
    .Range("N25").Value = yMax
    dMax = .Range("S27").Value - .Range("S26").Value
    If Abs(dMax) <= e Then GoTo Thoat
 
    If dMin * dMax < 0 Then
Trolai:
      y = (yMax + yMin) / 2
      .Range("N25").Value = y
      d = .Range("S27").Value - .Range("S26").Value
      If Abs(d) <= e Then
        Exit Sub
      Else
        If dMin * d < 0 Then yMax = y Else yMin = y
        GoTo Trolai
      End If
    Else
      MsgBox ("Khong co nghiem thoa dieu kien")
      .Range("N25").Value = yMin
      GoTo Thoat
    End If
  End With
Thoat:
  Application.ScreenUpdating = True
End Sub

Cảm ơn anh HieuCD rất nhiều. Đẳng cấp thật. Em không hiểu hết ý nghĩa của code nhưng nhấn cái rẹt là ra kết quả ngay. Cảm ơn HieuCD rất nhiều,
 
Upvote 0
Lỗi chậm là do các cái này:
heets("Sheet1").Range("N25").Value = Sheets("Sheet1").Range("N25").Value + 3 ‘buoc nhay

Thay nó bằng biến, hoặc Array, hoặc đổi lại cách giải thì sẽ vẫn Do Loop mà cải thiện tốc độ
 
Upvote 0
Lỗi chậm là do các cái này:
heets("Sheet1").Range("N25").Value = Sheets("Sheet1").Range("N25").Value + 3 ‘buoc nhay

Thay nó bằng biến, hoặc Array, hoặc đổi lại cách giải thì sẽ vẫn Do Loop mà cải thiện tốc độ
Đang ngóng xem dùng biến hoặc Arr như thế nào
 
Upvote 0
Dùng định luật hàm liên tục, quên mất tên
Mã:
Sub GPE()
  Dim yMax As Double, yMin As Double, y As Double, dMax As Double, dMin As Double, d As Double, e As Double
  e = 0.000000000001  'Sai so cho phep
  d = e + 1
  Application.ScreenUpdating = False
  With Sheets("Sheet1")
    yMin = .Range("N26").Value
    yMax = .Range("N27").Value
 
    .Range("N25").Value = yMin
    dMin = .Range("S27").Value - .Range("S26").Value
    If Abs(dMin) <= e Then GoTo Thoat
 
    .Range("N25").Value = yMax
    dMax = .Range("S27").Value - .Range("S26").Value
    If Abs(dMax) <= e Then GoTo Thoat
 
    If dMin * dMax < 0 Then
Trolai:
      y = (yMax + yMin) / 2
      .Range("N25").Value = y
      d = .Range("S27").Value - .Range("S26").Value
      If Abs(d) <= e Then
        Exit Sub
      Else
        If dMin * d < 0 Then yMax = y Else yMin = y
        GoTo Trolai
      End If
    Else
      MsgBox ("Khong co nghiem thoa dieu kien")
      .Range("N25").Value = yMin
      GoTo Thoat
    End If
  End With
Thoat:
  Application.ScreenUpdating = True
End Sub

Kính gửi anh Hiếu. Macro của anh hoạt động rất tốt, không có gì để bàn. Nhưng do muốn tìm hiểu nên anh HieuCD có thể giải thích giúp ý nghĩa các đoạn lệnh này được không:

1./ If dMin * dMax < 0 Then
2./ If dMin * d < 0 Then yMax = y Else yMin = y

Điều kiện dMin * dMax < 0 thật ra mình chưa hiểu nhằm loại trừ điều kiện gì của bài toán?

Điều kiện dMin * d < 0 theo mình hiểu là anh HieuCD đưa vào để nhận diện giá trị y cần tìm nó nằm bên trái hay bên phải giá trị (yMax + yMin) / 2, nhưng mình không hiểu tại sao là dMin * d ?

Cảm ơn anh Hiếu rất nhiều!
 
Upvote 0
Điều kiện dMin * dMax < 0 thật ra mình chưa hiểu nhằm loại trừ điều kiện gì của bài toán?
Cái đó chỉ là Đại số mà. Tích của 2 số thực khác dấu thì luôn âm (<0).
Bạn xem bài toán của mình, trường hợp nào thỏa mãn bất đẳng thức đó thì nó là 2 số thực khác dấu.
 
Upvote 0
Kính gửi anh Hiếu. Macro của anh hoạt động rất tốt, không có gì để bàn. Nhưng do muốn tìm hiểu nên anh HieuCD có thể giải thích giúp ý nghĩa các đoạn lệnh này được không:

1./ If dMin * dMax < 0 Then
2./ If dMin * d < 0 Then yMax = y Else yMin = y

Điều kiện dMin * dMax < 0 thật ra mình chưa hiểu nhằm loại trừ điều kiện gì của bài toán?

Điều kiện dMin * d < 0 theo mình hiểu là anh HieuCD đưa vào để nhận diện giá trị y cần tìm nó nằm bên trái hay bên phải giá trị (yMax + yMin) / 2, nhưng mình không hiểu tại sao là dMin * d ?

Cảm ơn anh Hiếu rất nhiều!
.Range("N25").Value = yMin
dMin = .Range("S27").Value - .Range("S26").Value

.Range("N25").Value = yMax
dMax = .Range("S27").Value - .Range("S26").Value

.Range("N25").Value = y
d = .Range("S27").Value - .Range("S26").Value

Mục tiêu là tìm giá trị y với: yMax >= y >= yMin sao cho chênh lệch d tiến tới số không

Theo tính chất của hàm liên tục: d = f(y) với
dMax = f(yMax) và dMin = f(yMin)
ứng với 1 giá trị d nằm trong khoảng dMin và dMax sẽ tồn tại ít nhất 1 giá trị y nằm trong khoảng yMin và yMax sao cho
d = f(y)
Bài toán của bạn tương đối đơn giản nên mình lượt bớt giải thuật biến dị và di truyền, mình coi như hàm d = f(y) đồng biến trong khoảng yMin và yMax, do đó muốn có nghiệm d bằng không với dMax > d > dMin thì dMin và dMax phải trái dấu , xét theo điều kiện dMin * dMax < 0

If dMin * d < 0 Then yMax = y Else yMin = y để cho kết quả dMin * dMax < 0

Nếu hàm d = f(y) không đồng biến thì code phức tạp hơn nhằm tránh bỏ sót nghiệm
 
Upvote 0
Cảm ơn anh Befaint
Vì anh cũng là cao thủ toán nên nhận diện ra ngay. Còn mình thì đọc rất nhiều lần mà không hiểu. Hồi học đại học mình không hiểu nổi việc tốn nhiều thời gian để đọc hiểu mấy cái tiên đề, định lý kia là để làm cái quái gì. Đi làm rồi nhìn lại cũng chẳng hiểu hồi xưa tốn rất nhiều thời gian để học mấy cái lượng giác, tích phân.v.v. để phục vụ cho công việc gì sau này.

Giải thuật nhị phân cũng đã nghe qua, nhưng cũng không hiểu nó dùng để làm gì. Bây giờ nghe anh Vetmini gọi nó là giải thuật nhị phân nên mới lên mạng tìm đọc.

Sau vụ này mới thấy lợi hại của các món này, đã có cái nhìn khác hơn về các giải thuật.
 
Upvote 0
.Range("N25").Value = yMin
dMin = .Range("S27").Value - .Range("S26").Value

.Range("N25").Value = yMax
dMax = .Range("S27").Value - .Range("S26").Value

.Range("N25").Value = y
d = .Range("S27").Value - .Range("S26").Value

Mục tiêu là tìm giá trị y với: yMax >= y >= yMin sao cho chênh lệch d tiến tới số không

Theo tính chất của hàm liên tục: d = f(y) với
dMax = f(yMax) và dMin = f(yMin)
ứng với 1 giá trị d nằm trong khoảng dMin và dMax sẽ tồn tại ít nhất 1 giá trị y nằm trong khoảng yMin và yMax sao cho
d = f(y)
Bài toán của bạn tương đối đơn giản nên mình lượt bớt giải thuật biến dị và di truyền, mình coi như hàm d = f(y) đồng biến trong khoảng yMin và yMax, do đó muốn có nghiệm d bằng không với dMax > d > dMin thì dMin và dMax phải trái dấu , xét theo điều kiện dMin * dMax < 0

If dMin * d < 0 Then yMax = y Else yMin = y để cho kết quả dMin * dMax < 0

Nếu hàm d = f(y) không đồng biến thì code phức tạp hơn nhằm tránh bỏ sót nghiệm

Cảm ơn anh HieuCD đã giải thích rất tường minh.

Mình hỏi vậy vì mình không biết là anh đã lược bỏ bớt do bài toán mình đặt ra ban đầu là đơn giản, khi đọc các dòng code mình băn khoăn vì cảm thấy cái code này nó chỉ đúng khi hàm số là đồng biến.

Lỡ rồi thì làm phiền anh thêm 1 tí nữa. Ví dụ như khi hàm số y=f(x) không đồng biến như file đính kèm. Để tìm giá trị x tương ứng với giá trị ymax, bao nhiêu năm nay mình giải quyết như sau: mình cho tính ra hết 2 cột giá trị x, y. Sau đó dùng hàm max tìm giá trị ymax, rồi từ ymax tra ngược lại để tìm ra giá trị x cần tìm.

Mình biết cách giải của mình là "nông dân", nhưng mình không nghĩ ra đoạn code nào để khảo sát cái này. Lúc nào có thời gian rỗi, nhờ anh xử lý giúp mình được không?

Trân trọng!
 

File đính kèm

Upvote 0
Cảm ơn anh HieuCD đã giải thích rất tường minh.

Mình hỏi vậy vì mình không biết là anh đã lược bỏ bớt do bài toán mình đặt ra ban đầu là đơn giản, khi đọc các dòng code mình băn khoăn vì cảm thấy cái code này nó chỉ đúng khi hàm số là đồng biến.

Lỡ rồi thì làm phiền anh thêm 1 tí nữa. Ví dụ như khi hàm số y=f(x) không đồng biến như file đính kèm. Để tìm giá trị x tương ứng với giá trị ymax, bao nhiêu năm nay mình giải quyết như sau: mình cho tính ra hết 2 cột giá trị x, y. Sau đó dùng hàm max tìm giá trị ymax, rồi từ ymax tra ngược lại để tìm ra giá trị x cần tìm.

Mình biết cách giải của mình là "nông dân", nhưng mình không nghĩ ra đoạn code nào để khảo sát cái này. Lúc nào có thời gian rỗi, nhờ anh xử lý giúp mình được không?

Trân trọng!
Bạn dùng công cụ Solver của Excel tìm giá trị cực đại
 
Upvote 0
Giá trị cực đại xảy ra khi đạo hàm chuyển dấu từ dương sang âm.
Muốn theo dõi tính chất đạo hàm phải có ít nhất 3 điểm.
 
Upvote 0
Bạn dùng công cụ Solver của Excel tìm giá trị cực đại
Mình sẽ tìm hiểu Solver như anh Hiếu chỉ điểm, nhưng có cách nào dùng code VBA không anh Hiếu? Vì bài toán có rất nhiều phần phải dùng macro, nếu có thể giải quyết bằng macro thì thuận tiện hơn, mình có thể ghép nhiều modun code vào 1 nút lệnh, khi mình nhấn nút nó chạy cho cả bài toán luôn. Không biết code VBA có thể điều khiển cho lệnh solver gì đó luôn không?

Cảm ơn anh.
 
Upvote 0
Giá trị cực đại xảy ra khi đạo hàm chuyển dấu từ dương sang âm.
Muốn theo dõi tính chất đạo hàm phải có ít nhất 3 điểm.
Cái này về ý nghĩa nó là 1 hàm số, nhưng thực tế nó chỉ là các bảng tính. Cho x vào thì nó ra y là kết quả tính, chứ không phải là hàm f(x) như trong bài toán giải tích nên không lấy đạo hàm được anh à.
 
Upvote 0
Mình sẽ tìm hiểu Solver như anh Hiếu chỉ điểm, nhưng có cách nào dùng code VBA không anh Hiếu? Vì bài toán có rất nhiều phần phải dùng macro, nếu có thể giải quyết bằng macro thì thuận tiện hơn, mình có thể ghép nhiều modun code vào 1 nút lệnh, khi mình nhấn nút nó chạy cho cả bài toán luôn. Không biết code VBA có thể điều khiển cho lệnh solver gì đó luôn không?

Cảm ơn anh.
Với dữ liệu bài nầy có lẽ bạn chỉ cần code đơn giản thôi
Nếu dữ liệu khác thì thì bạn cần kiểm tra code chạy đúng không
Mã:
Sub Button5_Click()
  Dim ChenhLech As Double, xMax As Double, yMax As Double, y As Double
  With Sheets("Sheet1")
    ChenhLech = (.Range("j13").Value - .Range("j12").Value) / 2
    .Range("h12") = 0
    Do While .Range("j13").Value < 25000
      y = .Range("h13")
      If y > yMax Then
        xMax = .Range("h12")
        yMax = .Range("h13")
      End If
      .Range("h12") = .Range("h12").Value + ChenhLech
    Loop
    .Range("h12") = xMax
  End With
End Sub
 

File đính kèm

Upvote 0
Cái này về ý nghĩa nó là 1 hàm số, nhưng thực tế nó chỉ là các bảng tính. Cho x vào thì nó ra y là kết quả tính, chứ không phải là hàm f(x) như trong bài toán giải tích nên không lấy đạo hàm được anh à.

Định nghĩa đạo hàm là gì? Tôi nghĩ bạn lầm lẫn giữa hàm số đạo hàm và giá trị đạo hàm.
Theo định nghĩa, đạo hàm là dx/dy khi dy tiến về 0.
Con toán tính đạo hàm tại một điểm chỉ giản dị (y2-y1)/(x2-x1).
Tuy nhiên, khảo sát đạo hàm trong lập trình là trình độ toán cao, phải qua một mớ lý thuyết.
 
Upvote 0
Với dữ liệu bài nầy có lẽ bạn chỉ cần code đơn giản thôi
Nếu dữ liệu khác thì thì bạn cần kiểm tra code chạy đúng không
Mã:
Sub Button5_Click()
  Dim ChenhLech As Double, xMax As Double, yMax As Double, y As Double
  With Sheets("Sheet1")
    ChenhLech = (.Range("j13").Value - .Range("j12").Value) / 2
    .Range("h12") = 0
    Do While .Range("j13").Value < 25000
      y = .Range("h13")
      If y > yMax Then
        xMax = .Range("h12")
        yMax = .Range("h13")
      End If
      .Range("h12") = .Range("h12").Value + ChenhLech
    Loop
    .Range("h12") = xMax
  End With
End Sub
Cảm ơn anh Hiếu!
Đúng là code chạy rất nhanh. Nhưng cũng đúng như anh nói, đoạn code này chỉ đúng trong số liệu này, nếu số liệu thay đổi 1 chút thì kết quả không còn đúng nữa. Ví dụ như file đính kèm. Khi tính thủ công thì Ymax = 385600 ứng với X=20000, trong khi tính theo VBA thì Ymax = 284950 ứng với X=17500

Mình nghĩ do bước nhảy "Chenhlech" trong đoạn ".Range("h12") = .Range("h12").Value + ChenhLech", bước nhảy quá lớn nên bỏ sót nghiệm.

Nhưng không sao, từ giải thuật nhị phân mà anh đã sử dụng ở bài đầu, mình sẽ chia bài toán ra nhiều đoạn đồng biến và vận dụng nó vào. Chắc chắn sẽ OK. Cảm ơn anh rất nhiều về sự giúp đỡ vừa qua!
 

File đính kèm

Upvote 0
Cảm ơn anh Hiếu!
Đúng là code chạy rất nhanh. Nhưng cũng đúng như anh nói, đoạn code này chỉ đúng trong số liệu này, nếu số liệu thay đổi 1 chút thì kết quả không còn đúng nữa. Ví dụ như file đính kèm. Khi tính thủ công thì Ymax = 385600 ứng với X=20000, trong khi tính theo VBA thì Ymax = 284950 ứng với X=17500

Mình nghĩ do bước nhảy "Chenhlech" trong đoạn ".Range("h12") = .Range("h12").Value + ChenhLech", bước nhảy quá lớn nên bỏ sót nghiệm.

Nhưng không sao, từ giải thuật nhị phân mà anh đã sử dụng ở bài đầu, mình sẽ chia bài toán ra nhiều đoạn đồng biến và vận dụng nó vào. Chắc chắn sẽ OK. Cảm ơn anh rất nhiều về sự giúp đỡ vừa qua!
do code của bạn
Do While .Range("j13").Value < 25000
nên mình tưởng giới hạn là 25000
chỉnh lại 1 chút
Mã:
Sub Button5_Click()
  Dim ChenhLech As Double, xMax As Double, yMax As Double, y As Double
  With Sheets("Sheet1")
    ChenhLech = (.Range("j13").Value - .Range("j12").Value) / 2
    .Range("h12") = 0
    y = .Range("h13") - 1
    Do While .Range("h13").Value <> y
      y = .Range("h13")
      If y > yMax Then
        xMax = .Range("h12")
        yMax = .Range("h13")
      End If
      .Range("h12") = .Range("h12").Value + ChenhLech
    Loop
    .Range("h12") = xMax
  End With
End Sub
 
Upvote 0
Web KT

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

Back
Top Bottom