Tạo dãy số ngẫu nhiên không trùng (1 người xem)

Liên hệ QC

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

anhtuan1066

Thành viên gạo cội
Tham gia
10/3/07
Bài viết
5,802
Được thích
6,912
Trên diển đàn GPE đã có rất nhiều bài viết nói về vấn đề này!
Tôi cũng đã tham khảo rất nhiều code ở các trang nước ngoài nhưng thấy rằng hầu hết đều viết rất khó hiểu và dài dòng!
Trong 1 dịp tình cờ khi nghiên cứu về Dictionary Object, tôi nhận thấy rằng nó có khả năng làm được điều này mà code lại cực kỳ đơn giản
Thuật toán dựa vào định nghĩa của Dictionary có đoạn: Key là những phần tử duy nhất trong Keys
Tôi đã xây dựng code như sau:
PHP:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long)
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  On Error Resume Next
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  With CreateObject("Scripting.Dictionary")
    Do
      .Add Int(Rnd() * (Top - Bottom + 1)) + Bottom, ""
    Loop Until .Count = Amount
    UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
  End With
End Function
Cú pháp hàm:
PHP:
=UniqueRandomNum(Số nhỏ, Số lớn, bao nhiêu số cần tạo)
Giả sử các bạn muốn tạo ra 30 số ngẩu nhiên không trùng nằm trong khoảng từ 1 đến 100, các bạn làm như sau:
- Quét chọn 30 cell tùy ý theo chiều dọc, chẳng hạn là A1:A30
- Gõ vào thanh Formula công thức =UniqueRandomNum(1,100,30)
- Bấm tổ hợp phím Ctrl + Shift + Enter
Hãy thí nghiệm với đoạn Test sau:
PHP:
Sub Test()
  Range("A1:A30").Value = UniqueRandomNum(1, 100, 30)
End Sub
--------------
Ghi chú: Dictionary Object còn làm được nhiều thứ khác nữa, chẳng hạn có thể xây dựng hàm trích lọc các phần tử duy nhất (ngẫu nhiên và duy nhất đã làm được, đương nhiên duy nhất sẽ càng dể hơn)
 

File đính kèm

Lần chỉnh sửa cuối:
Mọi người có thể tùy biến dãy số sau 2 chữ số thập phân được không?, (VD Từ 95.01-95.99). Thân!
 
Upvote 0
Không hiểu sao em dùng hàm của Thầy ndu vẫn bị lỗi trùng số. Em có sưu tầm được hàm ESPshuffle rất hay, tốc độ thì khỏi bàn, mà thử nhiều lần vẫn chưa lần nào bị trùng số. Em gửi kèm theo đây để các thầy tham khảo và cho ý kiến :)
Mã:
 Sub ESPshuffle(ByRef r As Range)
   ' fill the given range with unique random numbers 1..n
   ' where n is the number of cells of the range
   ' 2015-09-20 E/S/P
   ' algorithm: preset a collection with indices 1..n (= unique)
   ' and preserve uniqueness when selecting index at random

   Dim n As Long, nrows As Long, ncols As Long
   Dim i As Long, j As Long, idx As Long
   Dim values() As Long
   Dim arr As Variant

   arr = r  ' range to array, cell content doesnt matter
   nrows = UBound(arr, 1)
   ncols = UBound(arr, 2)
   n = nrows * ncols
   ' preset values, non-random, so unique
   ReDim values(1 To n)
   For i = 1 To n
       values(i) = i
   Next i

   Randomize
   For i = 1 To nrows
       For j = 1 To ncols
           ' choose a random element/index AMONG the remaining
           idx = Int(n * Rnd + 1) ' index in 1..n
           arr(i, j) = values(idx)
           ' remove that element =
           ' preserve the last element in array, then shorten it by 1
           values(idx) = values(n)
           n = n - 1
       Next j
   Next i

   ' fill cells in sheet
   r = arr
End Sub
Nguồn: [URL]https://stackoverflow.com/questions/32674682/generate-truly-random-numbers-in-range-of-cells-using-vba[/URL]

Thử code ở đây xem: http://www.giaiphapexcel.com/dienda...ẫu-nhiên-không-trùng.27286/page-5#post-786622
 
Upvote 0
Đổi lại giờ muốn tạo dãy số ngẫu nhiên từ những số cho trước và trùng lặp với số lần ít nhất cho trước thì sao nhỉ. Bạn nào làm mẫu cho cái hàm với,

Hàm này ứng dụng để phân chia lịch trực chắc là được, ai làm nhân sự cho ý kiến thử... :p
 
Upvote 0
.
 
Lần chỉnh sửa cuối:
Upvote 0
Anh HieuCD ơi có thể viết thành Sub được không, em muốn chạy sub cho file này, anh giúp em
Thì viết 1 cái sub gọi function này và dán kết quả vào sheet.
sub subchaychofilenay()
meData = UniqueRandom(chận dưới, chận trên, số lượng cần)
[A1].resize(ubound(meData)).value = Application.Transform(meData)
end sub
 
Upvote 0
Anh HieuCD ơi có thể viết thành Sub được không, em muốn chạy sub cho file này, anh giúp em
Mình thấy có hàm RandBetween mà ít thấy anh em sử dụng. Bài này mình thử viết theo hướng dùng nó thử xem sao
Code cho 1-100; 20: ( các code khác tương tự)
PHP:
Sub Matrix3()
Dim I, Wf, Vung, Kq, K, kK, iHang, A
    iHang = 20
    Vung = [row(1:100)]
    Set Wf = Application.WorksheetFunction
    ReDim Kq(1 To iHang, 1 To 1)
        For I = UBound(Vung) To 1 Step -1
            A = Wf.RandBetween(1, I)
            K = K + 1: kK = kK + 1
            Kq(K, 1) = Vung(A, 1): Vung(A, 1) = Vung(I, 1)
            If kK = iHang Then Exit For
        Next I
    [E6].Resize(iHang) = Kq
End Sub
 

File đính kèm

Upvote 0
Em vô tình lên đây tìm bài đọc thấy bài đọc này hay quá, nhưng mà trình độ em quá gà chỉ biết một tý về code chứ chưa biết viết. Em muốn hỏi cá bác là muốn tạo ra một dãy số ngẫu nhiên mà trong đó vừa có chữ vừa có số thì phải làm sao, chỉ biết lấy sô ngẫu nhiên còn ký tự thì không biết.
Mong mọi người giúp em.
 
Upvote 0
Em vô tình lên đây tìm bài đọc thấy bài đọc này hay quá, nhưng mà trình độ em quá gà chỉ biết một tý về code chứ chưa biết viết. Em muốn hỏi cá bác là muốn tạo ra một dãy số ngẫu nhiên mà trong đó vừa có chữ vừa có số thì phải làm sao, chỉ biết lấy sô ngẫu nhiên còn ký tự thì không biết.
Mong mọi người giúp em.
Thì mình quy ước ký tự là số, sau khi tạo được dãy số theo ý muốn mình tra ngược lại chỗ cái quy ước.
 
Upvote 0
Hay là:
Biến kí số thành kí tự số, xâu hết chúng lại thành 1 (gồm kí tự số & các kí tự muốn có);
Sau đó băm chúng ra như băm bèo, sau mỗi lần băm nối chúng lại (thực hiện đến khi chán thì nghỉ giai đoạn)
Sau đó xắc lấy từ đầu đến đít hay ngược lại, tùy í sướng bản thân mỗi người.
 
