Допустим, у вас есть 20-гранный кубик. Вы начинаете бросать этот кубик и должны бросить его несколько десятков раз, прежде чем наконец бросить все 20 значений. Вы задаетесь вопросом, сколько рулонов мне нужно, чтобы получить 50% шанс увидеть все 20 значений? И сколько бросков n
кубика с одной стороны мне нужно бросить, прежде чем я переверну все n
стороны?
После некоторого исследования вы обнаружите, что существует формула для расчета вероятности броска всех n
значений после r
бросков.
P(r, n) = n! * S(r, n) / n**r
где S(a, b)
обозначает числа Стирлинга второго рода , количество способов разбить набор из n объектов (каждый рулон) на k непустых подмножеств (каждая сторона).
Вы также найдете последовательность OEIS , которую мы назовем R(n)
, которая соответствует наименьшей, r
где она P(r, n)
составляет не менее 50%. Задача состоит в том, чтобы n
как можно быстрее вычислить й член этой последовательности.
Соревнование
- Учитывая
n
, найти наименьшее,r
гдеP(r, n)
больше или равно0.5
или 50%. - Ваш код теоретически должен обрабатывать любое неотрицательное целое число в
n
качестве входных данных, но мы будем тестировать ваш код только в диапазоне1 <= n <= 1000000
. - Для озвучивания, мы будем принимать общее время , необходимое для запуска
R(n)
на входах1
через10000
. - Мы проверим правильность ваших решений, запустив нашу версию of
R(n)
на вашем выводе, чтобы увидеть, еслиP(your_output, n) >= 0.5
иP(your_output - 1, n) < 0.5
, т.е. что ваш вывод на самом деле является наименьшимr
для данногоn
. - Вы можете использовать любое определение для
S(a, b)
вашего решения. В Википедии есть несколько определений, которые могут быть полезны здесь. - Вы можете использовать встроенные модули в своих решениях, включая те, которые рассчитывают
S(a, b)
, или даже те, которые рассчитываютP(r, n)
напрямую. - Вы можете жестко закодировать до 1000 значений
R(n)
и миллион чисел Стирлинга, хотя ни одно из них не является жестким ограничением, и его можно изменить, если вы сможете убедительно аргументировать их повышение или понижение. - Вам не нужно проверять все возможные
r
промежутки междуn
тем,r
что мы ищем, но вам нужно найти самое маленькое,r
а не толькоr
где- нибудьP(r, n) >= 0.5
. - Ваша программа должна использовать язык, который можно свободно запускать в Windows 10.
Спецификации компьютера, на котором будут проверяться ваши решения, таковы i7 4790k, 8 GB RAM
. Спасибо @DJMcMayhem за предоставление его компьютера для тестирования. Не стесняйтесь добавлять свои неофициальные сроки для справки, но официальное время будет предоставлено позже, как только диджей сможет его протестировать.
Контрольные примеры
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
Дайте мне знать, если у вас есть какие-либо вопросы или предложения. Удачи и хорошей оптимизации!
источник
Ответы:
Python + NumPy, 3,95 секунды
Попробуйте онлайн!
Как это работает
При этом используется ряд замкнутой формы для P ( r , n ) и его производная по r , переупорядоченная для численной устойчивости и векторизации, чтобы выполнить метод Ньютона для поиска r, такого что P ( r , n ) = 0.5, округляя r до целого числа перед каждым шагом, пока шаг не переместится на r менее чем на 3/4. С хорошим начальным предположением, это обычно занимает одну или две итерации.
x i = log (1 - i / n ) = log (( i + 1) ⋅ (( n - i - 1) / n n - i ) / n )
cx i = log ( n ! / (( n - i - 1)! ⋅ n i + 1 )
y i = cx i + cx n - i - 2 - cx n - 1 = логарифмический бином ( n , i + 1)
z i = (-1) i + 1 ⋅ бином ( n , ) r
1 + ∑ z i = n! ⋅ S ( r , n ) / n r = P ( r , n )
z i ⋅ x i + 1 = (-1) i + 1 ⋅ binom ( n , i + 1) ⋅ (( n - i - 1) / n ) r log (( n - i - 1) / n)
Σ г я ⋅ х я + 1 = г / д г Р ( г , п )
источник
0.366512
было чтоlog
-то давным-давно. Будем использовать-log(log(2)
в моей следующей итерации. Во-вторых, идея использовать метод Ньютона также очень умна, и я рад видеть, что это работает так хорошо. В-третьих, я почти наверняка собираюсь украстьexp(log(binom(n, i+1)) + r * log((n-i-1)/n))
: P Слава отличному ответу! : Dnumpy
импорт наfrom numpy import *
и по какой-то причине время сократилось до 0 ... Попробуйте онлайн ?r
, так как ваше первоначальное приближение уже достаточно хорошее. Надеюсь еще раз увидеть вас в PPCG и извините за все.