Em chào các anh chị . Em trở lại với VBA rồi đây... ^^
Hiện tại em đang gặp một chút vấn đề khi viết code, các anh chị cùng em tìm ra cách khắc phục nhé :>
Em đang có một File có hơn 10662 dòng . Em muốn chia 10662 dòng này thành 3 sheet . Thỏa mãn 2 điều kiện: Điều Kiện 1:
Sheet thứ nhất chiếm 70 % , Sheet thứ hai chiếm 10% , Sheet thứ 3 chiếm 20% Điều Kiện 2
Phải random ngẫu nhiên số dòng ở sheet "sdata"(như trên hình) rồi chia theo tỉ lệ vào từng sheet như ở điều kiện 1
Em cũng đã thử viết code và gặp hai vấn đề : Vấn đề 1:
Lúc chia vào từng sheet do ở trong sheet "sdata" có 10662 dòng nên không thể chia thế nào cho đều được :
Ví dụ: Sheet1(70%) chứa 7463 dòng , Sheet2(10%) chứa 1066 dòng , sheet3(20%) chứa 2132 dòng . Vậy còn thiếu 1 dòng . Câu hỏi đặt ra là chia thế nào cho hợp lý? Liệu bài toán này đặt ra có khả thi không ? Nếu không khả thi thì nên sửa đề thế nào ? Vấn đề 2:
Em thử dùng cái random rồi kết hợp với tính chất của collection nhưng không thể chạy được :> . Em muốn anh chị cũng dựa vào ý tưởng của em mà viết tiếp . Nếu được thế thì thực sự rất tuyệt vời (Em lưu code ở trong Module2 ) :>
Điều Kiện 1:
Sheet thứ nhất chiếm 70 % , Sheet thứ hai chiếm 10% , Sheet thứ 3 chiếm 20% Vấn đề 1:
Lúc chia vào từng sheet do ở trong sheet "sdata" có 10662 dòng nên không thể chia thế nào cho đều được :
Ví dụ: Sheet1(70%) chứa 7463 dòng , Sheet2(10%) chứa 1066 dòng , sheet3(20%) chứa 2132 dòng . Vậy còn thiếu 1 dòng . Câu hỏi đặt ra là chia thế nào cho hợp lý? Liệu bài toán này đặt ra có khả thi không ? Nếu không khả thi thì nên sửa đề thế nào ?
Ví dụ số dòng n = 70% <tổng dòng> (làm tròn tới số nguyên) cho sheet1. Số dòng m = 10% <tổng dòng> cho sheet2. Còn <tổng dòng> - n - m cho sheet3.
Em chào các anh chị . Em trở lại với VBA rồi đây... ^^
Hiện tại em đang gặp một chút vấn đề khi viết code, các anh chị cùng em tìm ra cách khắc phục nhé :>
Em đang có một File có hơn 10662 dòng . Em muốn chia 10662 dòng này thành 3 sheet . Thỏa mãn 2 điều kiện: Điều Kiện 1:
Sheet thứ nhất chiếm 70 % , Sheet thứ hai chiếm 10% , Sheet thứ 3 chiếm 20%
Điều Kiện 2
Phải random ngẫu nhiên số dòng ở sheet "sdata"(như trên hình) rồi chia theo tỉ lệ vào từng sheet như ở điều kiện 1
Em cũng đã thử viết code và gặp hai vấn đề : Vấn đề 1:
Lúc chia vào từng sheet do ở trong sheet "sdata" có 10662 dòng nên không thể chia thế nào cho đều được :
Ví dụ: Sheet1(70%) chứa 7463 dòng , Sheet2(10%) chứa 1066 dòng , sheet3(20%) chứa 2132 dòng . Vậy còn thiếu 1 dòng . Câu hỏi đặt ra là chia thế nào cho hợp lý? Liệu bài toán này đặt ra có khả thi không ? Nếu không khả thi thì nên sửa đề thế nào ? Vấn đề 2:
Em thử dùng cái random rồi kết hợp với tính chất của collection nhưng không thể chạy được :> . Em muốn anh chị cũng dựa vào ý tưởng của em mà viết tiếp . Nếu được thế thì thực sự rất tuyệt vời (Em lưu code ở trong Module2 ) :>
. . . . . .
Thiếu hay thừa 1 dòng cũng không xong thì hợp lý là như thế nào, theo bạn?
Bạn nói cái hợp lý của bạn là như thế nào đi, cộng đồng sẽ giúp bạn chia thế nào cho hợp lí ngay tấp lự!
Từ con số 100 đến con số 106 chỉ có 1 con đạt ý muốn của bạn thôi!
Em vẫn chưa thực sự hiểu dòng này anh muốn nói gì . Nhưng mà cái sự "hợp lý" mà em muốn nói đến là cách chia sao cho chính xác(làm sao cho đúng đắn nhất , làm sao cho lúc giải quyết vấn đề thứ hai không bị vướng mắc bởi vấn đề thứ nhất) . Nên em nghĩ đặt ra câu hỏi như thế là "hợp lý" chứ ạ (chắc cái sự hợp lý này là ý anh muốn nói đến ?)
Ở thế giới thực, con người hay máy móc khi thực hiện việc gì đó đều có sai số, quan trọng là ta quy ước chấp nhận sai số trong khoảng bao nhiêu.
Trong ngành cơ khí chế tạo có một môn là "Dung sai". Nếu bạn đã đọc/ học qua sẽ biết xử lý thế nào, đẩy sai số vào khâu nào cho hợp lý.
Còn cái vụ thứ hai thì tìm mấy bài ngẫu nhiên không trùng ấy. Trong này có cả núi, tha hồ mà tham khảo giải thuật, code sẵn cũng có. Cho từ khoá rồi đó, đừng có đổi ý kêu làm sẵn nhé.
Em cảm ơn các anh nhiều lắm ^^ . Đúng là em vừa mới có tìm ra một vài bài liên quan đến câu hỏi của mình (mặc dù lúc chiều đã tìm kiếm) . Em sẽ tiếp tục làm rồi có gì không hiểu thì em lại lôi bài chủ đề này lên :>
Cảm ơn các anh đã bỏ thời gian ra để đọc bài của em . Em cảm ơn
...
Còn cái vụ thứ hai thì tìm mấy bài ngẫu nhiên không trùng ấy. Trong này có cả núi, tha hồ mà tham khảo giải thuật, code sẵn cũng có. Cho từ khoá rồi đó, đừng có đổi ý kêu làm sẵn nhé.
Ngẫu nhiên không trùng áp dụng cho cái này hơi khó.
Cách dễ nhất là tạm cộng 1 cột phụ, gài Rand vào, sort, xóa cột phụ.
Bốc 70%, 10%, và 20%. Dùng hàm Round thì số dư là -1/0/1.
Có 3 cách:
1. cứ lấy đúng tỷ lệ. Sai số, tức số dư số thiếu thì "giàu út ăn, khó út chịu": đổ hết về phần cuối.
2. gọi thêm 1 hàm random, nhét dư/thiếu vào chỗ ấy.
3. đặt ra luật bắt chước kiểu như Bankers' Round:
Xem 3 thằng trên, thằng nào số lẻ thì bù vào thằng ấy.
Nếu nhiều hơn 1 thằng thì ưu tiên 70, 20, 10
Nếu không có thằng nào số lẻ thì chọn random.
Một giải thuật bảo đảm hổng giống ai. Có khả thi hay không thì chưa thử, chưa biết.
Tạo một cột phụ, ghi hàm Rand, copy paste value (để nó khỏi thay đổi nữa).
Dùng hàm Percentile.Inc để tìm trị ở khoảng 70% (a) và 80% (b)
Dùng advanced filter, biết rằng:
Có 70% < a
Có 10% (80%-70%) >= a và < b
Có 20% >= b
Vấn đề 2:
Em thử dùng cái random rồi kết hợp với tính chất của collection nhưng không thể chạy được :> . Em muốn anh chị cũng dựa vào ý tưởng của em mà viết tiếp . Nếu được thế thì thực sự rất tuyệt vời (Em lưu code ở trong Module2 ) :>
và muôn đời Col70.Count = 5 < 7 = length nên vòng Do While chạy tới ngày tận thế.
Có thể do sơ suất tôi không chính xác ở một điểm nào đó, nhưng code chắc chắn sẽ gặp trường hợp chạy tới ngày tận thế.
Thâm chí nếu may mắn ra khỏi vòng lặp thì code thực hiện lâu do gặp lỗi nhiều trong vòng lặp (thêm key đã có). Nên loại ngay các chỉ số đã được chọn trước khi thực hiện những lựa chọn tiếp theo (xem và hiểu Sub Draw). Tức vd. Srow = 100 và đã chọn được i = 92 thì trong lần chọn tiếp chỉ chọn trong các chỉ số 1-91 và 93-100
----------
Bạn có thể tham khảo code sau. Tôi không khẳng định là code chuẩn 100% vì tôi không kiểm tra kỹ. Tôi không khẳng định là code tối ưu, chạy nhanh, ngắn gọn vì tôi không thi thố với ai cả. Bạn có thể tham khảo nhưng không bắt buộc.
Bản thân tôi không thích dùng Collection. Dùng Ductionary hoàn toàn đủ và thuận tiện hơn nhiều.
Mã:
Sub Draw(Arr(), ByVal Amount As Long)
' Amount: so nguyen duong <= UBound(Arr, 1)
' haĚm chon ngau nhien Amount phan tu cua mang Arr va don chung xuong cuoi mang.
Dim index As Long, k As Long, a As Long, number As Long
number = UBound(Arr, 1)
If Amount > number Or Amount = 0 Then Exit Sub
Randomize
For k = 1 To Amount
index = Int(Rnd * (number - k + 1)) + 1
a = Arr(number - k + 1)
Arr(number - k + 1) = Arr(index)
Arr(index) = a
Next k
End Sub
Function CreateNum(dulieu())
Dim k As Long, length As Long, Arr()
Dim Col70 As Collection
Dim Col10 As Collection
Dim Col20 As Collection
On Error GoTo end_
Set Col70 = New Collection
Set Col10 = New Collection
Set Col20 = New Collection
ReDim Arr(1 To UBound(dulieu, 1))
For k = 1 To UBound(dulieu, 1)
Arr(k) = k ' nhap cac chi so dong cua mang dulieu vao mang Arr
Next
length = UBound(dulieu, 1) * 0.7
Draw Arr, length
For k = UBound(Arr) To UBound(Arr) - length + 1 Step -1
Col70.Add dulieu(Arr(k), 1) & " " & dulieu(Arr(k), 2), CStr(Arr(k))
Next
ReDim Preserve Arr(1 To UBound(Arr) - length) ' bo length phan tu o cuoi mang Arr do da nhap vao Col70
length = UBound(dulieu, 1) * 0.2
Draw Arr, length
For k = UBound(Arr) To UBound(Arr) - length + 1 Step -1
Col20.Add dulieu(Arr(k), 1) & " " & dulieu(Arr(k), 2), CStr(Arr(k))
Next
ReDim Preserve Arr(1 To UBound(Arr) - length)
length = UBound(Arr, 1)
Draw Arr, length
For k = UBound(Arr) To UBound(Arr) - length + 1 Step -1
Col10.Add dulieu(Arr(k), 1) & " " & dulieu(Arr(k), 2), CStr(Arr(k))
Next
' ReDim Arr(1 To Col10.Count + Col20.Count + Col70.Count, 1 To 1)
'
' For k = 1 To Col70.Count
' Arr(k, 1) = Col70.Item(k)
' Next k
' For k = 1 To Col20.Count
' Arr(Col70.Count + k, 1) = Col20.Item(k)
' Next k
' For k = 1 To Col10.Count
' Arr(Col70.Count + Col20.Count + k, 1) = Col10.Item(k)
' Next k
' Sheet1.Range("C1").Resize(UBound(Arr, 1)).Value = Arr
end_:
End Function
Sub LayRndDuLieu()
Dim n As Long, Arr()
With ThisWorkbook.Worksheets("sdata")
.Range("C1:C100000").ClearContents ' xoa ket qua cu
n = .Range("B" & Rows.Count).End(xlUp).Row
Arr = .Range("A1:B" & n).Value ' doc tung o trong hon 10 000 o tu sheet co le chet mat. Doc vao mang roi thao tac trong mang thoi
End With
CreateNum Arr
End Sub
và muôn đời Col70.Count = 5 < 8 = length nên vòng Do While chạy tới ngày tận thế.
Có thể do sơ suất tôi không chính xác ở một điểm nào đó, nhưng code chắc chắn sẽ gặp trường hợp chạy tới ngày tận thế.
Thâm chí nếu may mắn ra khỏi vòng lặp thì code thực hiện lâu do gặp lỗi nhiều trong vòng lặp (thêm key đã có). Nên loại ngay các chỉ số đã được chọn trước khi thực hiện những lựa chọn tiếp theo (xem và hiểu Sub Draw). Tức vd. Srow = 100 và đã chọn được i = 92 thì trong lần chọn tiếp chỉ chọn trong các chỉ số 1-91 và 93-100
----------
Bạn có thể tham khảo code sau. Tôi không khẳng định là code chuẩn 100% vì tôi không kiểm tra kỹ. Tôi không khẳng định là code tối ưu, chạy nhanh, ngắn gọn vì tôi không thi thố với ai cả. Bạn có thể tham khảo nhưng không bắt buộc.
Bản thân tôi không thích dùng Collection. Dùng Ductionary hoàn toàn đủ và thuận tiện hơn nhiều.
Mã:
Sub Draw(Arr(), ByVal Amount As Long)
' Amount: so nguyen duong <= UBound(Arr, 1)
' haĚm chon ngau nhien Amount phan tu cua mang Arr va don chung xuong cuoi mang.
Dim index As Long, k As Long, a As Long, number As Long
number = UBound(Arr, 1)
If Amount > number Or Amount = 0 Then Exit Sub
Randomize
For k = 1 To Amount
index = Int(Rnd * (number - k + 1)) + 1
a = Arr(number - k + 1)
Arr(number - k + 1) = Arr(index)
Arr(index) = a
Next k
End Sub
Function CreateNum(dulieu())
Dim k As Long, length As Long, Arr()
Dim Col70 As Collection
Dim Col10 As Collection
Dim Col20 As Collection
On Error GoTo end_
Set Col70 = New Collection
Set Col10 = New Collection
Set Col20 = New Collection
ReDim Arr(1 To UBound(dulieu, 1))
For k = 1 To UBound(dulieu, 1)
Arr(k) = k ' nhap cac chi so dong cua mang dulieu vao mang Arr
Next
length = UBound(dulieu, 1) * 0.7
Draw Arr, length
For k = UBound(Arr) To UBound(Arr) - length + 1 Step -1
Col70.Add dulieu(Arr(k), 1) & " " & dulieu(Arr(k), 2), CStr(Arr(k))
Next
ReDim Preserve Arr(1 To UBound(Arr) - length) ' bo length phan tu o cuoi mang Arr do da nhap vao Col70
length = UBound(dulieu, 1) * 0.2
Draw Arr, length
For k = UBound(Arr) To UBound(Arr) - length + 1 Step -1
Col20.Add dulieu(Arr(k), 1) & " " & dulieu(Arr(k), 2), CStr(Arr(k))
Next
ReDim Preserve Arr(1 To UBound(Arr) - length)
length = UBound(Arr, 1)
Draw Arr, length
For k = UBound(Arr) To UBound(Arr) - length + 1 Step -1
Col10.Add dulieu(Arr(k), 1) & " " & dulieu(Arr(k), 2), CStr(Arr(k))
Next
' ReDim Arr(1 To Col10.Count + Col20.Count + Col70.Count, 1 To 1)
'
' For k = 1 To Col70.Count
' Arr(k, 1) = Col70.Item(k)
' Next k
' For k = 1 To Col20.Count
' Arr(Col70.Count + k, 1) = Col20.Item(k)
' Next k
' For k = 1 To Col10.Count
' Arr(Col70.Count + Col20.Count + k, 1) = Col10.Item(k)
' Next k
' Sheet1.Range("C1").Resize(UBound(Arr, 1)).Value = Arr
end_:
End Function
Sub LayRndDuLieu()
Dim n As Long, Arr()
With ThisWorkbook.Worksheets("sdata")
.Range("C1:C100000").ClearContents ' xoa ket qua cu
n = .Range("B" & Rows.Count).End(xlUp).Row
Arr = .Range("A1:B" & n).Value ' doc tung o trong hon 10 000 o tu sheet co le chet mat. Doc vao mang roi thao tac trong mang thoi
End With
CreateNum Arr
End Sub