Вибірка випадкової послідовності, що не зменшується


20

Введення: два цілих числа n і k, задані в будь-якій формі, зручній для вашого коду

Виведення Випадкова не зменшується послідовність k цілих чисел, кожне в діапазоні від 1 до n. Зразок слід вибирати рівномірно з усіх не зменшуваних послідовностей k цілих чисел із цілими числами в межах від 1 до n.

Вихід може бути у будь-якому розумному форматі, який вам зручний.

Ви можете використовувати будь-який псевдовипадковий генератор, який надає улюблена бібліотека / мова.

Можна вважати, що цілі числа n, k> 0.

Приклад

Скажіть n, k = 2. Послідовності, що не зменшуються, є

1,1
1,2
2,2

Кожна послідовність повинна мати ймовірність 1/3 виведення.

Обмеження

Ваш код повинен працювати не більше декількох секунд протягом k = 20 і n = 100.

Що не працює

Якщо ви просто виберіть кожне ціле число випадковим чином від діапазону 1 до n і потім сортуєте список, ви не отримаєте рівномірного розподілу.


Виведення кількості послідовностей, що зменшуються для n, k може скласти цікавий виклик самостійно, якщо цього ще не було зроблено ...
ETHproductions

1
@ETHproductions Не дуже, це просто двочлен (пов'язаний з цим )
Sp3000

@ Sp3000 Ага, гаразд. Для мене це було цікавим викликом, щоб зрозуміти, як його ефективно розрахувати.
ETHproductions

Ваша вимога, щоб кожна послідовність мала однакову ймовірність виведення, неможливо задовольнити більшість PRNG, що мають садові сорти, які можуть мати лише 32 або 48 бітові стани. За словами Вольфрама, існує 535 квінтільйонних 20 підрядних елементів, 1, ..., 100 (не перевірив, скільки з них не зменшується). 2 ^ 64 - це лише 18 квінтільйон.
Sinan Ünür

Відповіді:


1

Власне , 14 12 байт

Ця відповідь заснований на 05AB1E відповідь Emigna в і відповідей на це питання Math.SE . Пропозиції з гольфу вітаються! Спробуйте в Інтернеті!

;;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

Пітон, 89 байт

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

Створення послідовності, що збільшується, а не зменшується, було б просто: це лише випадкове підмножина kчисел між 1і n, відсортованими.

Але ми можемо перетворити зростаючу послідовність у не зменшувальну, скорочуючи кожен проміжок між послідовними числами на 1. Отже, проміжок у 1 стає проміжком 0, роблячи рівні числа. Для цього зменшіть i«найбільше значення наi

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

Щоб результат був від 1до n, вхід повинен бути від 1до n+k-1. Це дає біекцію між не зменшуючими послідовностями чисел між 1і n, збільшуючи послідовності між 1і n+k-1. Той самий біекція використовується в аргументі зірок і брусків для підрахунку таких послідовностей.

У коді використовується функція python random.sample, яка відбирає kзразки без заміни зі списку вхідних даних. Сортування це дає зростаючу послідовність.


Це вражає. Чи можете ви додати пояснення методу, будь ласка?

Так, зараз зайнятий, поясню пізніше.
xnor

Я порахував 90 байт ... (і ви також import*можете зберегти 1 байт)
стрижень

@Rod Спасибі, я забув про це.
xnor


7

Пітон, 87 байт

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)

Імовірність nвключення максимально можливого значення дорівнює k/(n+k-1). Щоб включити його, поставте його в кінці списку та зменшіть необхідні цифри, що залишилися k. Щоб виключити це, декрементуйте верхню межу n. Потім повторюйте, поки не потрібно більше значень ( k==0).

randomЗдається, що в Python немає вбудованої змінної Бернуллі: 1 з певною вірогідністю, а 0 в іншому випадку. Отже, це перевіряє, чи randomпадає випадкове значення між 0 і 1, породжене внизу k/(n+k-1). Python 2 буде ставлення , як поплавок розподіл, таким чином , ми замість того, щоб помножити на знаменник: k>random()*(n+k-1).


Чи допоможе тут нуме?

@Lembik Добре подумав, але схоже, що вам доведеться імпортувати numpy.random, що занадто довго.
xnor

5

JavaScript (Firefox 30+), 74 байти

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

Пояснення

Відмінна відповідь Python xnor містить дуже хороший підсумок того, як / чому працює тут використана техніка. Першим кроком є ​​створення діапазону [1, 2, ..., n + k - 1] :

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

Далі нам потрібно взяти k випадкових предметів із цього діапазону. Для цього нам потрібно вибрати кожен елемент з вірогідністю s / q , де s - кількість елементів, які ще потрібні, а q - кількість елементів, що залишилися в діапазоні. Оскільки ми використовуємо розуміння масиву, це досить легко:

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

Це дає нам рівномірно розподілену зростаючу послідовність чисел. Це можна виправити, віднявши кількість елементів j, які ми знайшли раніше:

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

Нарешті, зберігаючи k в j , ми можемо включити k--у вираз і зберегти кілька байтів:

(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 байт

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

Дотримуйтесь логіки xnor, з невеликим застереженням. Ми могли б теоретично поголити байт, зробивши щось подібне:

K>rand(N+K-1

Але rand (зарезервовано для створення списку випадкових чисел, тому ми не зможемо зробити бажане неявне множення, щоб зберегти байт.

Це має працювати пристойно швидко на 84+ за обмеження, але я перевірю, щоб переконатися, коли зможу.


1

PHP, 77 75 73 байт

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

Бігайте так:

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_

Пояснення

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.

Налаштування

  • Збережені 2 байта шляхом видалення end()виклику і набору $argv[2]для $kзамість того, щоб скорочений доступу до аргументів
  • Збережено 2 байти, видаливши індекс з foreach, оскільки це просто збільшення числа. Просто збільште $iкожну ітерацію замість цього

Спочатку JavaScript, а тепер PHP. Всі найкращі мови наукового програмування :) Дякую.

@Lembik, ласкаво просимо. Зауважте, він використовує базовий PRNG. Не використовуйте для криптографічних речей . :)
aross
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.