Thuật toán hiệu quả nhất để in 1-100 bằng cách sử dụng trình tạo số ngẫu nhiên nhất định


11

Chúng tôi được cung cấp một trình tạo số ngẫu nhiên RandNum50tạo ra một số nguyên ngẫu nhiên đồng đều trong phạm vi 1150. Chúng tôi chỉ có thể sử dụng trình tạo số ngẫu nhiên này để tạo và in tất cả các số nguyên từ 1 đến 100 theo thứ tự ngẫu nhiên. Mỗi số phải đến chính xác một lần và xác suất của bất kỳ số nào xảy ra tại bất kỳ nơi nào đều phải bằng nhau.

Thuật toán hiệu quả nhất cho việc này là gì?


1
Sử dụng một mảng / hoặc vectơ bit để ghi lại các số đã thấy và một bộ đếm để ghi lại số lượng các số duy nhất được nhìn thấy.
Dave Clarke

@DaveClarke Làm cách nào tôi có thể tạo số lớn hơn 50 với số đó? Nếu tôi sử dụng nó nhiều hơn 1 lần thì tôi cũng sẽ tạo 1 bằng cách sử dụng chúng như thế nào?
Raj Wadhwa

1
Tất nhiên, thách thức là đảm bảo rằng tất cả các địa điểm xảy ra với khả năng như nhau. Bạn có thể sử dụng RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1).
Dave Clarke

2
@DaveClarke: Vì vậy, bạn đề xuất lấy mẫu từ chối lặp? Điều đó sẽ chấm dứt chỉ trong mong đợi.
Raphael

Tôi chỉ đơn thuần là đưa ra một gợi ý.
Dave Clarke

Câu trả lời:


3

Tôi nghĩ (vì vậy nó có thể sai :-) về giải pháp sử dụng shuffle Fisher-Yates . Để giữ phân phối đồng đều với xấp xỉ tốt (xem phần EDIT bên dưới) ở mỗi lần lặp, bạn có thể sử dụng thủ thuật này để tạo ra giá trị trong khoảng từ đến :0 k - 1O(N2)krand0k1

 // return a random number in [0..k-1] with uniform distribution
 // using a uniform random generator in [1..50]
 funtion krand(k) {    
   sum = 0
   for i = 1 to k do sum = sum + RandNum50() - 1
   krand = sum mod k
 }

Thuật toán Fisher-Yates trở thành:

arr : array[0..99]
for i = 0  to 99 do arr[i] = i+1; // store 1..100 in the array
for i = 99 downto 1 {
  r = krand(i+1)  // random value in [0..i]
  exchange the values of arr[i] and arr[r]
}
for i = 0 to 99 do print arr[i]

BIÊN TẬP:

Như Erick đã chỉ ra, krandchức năng trên không trả về phân phối đồng đều thực sự. Có các phương pháp khác có thể được sử dụng để có được xấp xỉ tốt hơn (tùy ý tốt hơn) và xấp xỉ nhanh hơn; nhưng (theo hiểu biết của tôi) cách duy nhất để có được phân phối đồng đều thực sự là sử dụng lấy mẫu từ chối : chọn các bit ngẫu nhiên và nếu số thu được ít hơn trả về, mặt khác tạo ra một số ngẫu nhiên khác; một triển khai có thể:r km=log2(k)rk

function trulyrand(k) {
    if (k <= 1) return 0
    while (true) { // ... if you're really unlucky ...
      m = ceil(log_2 (k) ) // calculate m such that k < 2^m
      r = 0  // will hold the random value
      while (m >= 0) {  // ... will add m bits        
        if ( rand50() > 25 ) then b = 1 else b = 0   // random bit
        r = r * 2 + b  // shift and add the random bit
        m = m - 1
      }      
      if (r < k) then return r  // we have 0<=r<2^m ; accept it, if r < k
    }
}

1
Trang wikipedia bạn liên kết đến các trạng thái có biến thể . O(n)
Dave Clarke

1
Tôi nghĩ "xáo trộn" là từ thông dụng chính ở đây.
Raphael

4
Thủ thuật trong krand (k) không tạo ra phân phối đồng đều thực sự, mặc dù đó là một xấp xỉ tốt: ngay cả với k = 3, điều này mang lại 33.333328% cơ hội xuất ra 0. Có lý do nào để tổng hợp tất cả các cách lên đến k ở đây không ? Tôi sẽ nghĩ rằng một giới hạn nhỏ hơn đủ nếu chúng ta chỉ muốn một xấp xỉ.
Erick Wong

1
@ErickWong: bạn nói đúng; Tôi nghĩ rằng phân phối đồng đều thực sự chỉ có thể đạt được bằng cách sử dụng phương pháp lấy mẫu từ chối không được bảo đảm để kết thúc trong thời gian không đổi. Có các sơ đồ gần đúng khác (cho phép đạt được bất kỳ xấp xỉ mong muốn nào), kế hoạch tôi đề xuất là đầu tiên xuất hiện trong đầu tôi.
Vor

2
@ ex0du5: Tôi biết điều đó, nhưng làm thế nào để bạn tạo ra một hoán vị ngẫu nhiên thống nhất của các số [1..100] chỉ sử dụng một trình tạo ngẫu nhiên thống nhất trong [1..100]? Phương pháp thay thế duy nhất tôi biết là: bước 1) chọn một giá trị ngẫu nhiên trong ; step2) nếu đã được chọn thì loại bỏ nó và goto step1; bước 3) in ; bước 4) nếu chúng ta chưa in tất cả 100 số goto bước1. Nhưng phương pháp này chỉ đơn giản là chuyển sự từ chối sang các yếu tố đã được chọn. r1..100rr
Vor

4

