Lăn để xem tất cả các mặt!


10

Giả sử bạn có một cái chết 20 mặt. Bạn bắt đầu lăn cái chết đó và phải lăn nó vài chục lần trước khi cuối cùng bạn cuộn tất cả 20 giá trị. Bạn tự hỏi, tôi cần bao nhiêu cuộn trước khi tôi có 50% cơ hội nhìn thấy tất cả 20 giá trị? Và ntôi cần phải lăn bao nhiêu cuộn một mặt trước khi lăn tất cả các nmặt?

Sau một số nghiên cứu, bạn phát hiện ra rằng một công thức tồn tại để tính cơ hội cán tất cả các ngiá trị sau khi rcuộn.

P(r, n) = n! * S(r, n) / n**r

trong đó S(a, b)biểu thị số Stirling của loại thứ hai , số cách phân vùng một tập hợp n đối tượng (mỗi cuộn) thành k tập con không rỗng (mỗi bên).

Bạn cũng tìm thấy những dãy OEIS , mà chúng tôi sẽ gọi R(n), mà tương ứng với nhỏ rnơi P(r, n)ít nhất là 50%. Thách thức là tính toán số hạng nthứ ba của chuỗi này càng nhanh càng tốt.

Các thách thức

  • Cho một n, tìm nhỏ nhất r trong đó P(r, n)lớn hơn hoặc bằng 0.5hoặc 50%.
  • Về mặt lý thuyết, mã của bạn sẽ xử lý bất kỳ số nguyên không âm nào nlàm đầu vào, nhưng chúng tôi sẽ chỉ kiểm tra mã của bạn trong phạm vi 1 <= n <= 1000000.
  • Cho điểm, chúng tôi sẽ mất tổng thời gian cần thiết để chạy R(n)trên đầu vào 1thông qua 10000.
  • Chúng tôi sẽ kiểm tra xem các giải pháp của bạn có đúng hay không bằng cách chạy phiên bản R(n)đầu ra của chúng tôi để xem liệu P(your_output, n) >= 0.5P(your_output - 1, n) < 0.5, tức là đầu ra của bạn thực sự là nhỏ nhất rcho một sản phẩm nhất định n.
  • Bạn có thể sử dụng bất kỳ định nghĩa cho S(a, b)trong giải pháp của bạn. Wikipedia có một số định nghĩa có thể hữu ích ở đây.
  • Bạn có thể sử dụng các giải pháp tích hợp trong các giải pháp của mình, bao gồm cả những giải pháp tính toán S(a, b)hoặc thậm chí là những giải pháp tính toán P(r, n)trực tiếp.
  • Bạn có thể mã hóa tối đa 1000 giá trị R(n)và một triệu số Stirling, mặc dù cả hai đều không phải là giới hạn cứng và có thể thay đổi nếu bạn có thể đưa ra một lập luận thuyết phục cho việc tăng hoặc giảm chúng.
  • Bạn không cần phải kiểm tra mọi thứ có thể rgiữa nrchúng tôi đang tìm kiếm, nhưng bạn cần phải tìm ra cái nhỏ nhất rvà không phải bất cứ rnơi nào P(r, n) >= 0.5.
  • Chương trình của bạn phải sử dụng ngôn ngữ có thể chạy tự do trên Windows 10.

Các thông số kỹ thuật của máy tính sẽ kiểm tra các giải pháp của bạn i7 4790k, 8 GB RAM. Cảm ơn @DJMcMayhem đã cung cấp máy tính của anh ấy để thử nghiệm. Vui lòng thêm thời gian không chính thức của bạn để tham khảo, nhưng thời gian chính thức sẽ được cung cấp sau khi DJ có thể kiểm tra nó.

Các trường hợp thử nghiệm

n       R(n)
1       1
2       2
3       5
4       7
5       10
6       13
20      67       # our 20-sided die
52      225      # how many cards from a huge uniformly random pile until we get a full deck
100     497
366     2294     # number of people for to get 366 distinct birthdays
1000    7274
2000    15934
5000    44418
10000   95768
100000  1187943
1000000 14182022

Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi hoặc đề nghị. Chúc may mắn và tối ưu hóa tốt!