Upvote 0
Vậy thì cái mã đó hoặc code đó phải viết làm sao. Giống như quy ước cho bảng chữ cái A=0, B=1..., Vd lấy một số ngẫu nhiên "102675" thì 2 số đầu là ký tự chữ còn 4 số sau là ký tự số. "102675"=BA2675. ra một chuỗi 1 ngàn số như thế đó. Làm mẫu cho em một cái. Cảm ơn mọi người.
 
Upvote 0
Em vô tình lên đây tìm bài đọc thấy bài đọc này hay quá, nhưng mà trình độ em quá gà chỉ biết một tý về code chứ chưa biết viết. Em muốn hỏi cá bác là muốn tạo ra một dãy số ngẫu nhiên mà trong đó vừa có chữ vừa có số thì phải làm sao, chỉ biết lấy sô ngẫu nhiên còn ký tự thì không biết.
Mong mọi người giúp em.


Có gì đâu, trong vba tạo ra một mảng gồm những số và ký tự mà bạn muốn lấy, ví dụ 1,2,3,"1","2","a","b"..., Giả sử có 10 phần tử, thì lấy ngẫu nhiên từ 1 đến 10.
Giả sử lấy ngẫu nhiên 2 lần được số 1 và 6, thì chuỗi ngẫu nhiên là 1a, vì ở vị trí thứ 6 là ký tự "a"
 
Upvote 0
Vậy thì cái mã đó hoặc code đó phải viết làm sao. Giống như quy ước cho bảng chữ cái A=0, B=1..., Vd lấy một số ngẫu nhiên "102675" thì 2 số đầu là ký tự chữ còn 4 số sau là ký tự số. "102675"=BA2675. ra một chuỗi 1 ngàn số như thế đó. Làm mẫu cho em một cái. Cảm ơn mọi người.

Từ AA đến ZZ có 26*26 = 676 trị.
Dùng 1 trong các hàm đã được chỉ dẫn ở trên, lấy ngấu nhiên 1000 số trong khoảng từ 10000 đến 6769999.
 
Upvote 0
Hình như bạn này muốn thực hiện việc cấp biển số xe 1 cách ngẫu nhiên;

Vậy thì nên cho biết cụ thể hơn đi:
Khu vực hay tỉnh thành nào?
5 hay 4 số?
CQ bạn đang cấp loại/dạng biển số nào?
 
Upvote 0
Hình như bạn này muốn thực hiện việc cấp biển số xe 1 cách ngẫu nhiên;

Vậy thì nên cho biết cụ thể hơn đi:
Khu vực hay tỉnh thành nào?
5 hay 4 số?
CQ bạn đang cấp loại/dạng biển số nào?

Biển số xe làm sao cấp kiểu ngẫu nhiên được? Chả lẽ chỉ cấp 1 lần rồi thôi?
Hay đây là một hình thức làm sổ ma. Tôi khai báo rằng cty tôi có 1000 chiếc xe, bây giờ cần nộp sổ chứng từ chi tiêu cho từng chiếc. Khi ấy tôi cần 1000 biển số tạo ngẫu nhiên.
 
Upvote 0
- Bạn mở file tác giả lên, bấm Alt + F11 vào xem người ta viết code gì trong đó
- Copy toàn bộ code
- Mở file của bạn lên, cũng bấm Alt + F11 rồi paste code đã copy vào (y chang file gốc)
Lưu ý: Code này đặt trong 1 Module, vậy bạn cũng phải vào menu Insert\Module (để có 1 Module như người ta) rồi hẳn paste code vào
Sao em viết code như vậy trong module nhưng vẫn trùng nhau và khi thoát Excel bật lại thì mất code
 
Upvote 0
Mình có 1 vấn đề khi sử dụng code này:
- Yêu cầu là: Chọn 1 dãy 14 tên trong 1 list có 18 tên cho trước.
- Mình đã copy code như các bạn hướng dẫn.
- Vấn đề mình gặp: Khi Enter thì ra 1 tên, tuy nhiên nếu copy câu lệnh và kéo xuống tới dòng thứ 14 thì bị lặp lại + lỗi như file đính kèm. (mình có xem công thức theo mẫu các bạn đưa ra nhưng không thấy khác gì)
Nhờ các bạn hướng dẫn cách khắc phục điểm này .
Xin chân thành cảm ơn!
 

File đính kèm

Upvote 0
Mình có 1 vấn đề khi sử dụng code này:
- Yêu cầu là: Chọn 1 dãy 14 tên trong 1 list có 18 tên cho trước.
- Mình đã copy code như các bạn hướng dẫn.
- Vấn đề mình gặp: Khi Enter thì ra 1 tên, tuy nhiên nếu copy câu lệnh và kéo xuống tới dòng thứ 14 thì bị lặp lại + lỗi như file đính kèm. (mình có xem công thức theo mẫu các bạn đưa ra nhưng không thấy khác gì)
Nhờ các bạn hướng dẫn cách khắc phục điểm này .
Xin chân thành cảm ơn!
- Quét chọn từ E2 đến E15
- Gõ vào thanh Formula công thức =INDEX($B$2:$B$19,UniqueRandomNum(1,18,14))
- Bấm Ctrl + Shift + Enter để kết thúc
--------------------------------
Vấn đề của bạn là: bạn chưa biết cách dùng
 
Upvote 0
Hi mọi ngươi,
Mình có 1 bài toán quá khó khăn với mình nhưng chắc khá dễ dàng với nhưng cao thủ trong diễn đàn.
Mình đang cần 1 hàm random không trùng trong 1 dãy số cố định và đồng thời loại bỏ 1 vài số đặc biệt trong dãy số này.
Ví dụ cụ thể như sau:
Mình cần random không trùng lặp khoảng 600-700 số trong dãy từ 6000-6999 nhưng đồng thời có 1 vài số đặc biệt mình cũng không muốn lặp lại trong dãy này như 6969,6888,6999,6789 và 1 vài số cụ thể khác.
Mọi người có thể chỉnh sửa hàm này theo mô tả bên trên dùm mình được không?
P/S mình khá mù về mảng này nên mong mọi người giúp đỡ.
 
Upvote 0
Chào các anh, em rất vui khi tìm được công thức excel của anh về Tạo dãy số ngẫu nhiên không trùng tại thread này https://www.giaiphapexcel.com/diendan/threads/tạo-dãy-số-ngẫu-nhiên-không-trùng.27286/
Nó giúp ích em nhiều trong công việc, tuy nhiên em chỉ xài được nó khi khối theo cột và Ctrl+Shift+Enter, lúc này kết quả sẽ trả ra ngẫu nhiên như anh nói.
tuy nhiên nếu em khối theo dòng và Ctrl+Shift+Enter thì ra kết quả không ngẫu nhiên mà hoàn toàn giống nhau.
Anh cho em hỏi có cách nào để sửa lỗi này không ạ, em cám ơn anh nhiều.
 
