Lấy mẫu một chuỗi không giảm ngẫu nhiên


20

Đầu vào: Hai số nguyên n và k được cung cấp dưới mọi hình thức thuận tiện cho mã của bạn

Đầu ra Một chuỗi số nguyên k không giảm ngẫu nhiên, mỗi số trong phạm vi từ 1 đến n. Mẫu phải được chọn thống nhất từ ​​tất cả các chuỗi không giảm của số nguyên k với số nguyên trong phạm vi từ 1 đến n.

Đầu ra có thể ở bất kỳ định dạng hợp lý nào bạn thấy thuận tiện.

Bạn có thể sử dụng bất kỳ trình tạo giả ngẫu nhiên nào mà thư viện / ngôn ngữ yêu thích của bạn cung cấp.

Chúng ta có thể giả sử rằng các số nguyên n, k> 0.

Thí dụ

Nói n, k = 2. Các chuỗi không giảm là

1,1
1,2
2,2

Mỗi chuỗi nên có xác suất 1/3 được xuất ra.

Sự hạn chế

Mã của bạn sẽ chạy trong không quá vài giây với k = 20 và n = 100.

Những gì không làm việc

Nếu bạn chỉ lấy mẫu ngẫu nhiên từng số nguyên từ phạm vi 1 đến n và sau đó sắp xếp danh sách bạn sẽ không nhận được phân phối thống nhất.


Xuất ra số lượng các chuỗi không giảm cho n, k có thể tự thực hiện một thử thách thú vị, nếu nó chưa được thực hiện ...
ETHproductions

1
@ETHproductions Không thực sự, nó chỉ là một nhị thức (liên quan đến điều này )
Sp3000

@ Sp3000 À, được rồi. Đó là một thử thách thú vị đối với tôi để tìm ra cách tính toán hiệu quả mặc dù.
Sản xuất ETH

Yêu cầu của bạn rằng mỗi chuỗi có xác suất đầu ra bằng nhau là không thể đáp ứng với hầu hết các PRNG giống vườn có thể chỉ có trạng thái 32 hoặc 48 bit. Theo Wolfram, có 535 triệu phần tử 20 phần tử của 1, ..., 100 (không kiểm tra xem có bao nhiêu trong số chúng không tăng). 2 ^ 64 chỉ là 18 triệu.
Sinan Ünür

Câu trả lời:


1

Trên thực tế , 14 12 byte

Câu trả lời này dựa trên câu trả lời 05AB1E của Emignacâu trả lời cho câu hỏi Math.SE này . Gợi ý chơi golf chào mừng! Hãy thử trực tuyến!

;;ra+DR╚HS♀-

Ungolfing

      Implicit input n, then k.
;;    Duplicate k twice.
r     Push range [0...k] for later.
a     Invert the stack. Stack: n, k, k, [0...k]
+DR   Push the range [1..n+k-1].
╚     Shuffle the range. Stack: shuffled_range, k, [0...k]
H     Push the first k elements of shuffled_range. Call this increasing.
S     Sort increasing so the elements are actually increasing.
♀-    Subtract each element of [0...k] from each element of increasing.
      This gives us our non-decreasing sequence.
      Implicit return.

13

Python, 89 byte

from random import*
lambda n,k:[x-i for i,x in enumerate(sorted(sample(range(1,n+k),k)))]

Việc tạo ra một chuỗi tăng chứ không phải là một chuỗi không giảm sẽ đơn giản: đây chỉ là một tập hợp con ngẫu nhiên của các ksố giữa 1n, được sắp xếp.

Nhưng, chúng ta có thể chuyển đổi một chuỗi tăng dần thành một chuỗi không giảm bằng cách thu hẹp mỗi khoảng cách giữa các số liên tiếp bằng 1. Vì vậy, khoảng cách 1 trở thành khoảng cách bằng 0, tạo ra các số bằng nhau. Để làm như vậy, hãy giảm igiá trị lớn nhất củai

r[0], r[1], ..., r[n-1]  =>  r[0]-0, r[1]-1, ..., r[n-1]-(n-1)

Đối với kết quả là từ 1để n, đầu vào phải là từ 1để n+k-1. Điều này đưa ra một sự lựa chọn giữa các chuỗi số không giảm giữa 1n, để tăng các chuỗi giữa 1n+k-1. Các mệnh đề tương tự được sử dụng trong đối số sao và thanh để đếm các chuỗi như vậy.

Mã này sử dụng hàm python random.sample, lấy kmẫu mà không cần thay thế từ danh sách đầu vào. Sắp xếp nó cho trình tự tăng dần.


Điều này thật ấn tượng. Bạn có thể thêm một lời giải thích về phương pháp xin vui lòng?

Yup, bận bây giờ, sẽ giải thích sau.
xnor

Tôi đã đếm 90 byte ... (và bạn cũng có thể import*lưu 1 byte)
Rod

@Rod Cảm ơn, tôi quên mất điều đó.
xnor