1
@Jonathan ALLan Tôi nên chọn một từ khác. Cảm ơn cho những người đứng đầu lên.
Sherlock9

Câu trả lời:


7

Python + NumPy, 3,95 giây

from __future__ import division
import numpy as np

def rolls(n):
    if n == 1:
        return 1
    r = n * (np.log(n) - np.log(np.log(2)))
    x = np.log1p(np.arange(n) / -n)
    cx = x.cumsum()
    y = cx[:-1] + cx[-2::-1] - cx[-1]
    while True:
        r0 = np.round(r)
        z = np.exp(y + r0 * x[1:])
        z[::2] *= -1
        r = r0 - (z.sum() + 0.5) / z.dot(x[1:])
        if abs(r - r0) < 0.75:
            return np.ceil(r).astype(int)

for n in [1, 2, 3, 4, 5, 6, 20, 52, 100, 366, 1000, 2000, 5000, 10000, 100000, 1000000]:
    print('R({}) = {}'.format(n, rolls(n)))

import timeit
print('Benchmark: {:.2f}s'.format(timeit.timeit(lambda: sum(map(rolls, range(1, 10001))), number=1)))

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

Điều này sử dụng chuỗi dạng đóng cho P ( r , n ) và đạo hàm của nó đối với r , được sắp xếp lại để ổn định số và vector hóa, để thực hiện tìm kiếm phương pháp của Newton cho r sao cho P ( r , n ) = 0,5, làm tròn r đến một số nguyên trước mỗi bước, cho đến khi bước di chuyển r nhỏ hơn 3/4. Với một dự đoán ban đầu tốt, điều này thường chỉ mất một hoặc hai lần lặp.

x i = log (1 - i / n ) = log (( n - i ) / n )
cx i = log ( n ! / (( n - i - 1)! n i + 1 )
y i = cx i + cx n - i - 2 - cx n - 1 = log binom ( n , i + 1)
z i = (-1) i + 1 ⋅ binom ( n ,i + 1) ⋅ (( n - i - 1) / n ) r
1 + ∑ z i = n! ⋅ S ( r , n ) / n r = P ( r , n )
z ix i + 1 = (-1) i + 1 ⋅ binom ( n , i + 1) ⋅ (( n - i - 1) / n ) r log (( n - i - 1) / n)
z ix i + 1 = d / d r P ( r , n )


1
Công việc tuyệt vời trên toàn bộ câu trả lời! Đầu tiên, tôi nên nhận ra rằng đó 0.366512logmột cái gì đó từ lâu. Sẽ sử dụng -log(log(2)trong lần lặp lại tiếp theo của tôi. Thứ hai, ý tưởng sử dụng phương pháp của Newton cũng rất thông minh và tôi rất vui khi thấy điều này hoạt động rất tốt. Thứ ba, tôi gần như chắc chắn sẽ đánh cắp exp(log(binom(n, i+1)) + r * log((n-i-1)/n)): P Kudos về một câu trả lời tuyệt vời! : D
Sherlock9

1
Tôi đã thêm thời gian chính thức! Câu trả lời hay BTW :)
James

2
Tôi thực sự bối rối. Tôi đã thay đổi numpynhập khẩu from numpy import *và vì một số lý do thời gian giảm xuống còn 0 ... Hãy thử trực tuyến ?
notjagan

@notjagan có thể nhấn cache?
NoOneIsHãy

1
Tôi muốn xin lỗi vì một số điều: 1) đạo văn của tôi về câu trả lời của bạn khi tôi cố gắng tìm cải tiến; 2) không sở hữu nó đúng cách và chỉ cố gắng sửa câu trả lời của tôi; 3) rằng lời xin lỗi này đã mất quá lâu. Tôi đã bị thương đến mức ban đầu tôi chỉ từ bỏ thử thách này. Trong một nỗ lực nhỏ để sửa chữa, tôi cho rằng thật công bằng khi nói với bạn rằng cải tiến chính của tôi về câu trả lời này đã thay đổi từ phương pháp của Newton sang tăng dần r, vì xấp xỉ ban đầu của bạn đã khá tốt. Hy vọng sẽ gặp lại bạn trong PPCG một lần nữa và xin lỗi vì tất cả mọi thứ.
Sherlock9
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.