Upvote 0
Chào các anh, em rất vui khi tìm được công thức excel của anh về Tạo dãy số ngẫu nhiên không trùng tại thread này https://www.giaiphapexcel.com/diendan/threads/tạo-dãy-số-ngẫu-nhiên-không-trùng.27286/
Nó giúp ích em nhiều trong công việc, tuy nhiên em chỉ xài được nó khi khối theo cột và Ctrl+Shift+Enter, lúc này kết quả sẽ trả ra ngẫu nhiên như anh nói.
tuy nhiên nếu em khối theo dòng và Ctrl+Shift+Enter thì ra kết quả không ngẫu nhiên mà hoàn toàn giống nhau.
Anh cho em hỏi có cách nào để sửa lỗi này không ạ, em cám ơn anh nhiều.
Cái này không phải là lỗi. Đây là tác giả muốn hiển thị kết quả như vậy.
Nếu muốn thành dòng, trong code của link trên, thử thay dòng trên bằng dòng dưới xem sao
Mã:
 UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
Mã:
 UniqueRandomNum = .Keys
 
Upvote 0
Chào các anh, em rất vui khi tìm được công thức excel của anh về Tạo dãy số ngẫu nhiên không trùng tại thread này https://www.giaiphapexcel.com/diendan/threads/tạo-dãy-số-ngẫu-nhiên-không-trùng.27286/
Nó giúp ích em nhiều trong công việc, tuy nhiên em chỉ xài được nó khi khối theo cột và Ctrl+Shift+Enter, lúc này kết quả sẽ trả ra ngẫu nhiên như anh nói.
tuy nhiên nếu em khối theo dòng và Ctrl+Shift+Enter thì ra kết quả không ngẫu nhiên mà hoàn toàn giống nhau.
Anh cho em hỏi có cách nào để sửa lỗi này không ạ, em cám ơn anh nhiều.
Bạn thử.Chỉnh 1 chút không biết có đúng ý không.
Mã:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long, Optional ByVal dk As Boolean = True)
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  On Error Resume Next
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  With CreateObject("Scripting.Dictionary")
    Do
      .Add Int(Rnd() * (Top - Bottom + 1)) + Bottom, ""
    Loop Until .Count = Amount
    If dk = True Then
        UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
    Else
        UniqueRandomNum = (.Keys)
    End If
  End With
End Function
Mã:
=UniqueRandomNum(23,30,5,0)
=UniqueRandomNum(23,30,5,1)
 
Upvote 0
Cái này không phải là lỗi. Đây là tác giả muốn hiển thị kết quả như vậy.
Nếu muốn thành dòng, trong code của link trên, thử thay dòng trên bằng dòng dưới xem sao
Mã:
 UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
Mã:
 UniqueRandomNum = .Keys
dạ cám ơn anh, nhờ anh em đãchỉnh lại được công thức và làm được. Một lần nữa cám ơn các anh trong diễn đàn, kiến thức của các anh thật vô biên
Bài đã được tự động gộp:

Bạn thử.Chỉnh 1 chút không biết có đúng ý không.
Mã:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long, Optional ByVal dk As Boolean = True)
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  On Error Resume Next
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  With CreateObject("Scripting.Dictionary")
    Do
      .Add Int(Rnd() * (Top - Bottom + 1)) + Bottom, ""
    Loop Until .Count = Amount
    If dk = True Then
        UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
    Else
        UniqueRandomNum = (.Keys)
    End If
  End With
End Function
Mã:
=UniqueRandomNum(23,30,5,0)
=UniqueRandomNum(23,30,5,1)
dạ em cám ơn anh, em đã thử và làm được ạ!
 
Upvote 0
Bạn thử.Chỉnh 1 chút không biết có đúng ý không.
Mã:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long, Optional ByVal dk As Boolean = True)
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  On Error Resume Next
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  With CreateObject("Scripting.Dictionary")
    Do
      .Add Int(Rnd() * (Top - Bottom + 1)) + Bottom, ""
    Loop Until .Count = Amount
    If dk = True Then
        UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
    Else
        UniqueRandomNum = (.Keys)
    End If
  End With
End Function
Mã:
=UniqueRandomNum(23,30,5,0)
=UniqueRandomNum(23,30,5,1)
Nếu chỉ lấy số nguyên thì thuật toán này không tốt.
Nên viết sao cho kết quả tương thích với mọi dạng của vùng công thức (1 dòng, 1 cột hoặc bảng nhiều dòng nhiều cột) và có thể bỏ luôn tham số số lượng số cần lấy.
 
Upvote 0
Nếu chỉ lấy số nguyên thì thuật toán này không tốt.
Nên viết sao cho kết quả tương thích với mọi dạng của vùng công thức (1 dòng, 1 cột hoặc bảng nhiều dòng nhiều cột) và có thể bỏ luôn tham số số lượng số cần lấy.
Hi em sửa theo bài 1 thôi anh.
 
Upvote 0
Hi em sửa theo bài 1 thôi anh.
Bài đó tròn 10 năm rồi bạn (thật trùng hợp), nó lạc hậu rồi :D.
Mã:
Function UniqueRandomNum(Bottom As Long, Top As Long)
    Application.Volatile
    Dim CallerRng As Range, NumberSet() As Long, Result() As Long, i As Long, j As Long, k As Long, n As Long
    ReDim NumberSet(1 To Top - Bottom + 1)
    For i = Bottom To Top
        n = n + 1
        NumberSet(n) = i
    Next
    Set CallerRng = Application.Caller
    ReDim Result(1 To CallerRng.Rows.Count, 1 To CallerRng.Columns.Count)
    Randomize
    For i = 1 To UBound(Result, 1)
        For j = 1 To UBound(Result, 2)
            k = Int(Rnd() * n) + 1
            Result(i, j) = NumberSet(k)
            NumberSet(k) = NumberSet(n)
            n = n - 1
        Next
    Next
    UniqueRandomNum = Result
End Function
 

File đính kèm

Upvote 0
Bài đó tròn 10 năm rồi bạn (thật trùng hợp), nó lạc hậu rồi :D.
Mã:
Function UniqueRandomNum(Bottom As Long, Top As Long)
    Application.Volatile
    Dim CallerRng As Range, NumberSet() As Long, Result() As Long, i As Long, j As Long, k As Long, n As Long
    ReDim NumberSet(1 To Top - Bottom + 1)
    For i = Bottom To Top
        n = n + 1
        NumberSet(n) = i
    Next
    Set CallerRng = Application.Caller
    ReDim Result(1 To CallerRng.Rows.Count, 1 To CallerRng.Columns.Count)
    Randomize
    For i = 1 To UBound(Result, 1)
        For j = 1 To UBound(Result, 2)
            k = Int(Rnd() * n) + 1
            Result(i, j) = NumberSet(k)
            NumberSet(k) = NumberSet(n)
            n = n - 1
        Next
    Next
    UniqueRandomNum = Result
End Function
Anh ơi, em chưa hiểu được code tạm thời chỉ có thể dùng, có cách nào chạy random trong mảng dữ liệu cho trước không ạ (chữ)
Mấy code trên em dùng đều bị lỗi (chỉ bị khi khi sử dụng userform để thực hiện call sub****) => hiện kết quả cũ lên bảng tính (không thể random) ạ
em cảm ơn nhiều ạ
 