7

Python, 87 byte

from random import*
f=lambda n,k:k>random()*(n+k-1)and f(n,k-1)+[n]or k*[7]and f(n-1,k)

Xác suất mà giá trị tối đa có thể nđược bao gồm bằng k/(n+k-1). Để bao gồm nó, đặt nó ở cuối danh sách và giảm các số cần thiết còn lại k. Để loại trừ nó, giảm giới hạn trên n. Sau đó, lặp lại cho đến khi không còn giá trị nào nữa ( k==0).

Python randomdường như không tích hợp sẵn cho biến Bernoulli: 1 với một số xác suất và 0 nếu không. Vì vậy, điều này kiểm tra nếu một giá trị ngẫu nhiên giữa 0 và 1 được tạo ra bởi randomrơi bên dưới k/(n+k-1). Python 2 sẽ tỷ lệ là phân chia float, vì vậy chúng tôi thay vào đó nhân với mẫu số : k>random()*(n+k-1).


Numpy sẽ giúp đỡ ở đây?

@Lembik Nghĩ hay, nhưng có vẻ như bạn phải nhập numpy.random, quá dài.
xnor

5

JavaScript (Firefox 30+), 74 byte

(n,k,i=0,j=k)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)i-j+k--]

Giải trình

Câu trả lời Python tuyệt vời của xnor chứa một bản tóm tắt rất hay về cách thức / lý do tại sao kỹ thuật được sử dụng ở đây hoạt động. Bước đầu tiên là tạo phạm vi [1, 2, ..., n + k - 1] :

(n,k,i=0)=>[for(_ of Array(q=k+n-1))++i]

Tiếp theo chúng ta cần lấy k mục ngẫu nhiên từ phạm vi này. Để làm điều này, chúng ta cần chọn từng mục với xác suất s / q , trong đó s là số lượng vật phẩm vẫn cần và q là số lượng vật phẩm còn lại trong phạm vi. Vì chúng tôi đang sử dụng một cách hiểu mảng, điều này khá dễ dàng:

(n,k,i=0)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)k--&&i]

Điều này cho chúng ta một chuỗi số tăng phân bố đồng đều . Điều này có thể được khắc phục bằng cách trừ đi số lượng vật phẩm j mà chúng ta đã tìm thấy trước đây:

(n,k,i=0,j=0)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)k--&&i-j++]

Cuối cùng, bằng cách lưu trữ k trong j , chúng ta có thể kết hợp k--vào biểu thức và lưu một vài byte:

(n,k,i=0,j=k)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)i-j+k--]

2

TI-BASIC, 54 byte

Prompt N,K
K→dim(L1
While K
If rand<K/(N+K-1
Then
N→L1(K
K-1→K
Else
N-1→N
End
End
Disp L1

Theo logic của xnor, với một cảnh báo nhỏ. Về mặt lý thuyết chúng ta có thể cạo một byte bằng cách làm một cái gì đó như thế này:

K>rand(N+K-1

Nhưng rand (được dành riêng để tạo một danh sách các số ngẫu nhiên, vì vậy chúng tôi sẽ không thể thực hiện phép nhân ẩn mong muốn để lưu một byte.

Điều này sẽ chạy nhanh trên 84+ cho mỗi hạn chế nhưng tôi sẽ kiểm tra để đảm bảo khi nào tôi có thể.


1

PHP, 77 75 73 byte

foreach(array_rand(range(2,$argv[1]+$k=$argv[2]),$k)as$v)echo$v+1-$i++,_;

Chạy như thế này:

php -r 'foreach(array_rand(range(2,$argv[1]+$k=$argv[2]),$k)as$v)echo$v+1-$i++,_;' -- 10 5 2>/dev/null;echo
> 1_4_6_9_9_

Giải trình

foreach(                    # Iterate over...
  array_rand(               #   a (sorted) random number of items from...
    range(                  #     an array with items...
      2,                    #       from 2
      $argv[1]+$k=$argv[2]  #       to n + k (set arg 2 to $k)
    ),
    $k                      #     Take k number of items (their keys)
  )
  as $v
)
  echo $v +1 - $i++,"_";    # Print the value subtracted by the index.
                            # Need to add 1, because keys are 0-indexed.

Tinh chỉnh

  • Lưu 2 byte bằng cách loại bỏ end()cuộc gọi và thiết lập $argv[2]để $kthay vào đó truy cập rút ngắn để tranh luận
  • Đã lưu 2 byte bằng cách xóa chỉ mục khỏi foreach, vì nó đơn giản là một số tăng. Chỉ cần tăng $imỗi lần lặp thay thế

JavaScript đầu tiên và bây giờ là PHP. Tất cả các ngôn ngữ lập trình khoa học tốt nhất :) Cảm ơn bạn.

@Lembik, bạn được chào đón. Tâm trí bạn, nó sử dụng một PRNG cơ bản. Không sử dụng cho các công cụ mã hóa . :)
qua
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.