Vì những người khác đã đưa ra các giải pháp và giải pháp gần đúng liên quan đến việc lấy số lượng sai lệch không xác định, làm thế nào về một bằng chứng cho thấy không có thuật toán nào được đảm bảo chỉ yêu cầu số lượng RandNum50()cuộc gọi hữu hạn ?

Như những người khác đã lưu ý, in các số từ 1-100 theo thứ tự ngẫu nhiên tương đương với in một hoán vị ngẫu nhiên của các số này; Có 100! trong số các hoán vị này và do đó, bất kỳ hoán vị cụ thể nào cũng phải được xuất với xác suất .1100!

Nhưng nếu chúng ta biết rằng thuật toán của chúng tôi đã sử dụng nhiều nhất là gọi cho một số , thì chúng ta có thể lập luận như sau: trước tiên, bỏ qua các đường dẫn tính toán thực hiện ít hơn cuộc gọi để thực hiện các cuộc gọi giả khác (nghĩa là các cuộc gọi trong đó giá trị trả về là không liên quan), do đó tất cả các đường dẫn tính toán thực hiện các cuộc gọi chính xác . Bất kỳ chuỗi kết quả nào từ các lệnh gọi của chúng tôi đều phải dẫn đến một số hoán vị đầu ra và vì vậy chúng tôi có thể xây dựng một 'bảng kết quả' để ánh xạ bất kỳ chuỗi đã cho nào từ các cuộc gọi của chúng tôi vào một cuộc gọi cụ thể hoán vị đầu ra. Vì mỗi kết quả này đều có khả năng như nhau (mỗi kết quả đều có xác suấtkRandNum50kkRandNum50kkRandNum50(r1,r2,,rk)150k ), thì xác suất nhận được bất kỳ hoán vị cụ thể nào từ thuật toán của chúng tôi phải có dạng đối với một số . Nhưng Không thể ở dạng này, vìkhông chia cho bất kỳ (ví dụ: 3 chia nhưng không thể chia bất kỳ số nào có dạng ). Điều này có nghĩa là không có phân phối kết quả nào cho các cuộc gọi số ngẫu nhiên có thể tạo ra một hoán vị thống nhất.c50kc1100!100!50kk100!50k


2

Các giải pháp trước đây không tối ưu. Độ phức tạp chính xác là trong các cuộc gọi đến RandNum50 và được mô tả chi tiết ở đây , sử dụng làm nguồn bit ngẫu nhiên (như được đề xuất bởi Vor):nlogn+O(1)

if ( rand50() > 25 ) then b = 1 else b = 0   // random bit

Ý tưởng cơ bản là bạn tiết kiệm được rất nhiều bit nếu bạn tạo đồng phục trong khoảng từ đếnvà sau đó sử dụng phân rã cơ sở giai thừa , thay vì tạo ra một chuỗi các đồng phục dao động trong khoảng , rồi , rồi , v.v., . Đây thực sự là, như tôi đã đề cập trong bài viết, chủ đề của một bài báo tôi đã gửi!1n!123n

Nếu bạn không biết cách tạo đồng phục, như được đề xuất trong bài đăng đó, từ một bit ngẫu nhiên, bạn cũng có thể tạo ra một xấp xỉ trực tiếp của đồng phục, theo cách này (tương đương với "thật sự" của Vor, nhưng nhanh hơn):

P = (RandNum50()-1) + (RandNum50()-1)*50^1 + (RandNum50()-1)*50^2 + ...

đi xa như bạn cần phải đi. Điều này đang phát triển trong cơ sở . Sau đó, chỉ cần cắt bớt , tức là, , trong trường hợp của bạn. Giá trị này không hoàn toàn ngẫu nhiên, nhưng nó là thước đo tính đồng nhất thường được sử dụng. Hoặc, như Vor gợi ý, bạn có thể từ chối nếu . Sau đó, với giá trị này, bạn có thể thực hiện mở rộng cơ sở giai thừa như được mô tả trong bài .P50Pn = 100 ! P > nQ=Pmodnn=100!P>n


1

Tôi đã không thực hiện phân tích để xác nhận mức độ đồng nhất (hoặc không) này và nó có thể được điều chỉnh thành một sự xáo trộn thực sự, nhưng bạn có thể chọn, từ một mảng bắt đầu của ichỉ số thứ = i + 1, (k + RandNum50() + RandNum50() - 1) mod (100 - k)chỉ mục, với loại bỏ, cho k= 0,99?

Điều này "đẩy" đỉnh trong RandNum50() + RandNum50()phân phối về phía trước một cách thống nhất.

Tôi khá chắc chắn rằng điều này không hoàn toàn đúng như tôi đã nói vì chỉ số 0 (1) không thể lấy được từ lựa chọn đầu tiên và tôi không thể nhanh chóng thấy một điều chỉnh 1..50 + 1..50 thay thế tạo ra 0 ..99.

Cập nhật

Để khắc phục vấn đề tôi lưu ý, tôi đã sử dụng một cách hiệu quả RandNum100như được đề cập trong các nhận xét câu hỏi để khởi tạo ngẫu nhiên phần kbù đầu tiên .

Điều này tạo ra một phân phối với một làn sóng đáng kể ở phía trước.

Thay vì tiến lên 1, tôi đã sử dụng số khác RandNum50để tăng số đó trước k. Điều này tạo ra một kết quả đủ ngẫu nhiên đối với tôi, nhưng nó vẫn không phải là "thực sự" ngẫu nhiên, như có thể dễ dàng nhìn thấy nếu bạn thay đổi K thành 2.

Kiểm tra mã VB.NET nơi tôi phục vụ cho bất kỳ K. Ngay cả Lưu ý đó là O (K), 6K + 2 trên thực tế.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.