Lần chỉnh sửa cuối:
Upvote 0
Đề tài này cũng lâu lắm rồi bạn à (năm 2009) và sau đó đã được cải tiến rất nhiều
Mã:
Function UniqueRandNum(ByVal Bottom As Long, ByVal Top As Long, ByVal Amount As Long)
  Dim i As Long, lPos As Long, n As Long, lTmp As Long, idx As Long
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  If Top > Bottom Then
    If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
    ReDim arr(Bottom To Top) As Long
    ReDim aDes(1 To Amount, 1 To 1)
    n = Top - Bottom + 1
    For i = 1 To Amount
      idx = n + Bottom - 1
      lPos = Int(Rnd() * n) + Bottom
      If arr(lPos) = 0 Then arr(lPos) = lPos
      If arr(idx) = 0 Then arr(idx) = idx
      aDes(i, 1) = arr(lPos)
      lTmp = arr(lPos): arr(lPos) = arr(idx): arr(idx) = lTmp
      n = n - 1
    Next
    UniqueRandNum = aDes
   End If
End Function
Dạ cháu dùng sự kiện open và call ... thì kết quả không còn random ạ huhu
 
Upvote 0
Dạ cháu dùng sự kiện open và call ... thì kết quả không còn random ạ huhu
Lâu lắm không đụng đến hàm này rồi nên cũng quên khá nhiều
Bạn thử sửa lại thế này xem:
Mã:
Function UniqueRandNum(ByVal Bottom As Long, ByVal Top As Long, Optional ByVal Amount As Long)
  Dim i As Long, lPos As Long, n As Long, lTmp As Long, idx As Long
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  If Top > Bottom Then
    If (Amount > Top - Bottom + 1) Or (Amount <= 0) Then Amount = Top - Bottom + 1
    ReDim arr(Bottom To Top)
    ReDim aDes(1 To Amount, 1 To 1)
    n = Top - Bottom + 1
    Randomize
    For i = 1 To Amount
      idx = n + Bottom - 1
      lPos = Int(Rnd() * n) + Bottom
      If arr(lPos) = vbNullString Then arr(lPos) = lPos
      If arr(idx) = vbNullString Then arr(idx) = idx
      aDes(i, 1) = arr(lPos)
      lTmp = arr(lPos): arr(lPos) = arr(idx): arr(idx) = lTmp
      n = n - 1
    Next
    UniqueRandNum = aDes
   End If
End Function
 
Upvote 0
Lâu lắm không đụng đến hàm này rồi nên cũng quên khá nhiều
Bạn thử sửa lại thế này xem:
Mã:
Function UniqueRandNum(ByVal Bottom As Long, ByVal Top As Long, Optional ByVal Amount As Long)
  Dim i As Long, lPos As Long, n As Long, lTmp As Long, idx As Long
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  If Top > Bottom Then
    If (Amount > Top - Bottom + 1) Or (Amount <= 0) Then Amount = Top - Bottom + 1
    ReDim arr(Bottom To Top)
    ReDim aDes(1 To Amount, 1 To 1)
    n = Top - Bottom + 1
    Randomize
    For i = 1 To Amount
      idx = n + Bottom - 1
      lPos = Int(Rnd() * n) + Bottom
      If arr(lPos) = vbNullString Then arr(lPos) = lPos
      If arr(idx) = vbNullString Then arr(idx) = idx
      aDes(i, 1) = arr(lPos)
      lTmp = arr(lPos): arr(lPos) = arr(idx): arr(idx) = lTmp
      n = n - 1
    Next
    UniqueRandNum = aDes
   End If
End Function
Cháu cảm ơn nhiều ạ, code chạy ok ạ, chỉ có một điều là sau nhiều lần thử, thi thoảng làm treo file ạ.
Mã:
Sub test()
Dim ch(), da(), i As Long
  tongsoch = 10
  ch = UniqueRd(1, Sheet1.Range("J1").Value, 10)
  Sheet2.Cells(2, 3).Resize(1, 10) = WorksheetFunction.Transpose(ch)
  For i = 3 To tongsoch + 2
    da = UniqueRd(1, 4, 4)
    Sheet2.Cells(3, i).Resize(4, 1) = da
  Next i

End Sub
 
Upvote 0
Cháu cảm ơn nhiều ạ, code chạy ok ạ, chỉ có một điều là sau nhiều lần thử, thi thoảng làm treo file ạ.
Mã:
Sub test()
Dim ch(), da(), i As Long
  tongsoch = 10
  ch = UniqueRd(1, Sheet1.Range("J1").Value, 10)
  Sheet2.Cells(2, 3).Resize(1, 10) = WorksheetFunction.Transpose(ch)
  For i = 3 To tongsoch + 2
    da = UniqueRd(1, 4, 4)
    Sheet2.Cells(3, i).Resize(4, 1) = da
  Next i

End Sub
Thử dùng hàm tự tạo
Mã:
Function NgauNhienKhongTrung(ByVal Bottom&, ByVal Top&, Optional ByVal N& = 0, Optional ByVal bCapNhat As Boolean = False)
  Dim sArr(), Res() As Long, D&, i&, k&
  If bCapNhat Then Application.Volatile ' Thay doi ngau nhien khi bam F9
  If Top > Bottom Then
    D = Top - Bottom + 1
    If (N > D) Or (N <= 0) Then N = D
    ReDim sArr(1 To D)
    ReDim Res(1 To N, 1 To 1)
    Randomize
    For i = 1 To N
      k = Int(Rnd() * D) + 1
      If sArr(k) = Empty Then Res(i, 1) = k + Bottom - 1 Else Res(i, 1) = sArr(k) + Bottom - 1
      If sArr(D) = Empty Then sArr(k) = D Else sArr(k) = sArr(D)
      D = D - 1
    Next
    NgauNhienKhongTrung = Res
   End If
End Function
 
Upvote 0
Thử dùng hàm tự tạo
Mã:
Function NgauNhienKhongTrung(ByVal Bottom&, ByVal Top&, Optional ByVal N& = 0, Optional ByVal bCapNhat As Boolean = False)
  Dim sArr(), Res() As Long, D&, i&, k&
  If bCapNhat Then Application.Volatile ' Thay doi ngau nhien khi bam F9
  If Top > Bottom Then
    D = Top - Bottom + 1
    If (N > D) Or (N <= 0) Then N = D
    ReDim sArr(1 To D)
    ReDim Res(1 To N, 1 To 1)
    Randomize
    For i = 1 To N
      k = Int(Rnd() * D) + 1
      If sArr(k) = Empty Then Res(i, 1) = k + Bottom - 1 Else Res(i, 1) = sArr(k) + Bottom - 1
      If sArr(D) = Empty Then sArr(k) = D Else sArr(k) = sArr(D)
      D = D - 1
    Next
    NgauNhienKhongTrung = Res
   End If
End Function
Cảm ơn bác nhiều ạ
 
Upvote 0
Thử dùng hàm tự tạo
Mã:
Function NgauNhienKhongTrung(ByVal Bottom&, ByVal Top&, Optional ByVal N& = 0, Optional ByVal bCapNhat As Boolean = False)
  Dim sArr(), Res() As Long, D&, i&, k&
  If bCapNhat Then Application.Volatile ' Thay doi ngau nhien khi bam F9
  If Top > Bottom Then
    D = Top - Bottom + 1
    If (N > D) Or (N <= 0) Then N = D
    ReDim sArr(1 To D)
    ReDim Res(1 To N, 1 To 1)
    Randomize
    For i = 1 To N
      k = Int(Rnd() * D) + 1
      If sArr(k) = Empty Then Res(i, 1) = k + Bottom - 1 Else Res(i, 1) = sArr(k) + Bottom - 1
      If sArr(D) = Empty Then sArr(k) = D Else sArr(k) = sArr(D)
      D = D - 1
    Next
    NgauNhienKhongTrung = Res
   End If
End Function
Em xin chào anh! Em muốn tạo một tập hợp số ngẫu nhiên từ một tập hợp cho trước thì làm như thế nào ạ?, ví dụ tập hợp cho trước là từ 100 đến 1000 nhưng bỏ đi phần tử 101,102,501,502... (các phần tử bỏ đi tùy ý), từ tập đó ta lấy ngẫu nhiên 500 số không trùng chẳng hạn, xin anh giúp đỡ ạ!
 
Upvote 0
vâng, danh sách số bỏ đi là 10 số từ 500 đến 509 chẳng hạn ạ, tùy ý ở đây nghĩa là 10 số bỏ đi đó mình có thể thay đổi được ạ, không cố định là 500 đến 509
Thử hàm tự tạo
cú pháp ví dụ: =xyz(100,500,{200;201;202})
Kết qua trả về 1 chuỗi
Mã:
Function xyz(dau, cuoi, loaitru)
Dim mang, Kq
Dim lo, ol
Dim i, j, k
ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
Next j
For j = dau To cuoi
    If mang(j) = "" Then
        mang(dau + k) = j
        k = k + 1
    End If
Next j
ReDim Preserve mang(dau To k + dau)
lo = k
ol = lo
ReDim Kq(1 To lo)
Randomize
For i = lo To 1 Step -1
    j = Int(Rnd() * i) + dau
    Kq(i) = mang(j)
    mang(j) = mang(ol + dau)
    ol = ol - 1
Next i
xyz = Join(Kq)
End Function
---
Dãy số phải là nguyên dương nhé bạn
 
Lần chỉnh sửa cuối:
Upvote 0
Thử hàm tự tạo
cú pháp ví dụ: =xyz(100,500,{200;201;202})
Kết qua trả về 1 chuỗi
Mã:
Function xyz(dau, cuoi, loaitru)
Dim mang, Kq
Dim lo, ol
Dim i, j, k
ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
Next j
For j = dau To cuoi
    If mang(j) = "" Then
        mang(dau + k) = j
        k = k + 1
    End If
Next j
ReDim Preserve mang(dau To k + dau)
lo = k
ol = lo
ReDim Kq(1 To lo)
Randomize
For i = lo To 1 Step -1
    j = Int(Rnd() * i) + dau
    Kq(i) = mang(j)
    mang(j) = mang(ol + dau)
    ol = ol - 1
Next i
xyz = Join(Kq)
End Function
---
Dãy số phải là nguyên dương nhé bạn
Dạ, em cần sự trợ giúp như sau ạ:
1/: em muốn tạo dãy số ngẫu nhiên từ một tập số cho trước có loại trừ đi một số phần tử, và muốn từng số đó hiển thị theo từng dòng và dãy số đó là số nguyên >=0, (em cũng thử test theo function của anh nhưng không hiểu sao nó thiếu mất mấy số nằm ngoài tập loại trừ em cho vào)
2/: em thử kết hợp function với điều kiện khác để loại số, như trong file đính kèm thì em kết hợp kiểu "bé tập đi" thui ạ, nhưng gặp vấn đề lỗi out stack space!, bấm sub nó chỉ quay đến một mức rồi dừng lại, liệu có cách nào khắc phục không ạ?
 

File đính kèm

Upvote 0
Dạ, em cần sự trợ giúp như sau ạ:
1/: em muốn tạo dãy số ngẫu nhiên từ một tập số cho trước có loại trừ đi một số phần tử, và muốn từng số đó hiển thị theo từng dòng và dãy số đó là số nguyên >=0, (em cũng thử test theo function của anh nhưng không hiểu sao nó thiếu mất mấy số nằm ngoài tập loại trừ em cho vào)
2/: em thử kết hợp function với điều kiện khác để loại số, như trong file đính kèm thì em kết hợp kiểu "bé tập đi" thui ạ, nhưng gặp vấn đề lỗi out stack space!, bấm sub nó chỉ quay đến một mức rồi dừng lại, liệu có cách nào khắc phục không ạ?
Cách của bạn có lẽ phải chờ rất lâu mới có kết quả.
Theo code của bạn: lập tổ hợp chập 50 của 101 phần tử, sau đó so sánh với 10 số cho trước.
Dùng hàm COMBIN(101,50) để tính, số tổ hợp có thể > 10^29, đây là chưa kể trường hợp lặp lại nhiều lần 1 tổ hợp -> để so sánh đạt yêu cầu với 10 số cho trước có lẽ phải chờ hơi lâu
Tốt nhất là bạn loại trừ 10 số ngay từ đầu khỏi tập cho trước, sau đó lấy ngẫu nhiên.
Bài đã được tự động gộp:

@domjnjc
Hàm xyz ở trên, bạn nhập thế nào
 
Lần chỉnh sửa cuối:
Upvote 0
Cách của bạn có lẽ phải chờ rất lâu mới có kết quả.
Theo code của bạn: lập tổ hợp chập 50 của 101 phần tử, sau đó so sánh với 10 số cho trước.
Dùng hàm COMBIN(101,50) để tính, số tổ hợp có thể > 10^29, đây là chưa kể trường hợp lặp lại nhiều lần 1 tổ hợp -> để so sánh đạt yêu cầu với 10 số cho trước có lẽ phải chờ hơi lâu
Tốt nhất là bạn loại trừ 10 số ngay từ đầu khỏi tập cho trước, sau đó lấy ngẫu nhiên.
Bài đã được tự động gộp:

@domjnjc
Hàm xyz ở trên, bạn nhập thế nào
dạ vâng, để so sánh với 10 số thì tương đối lâu và chủ yếu là nó bị lỗi out stack space nên chắc cách đó không khả thi rồi.
còn về hàm xyz, ví dụ em nhập như sau: =xyz(100,110,{101}) thì có lúc ra 9 kết quả, lúc ra 10 kết quả a ạ
 
Upvote 0
dạ vâng, để so sánh với 10 số thì tương đối lâu và chủ yếu là nó bị lỗi out stack space nên chắc cách đó không khả thi rồi.
còn về hàm xyz, ví dụ em nhập như sau: =xyz(100,110,{101}) thì có lúc ra 9 kết quả, lúc ra 10 kết quả a ạ
Theo bạn, ra 9 đúng hay ra 10 đúng?
 
Upvote 0
" tập hợp cho trước " và " các phần tử bỏ đi tùy ý " bạn ghi nhận vào đâu?
Em muốn ghi nhận vào code luôn được không ạ? ví dụ như trong file test e gửi bên trên, khi cần điều chỉnh dãy số cần lấy ra thì em chỉnh code ở phần sub
Range("'test'!A1:A50").Value = UniqueRandomNum(100, 200, 50) như thay A50 thành A55 và 50 thành 55,..., nên em muốn tương tự như vậy, khi muốn điều chỉnh dãy ngẫu nhiên cần lấy, chỉ cần nhập vào code phần sub
Bài đã được tự động gộp:

Theo bạn, ra 9 đúng hay ra 10 đúng?
kết quả 10 là đúng, em không hiểu lại có lúc ra 9 ạ
 
Upvote 0
kết quả 10 là đúng, em không hiểu lại có lúc ra 9 ạ
Thử lại:
Mã:
Function zzz(dau, cuoi, loaitru)
Dim mang, Kq
Dim i, j, k, rd, temp
Dim pt As Long
ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

pt = cuoi - dau + 1 - k
ReDim Kq(1 To pt)
k = 0
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        Kq(k) = i
    End If
Next i

Randomize
For i = 1 To pt
    rd = Int(Rnd() * pt) + 1
    temp = Kq(i)
    Kq(i) = Kq(rd)
    Kq(rd) = temp
Next i
zzz = Join(Kq)
End Function
 
Upvote 0
Thử lại:
Mã:
Function zzz(dau, cuoi, loaitru)
Dim mang, Kq
Dim i, j, k, rd, temp
Dim pt As Long
ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

pt = cuoi - dau + 1 - k
ReDim Kq(1 To pt)
k = 0
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        Kq(k) = i
    End If
Next i

Randomize
For i = 1 To pt
    rd = Int(Rnd() * pt) + 1
    temp = Kq(i)
    Kq(i) = Kq(rd)
    Kq(rd) = temp
Next i
zzz = Join(Kq)
End Function
em cảm ơn, ok rồi a ạ!
 
Upvote 0
em cảm ơn, ok rồi a ạ!
Rút gọn còn 2 vòng lặp:
PHP:
Function xxx(dau, cuoi, loaitru)
Dim mang, Kq
Dim i, j, k, rd

ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

ReDim Kq(1 To (cuoi - dau + 1 - k))
k = 0
Randomize
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        rd = Int(Rnd() * k) + 1
        Kq(k) = Kq(rd)
        Kq(rd) = i
    End If
Next i

xxx = Join(Kq)
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Rút gọn còn 2 vòng lặp:
PHP:
Function xxx(dau, cuoi, loaitru)
Dim mang, Kq
Dim i, j, k, rd

ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

ReDim Kq(1 To (cuoi - dau + 1 - k))
k = 0
Randomize
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        rd = Int(Rnd() * k) + 1
        Kq(k) = Kq(rd)
        Kq(rd) = i
    End If
Next i

xxx = Join(Kq)
End Function
Trường hợp số loại trừ không nằm trong khoảng dau - cuoi thì sao? Ví dụ:
Mã:
=xxx(1,10,{1,3,5,7,9,11})
Không có thì bỏ qua chứ sao lại lỗi? Tôi nghĩ là vậy!
 
Upvote 0
Rút gọn còn 2 vòng lặp:
PHP:
Function xxx(dau, cuoi, loaitru)
Dim mang, Kq
Dim i, j, k, rd

ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

ReDim Kq(1 To (cuoi - dau + 1 - k))
k = 0
Randomize
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        rd = Int(Rnd() * k) + 1
        Kq(k) = Kq(rd)
        Kq(rd) = i
    End If
Next i

xxx = Join(Kq)
End Function
Nếu chỉ lấy N Kết quả thì sao
 
Upvote 0
xin anh chỉ giúp thêm trường hợp chỉ lấy N số với ạ,
Mượn code của bài 151, bạn thử xem
Mã:
Option Explicit

'Function xxx(dau, cuoi, loaitru)
Function xxx(dau, cuoi, loaitru, soluongCanlay)
Dim mang, Kq
Dim i, j, k, rd

ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

ReDim Kq(1 To (cuoi - dau + 1 - k))
k = 0
Randomize
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        rd = Int(Rnd() * k) + 1
        Kq(k) = Kq(rd)
        Kq(rd) = i
       
        If k = soluongCanlay Then Exit For '<---
    End If
Next i

'xxx = Join(Kq)
xxx = Trim(Join(Kq))
End Function
---
Sửa lại cú pháp ví dụ: =xxx(100,200,{101},3)
 
Upvote 0
Mượn code của bài 151, bạn thử xem
Mã:
Option Explicit

'Function xxx(dau, cuoi, loaitru)
Function xxx(dau, cuoi, loaitru, soluongCanlay)
Dim mang, Kq
Dim i, j, k, rd

ReDim mang(dau To cuoi)
For Each j In loaitru
    mang(j) = 1
    k = k + 1
Next j

ReDim Kq(1 To (cuoi - dau + 1 - k))
k = 0
Randomize
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        rd = Int(Rnd() * k) + 1
        Kq(k) = Kq(rd)
        Kq(rd) = i
      
        If k = soluongCanlay Then Exit For '<---
    End If
Next i

'xxx = Join(Kq)
xxx = Trim(Join(Kq))
End Function
---
Sửa lại cú pháp ví dụ: =xxx(100,200,{101},3)
em thử và hiển thị kết quả là dãy từ bé đến lớn, khi đạt đủ số lượng cần lấy chứ nó không được ngẫu nhiên.
ví dụ: =xxx(100,200,{101},3), thì kết quả chỉ hiện là tập hợp các số 100,102,103
 
Upvote 0
em thử và hiển thị kết quả là dãy từ bé đến lớn, khi đạt đủ số lượng cần lấy chứ nó không được ngẫu nhiên.
ví dụ: =xxx(100,200,{101},3), thì kết quả chỉ hiện là tập hợp các số 100,102,103
Thử UDF này:
PHP:
Function zzz(ByVal dau As Long, ByVal cuoi As Long, ByVal loaitru As Variant, Optional ByVal soluong As Long = 0)
Dim mang, Kq
Dim i, j, k, rd, temp
Dim pt As Long
ReDim mang(dau To cuoi)
For Each j In loaitru
    If j >= dau And j <= cuoi Then
        mang(j) = 1
        k = k + 1
    End If
Next j

pt = cuoi - dau + 1 - k
ReDim Kq(1 To pt)
k = 0
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        Kq(k) = i
    End If
Next i
If soluong = 0 Or soluong > pt Then soluong = pt
    
Randomize
For i = 1 To soluong
    rd = Int(Rnd() * pt) + 1
    temp = Kq(i)
    Kq(i) = Kq(rd)
    Kq(rd) = temp
Next i
ReDim Preserve Kq(1 To soluong)
zzz = Join(Kq)
End Function
 
Upvote 0
em thử và hiển thị kết quả là dãy từ bé đến lớn, khi đạt đủ số lượng cần lấy chứ nó không được ngẫu nhiên.
ví dụ: =xxx(100,200,{101},3), thì kết quả chỉ hiện là tập hợp các số 100,102,103
Bài trên chỉ để ý tới số lượng, bạn tìm code mới của @phuocam mà dùng
 
Upvote 0
Bài trên chỉ để ý tới số lượng, bạn tìm code mới của @phuocam mà dùng
Thử UDF này:
PHP:
Function zzz(ByVal dau As Long, ByVal cuoi As Long, ByVal loaitru As Variant, Optional ByVal soluong As Long = 0)
Dim mang, Kq
Dim i, j, k, rd, temp
Dim pt As Long
ReDim mang(dau To cuoi)
For Each j In loaitru
    If j >= dau And j <= cuoi Then
        mang(j) = 1
        k = k + 1
    End If
Next j

pt = cuoi - dau + 1 - k
ReDim Kq(1 To pt)
k = 0
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        Kq(k) = i
    End If
Next i
If soluong = 0 Or soluong > pt Then soluong = pt
   
Randomize
For i = 1 To soluong
    rd = Int(Rnd() * pt) + 1
    temp = Kq(i)
    Kq(i) = Kq(rd)
    Kq(rd) = temp
Next i
ReDim Preserve Kq(1 To soluong)
zzz = Join(Kq)
End Function
Đúng cái em cần rồi. Many thanks!
 
Upvote 0
Dạ, em cần sự trợ giúp như sau ạ:
1/: em muốn tạo dãy số ngẫu nhiên từ một tập số cho trước có loại trừ đi một số phần tử, và muốn từng số đó hiển thị theo từng dòng và dãy số đó là số nguyên >=0, (em cũng thử test theo function của anh nhưng không hiểu sao nó thiếu mất mấy số nằm ngoài tập loại trừ em cho vào)
2/: em thử kết hợp function với điều kiện khác để loại số, như trong file đính kèm thì em kết hợp kiểu "bé tập đi" thui ạ, nhưng gặp vấn đề lỗi out stack space!, bấm sub nó chỉ quay đến một mức rồi dừng lại, liệu có cách nào khắc phục không ạ?
Một cách dùng Power query, chuột phải vào bảng Query1 refresh để thay đổi kết quả
1605938389395.png
 

File đính kèm

Upvote 0
Thử UDF này:
PHP:
Function zzz(ByVal dau As Long, ByVal cuoi As Long, ByVal loaitru As Variant, Optional ByVal soluong As Long = 0)
Dim mang, Kq
Dim i, j, k, rd, temp
Dim pt As Long
ReDim mang(dau To cuoi)
For Each j In loaitru
    If j >= dau And j <= cuoi Then
        mang(j) = 1
        k = k + 1
    End If
Next j

pt = cuoi - dau + 1 - k
ReDim Kq(1 To pt)
k = 0
For i = dau To cuoi
    If mang(i) = "" Then
        k = k + 1
        Kq(k) = i
    End If
Next i
If soluong = 0 Or soluong > pt Then soluong = pt
   
Randomize
For i = 1 To soluong
    rd = Int(Rnd() * pt) + 1
    temp = Kq(i)
    Kq(i) = Kq(rd)
    Kq(rd) = temp
Next i
ReDim Preserve Kq(1 To soluong)
zzz = Join(Kq)
End Function
Biến loaitru phải nên là dạng Optional (có hoặc không cũng được). Ví dụ:
Mã:
=zzz(1,10,{2,4},5)
Có nghĩa là lấy 5 phần tử ngẫu nhiên từ 1 đến 10, loại trừ số 2 và số 4
nhưng nếu là vầy
Mã:
=zzz(1,10,,5)
thì có nghĩa là lấy 5 phần tử ngẫu nhiên từ 1 đến 10, không loại trừ gì cả
Ngoài ra nếu loaitru chi là 1 phần tử duy nhất (số 2 chẳng hạn) thì code phải cho phép viết theo dạng
Mã:
=zzz(1,10,2,5)
mà không cần phải là:
Mã:
=zzz(1,10,{2},5)
----------------------------------------
Vân... vân... phải sửa nhiều lắm!
Tôi đang tự hỏi: 3 vòng lập liệu có nhiều lắm không?
 
Upvote 0
1> Không phải là CHẬM HƠN mà gọi là QUÁ QUÁ.. CHẬM mới đúng ----> Trong 1 chương trình hoàn chỉnh thường có nhiều module, và trong từng module người ta hơn thua nhau từng ms một (chứ tốc độ rùa thế thì.. không ăn thua)
2> Dictionary Object là món chuyên về Unique rồi ---> Thiết nghĩ chẳng cần phải "cải biên" thêm làm gì ----> Chẳng ai lại đi bỏ cái "sở trường" mà đi dùng cái "sở đoản" cho mất công
3> Cái vụ On Error Resume Next chẳng qua là "người ta" quá hiểu lỗi có thể xuất hiện ở đây là gì rồi ---> Nếu viết cho rõ thì vầy:
PHP:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long)
  Dim Tmp as Long
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  With CreateObject("Scripting.Dictionary")
    Do
      Tmp = Int(Rnd() * (Top - Bottom + 1)) + Bottom
      If Not .Exists(Tmp) then .Add Tmp, ""
    Loop Until .Count = Amount
    UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
  End With
 End Function
4> Xét về mặt "dễ hiểu" thì cũng không chắc code của bạn đã dễ hiểu hơn đâu (thuật toán dạng này đã từng có trên diễn đàn rồi)
-----------------------------
Bạn còn ý kiến gì khác hơn không? Tôi rất mong có thể học hỏi thêm những điều mới lạ (mà tôi chưa biết) từ bạn
Cái code của bạn cũng rất hay nhưng nó không tạo chuỗi số mới khi load lại hoặc thay đổi một giá trị ở ô khác
Bài đã được tự động gộp:

Ẹc... Ẹc...
Nếu bạn thích Collection như thế thì "tặng" bạn hàm tôi tự viết
PHP:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long)
  Dim Arr(), i As Long, Tmp As Long, iCount As Long
  On Error Resume Next
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  ReDim Arr(1 To Amount, 1 To 1)
  With New Collection
    Do
      iCount = .Count
      Tmp = Int(Rnd() * (Top - Bottom + 1)) + Bottom
      .Add Tmp, CStr(Tmp)
      If iCount <> .Count Then
        i = i + 1
        Arr(i, 1) = Tmp
      End If
    Loop Until .Count = Amount
    UniqueRandomNum = Arr
  End With
End Function
PHP:
Sub Test()
  Dim TG As Double
  TG = Timer
  Range("A1:A60000").Value = UniqueRandomNum(1, 100000, 60000)
  MsgBox Timer - TG
End Sub
Thuật toán như trên chắc bạn thừa hiểu: Hể Add được thằng nào thì quăng luôn thằng đó vào mảng (chẳng viêc gì phải thêm vòng lập nữa cho lòng vòng)
Hàm này tuy cũng chẳng hay ho gì nhưng được cái là:
- Chỉ dùng 1 vòng lập
- Tốc độ nhanh ít nhất gấp 100 lần code của bạn
- Code của tôi chạy 20000 dòng trong 0.2 giây, của bạn 51 giây
- Code của tôi chạy 60000 dòng trong 0.5 giây, của bạn thì... Ẹc... Ẹc... chẳng biết bao lâu mới xong, vì tôi chờ 5 phút và.. mất kiên nhân quá nên Ctrl + Break cho rồi (nếu không thì phải đi uống cafe để chờ)
----------------------------
Kết luận cuối cùng: Đã tối ưu hết mức. bằng cách dùng mảng, có thể tăng tốc độ code lên đến mức cực khủng... Nhưng dù sao vẫn phải dùng tí "mẹo" ---> khả năng uyển chuyển không thể như Dictionary được
Thật ra tôi biết Collection đã lâu nhưng từ khi biết được Dictionary thì tôi không còn thích dùng Collection nữa, vì nói chung là tôi chẳng thấy nó có gì hay cả (nếu không nói là... quá tệ)
Mời xem file nha
Bạn cho hỏi là làm sao để khi nó tự tạo ra một dãy số mới khi mình thay đổi một giá trị trong bảng (kiểu như hàm rand có sẵn vậy)
 
Lần chỉnh sửa cuối:
Upvote 0
Cái code của bạn cũng rất hay nhưng nó không tạo chuỗi số mới khi load lại hoặc thay đổi một giá trị ở ô khác
Bài đã được tự động gộp:


Bạn cho hỏi là làm sao để khi nó tự tạo ra một dãy số mới khi mình thay đổi một giá trị trong bảng (kiểu như hàm rand có sẵn vậy)
Trong code, tác giả đã tạm bỏ dòng này để hàm không tự động cập nhật
Mã:
Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
Bạn chọn nó là OK
 
Upvote 0
Trong code, tác giả đã tạm bỏ dòng này để hàm không tự động cập nhật
Mã:
Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
Bạn chọn nó là OK
Cám ơn bạn
Trong code, tác giả đã tạm bỏ dòng này để hàm không tự động cập nhật
Mã:
Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
Bạn chọn nó là OK
Đã chọn rồi mà không thấy tác dụng gì cả bạn ơi
 
Upvote 0
Mình có xem các bác hướng dẫn và làm file bốc thăm thi đấu loại trực tiếp cho môn Bida, anh em cần có thể lấy tham khảo, Bốc thăm ngẫu nhiên và điền tên vào Bảng thi đấu sẵn luôn nhé!
 

File đính kèm

Upvote 0
Trên diển đàn GPE đã có rất nhiều bài viết nói về vấn đề này!
Tôi cũng đã tham khảo rất nhiều code ở các trang nước ngoài nhưng thấy rằng hầu hết đều viết rất khó hiểu và dài dòng!
Trong 1 dịp tình cờ khi nghiên cứu về Dictionary Object, tôi nhận thấy rằng nó có khả năng làm được điều này mà code lại cực kỳ đơn giản
Thuật toán dựa vào định nghĩa của Dictionary có đoạn: Key là những phần tử duy nhất trong Keys
Tôi đã xây dựng code như sau:
PHP:
Function UniqueRandomNum(Bottom As Long, Top As Long, Amount As Long)
  'Application.Volatile '<--- Neu muon gia tri thay doi khi bam F9
  On Error Resume Next
  If Amount > Top - Bottom + 1 Then Amount = Top - Bottom + 1
  With CreateObject("Scripting.Dictionary")
    Do
      .Add Int(Rnd() * (Top - Bottom + 1)) + Bottom, ""
    Loop Until .Count = Amount
    UniqueRandomNum = WorksheetFunction.Transpose(.Keys)
  End With
End Function
Cú pháp hàm:
PHP:
=UniqueRandomNum(Số nhỏ, Số lớn, bao nhiêu số cần tạo)
Giả sử các bạn muốn tạo ra 30 số ngẩu nhiên không trùng nằm trong khoảng từ 1 đến 100, các bạn làm như sau:
- Quét chọn 30 cell tùy ý theo chiều dọc, chẳng hạn là A1:A30
- Gõ vào thanh Formula công thức =UniqueRandomNum(1,100,30)
- Bấm tổ hợp phím Ctrl + Shift + Enter
Hãy thí nghiệm với đoạn Test sau:
PHP:
Sub Test()
  Range("A1:A30").Value = UniqueRandomNum(1, 100, 30)
End Sub
--------------
Ghi chú: Dictionary Object còn làm được nhiều thứ khác nữa, chẳng hạn có thể xây dựng hàm trích lọc các phần tử duy nhất (ngẫu nhiên và duy nhất đã làm được, đương nhiên duy nhất sẽ càng dể hơn)
Nhờ bạn viết giúp mình hàm này với: Daysongaunhien (số hàng; số cột; min; max; bộ số cần tạo; kiểu sắp xếp). Trong đó: dãy số ngẫu nhiên được tạo không trùng lặp, dãy số trong mỗi hàng tăng dần từ trái qua phải; số hàng dưới luôn lớn hơn số hàng trên
 
Upvote 0
Nhờ bạn viết giúp mình hàm này với: Daysongaunhien (số hàng; số cột; min; max; bộ số cần tạo; kiểu sắp xếp). Trong đó: dãy số ngẫu nhiên được tạo không trùng lặp, dãy số trong mỗi hàng tăng dần từ trái qua phải; số hàng dưới luôn lớn hơn số hàng trên
Người mà bạn có ý nhờ viết hàm đã xa diễn đàn từ giữa tháng 12/2018 đến nay rồi!
Dù sao bạn cũng cần nêu rõ hơn:
→ Giá trị nhỏ nhất trong hàng dưới có cần lớn hơn trị cực đại của hàm trên hay không?
→ 1 bảng số liệu đã biết số dòng & số cột, cũng có nghĩa là biết được số phần tử trong bảng;
Vậy thì đề bài có thể cụ thể hóa như vầy được không:
Lấy ngẫu nhiên các con số từ 1 đến 999 lắp vô các ô trên bảng tính có 8 dòng & 12 cột
sao cho các số trong hàng hay cột đều tăng dần.

Với đề bài cụ thể này, mình cho rằng có thể có vài ba cách giải & chúc bạn thành công!

PHP:
Function BangNgauSo(Optional Rws As Byte = 8, Optional Col As Byte = 12, _
    Optional Min_ As Integer = 1, Optional Max_ As Integer = 999)
 Dim J As Integer, W As Integer, Z As Integer, Tmp As Integer
 
 If Rws > 8 Then Rws = 8
 If Col > 12 Then Col = 12
 ReDim Arr(1 To 8, 1 To 12) As String
 Randomize:             Tmp = 1
 For J = 1 To 8
    Tmp = Tmp + 1
    For Z = 1 To 12
        Tmp = 1 + Tmp + 3 * Rnd() \ 1
        Arr(J, Z) = Str(Tmp)
    Next Z
 Next J
 BangNgauSo = Arr()
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Mình có xem các bác hướng dẫn và làm file bốc thăm thi đấu loại trực tiếp cho môn Bida, anh em cần có thể lấy tham khảo, Bốc thăm ngẫu nhiên và điền tên vào Bảng thi đấu sẵn luôn nhé!
Đánh độ bi da thì người ta lắc từ túi da ra một viên bi ghi số ngẫu nhiên rối đánh dựa theo số ấy.
Số ấy người chơi giấu chặt chứ đâu có ghi bảng biếc gì.

...
Vậy thì đề bài có thể cụ thể hóa như vầy được không:
Lấy ngẫu nhiên các con số từ 1 đến 999 lắp vô các ô trên bảng tính có 8 dòng & 12 cột
sao cho các số trong hàng hay cột đều tăng dần.
...
Thuật toán:
1. chọn lấy ngẫu nhiên theo 1 trong các cách giải trên
2. chép kết quả vào một mảng (nếu không sẵn là một mảng)
3. sort mảng theo thứ tự
4. tuần tự chép kết quả vào mảng m*n. Cột trước hàng sau
 
Lần chỉnh sửa cuối:
Upvote 0

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

Back
Top Bottom