Рубрики
Без рубрики

«Терминал» случайный для приоритеты, выбрав элемент из списка

Давайте посмотрим на решение, которое случайно выбирает элемент из списка. Вместо назначения равных … Теги с Python, случайным.

Давайте посмотрим на решение, которое случайно выбирает элемент из списка. Вместо того, чтобы присвоить равную вероятность каждому элементу в списке, давайте создадим алгоритм, который присваивает наибольшую вероятность элементу в индексе 0 и снижает вероятность всех оставшихся элементов по мере роста индекса в списке. Можем ли мы реализовать такую функцию? 😓

Вы можете использовать уже доступные процедуры, присутствующие в стандартной библиотеке Python, чтобы выбрать случайный элемент из списка. Допустим, мы хотим случайным образом выбрать номер из списка целых чисел. Единственное, что вам нужно сделать, это запустить следующий фрагмент:

import random
my_list = [42, 33, 30, int("deadbeef", base=16)]
random.choice(my_list)
# results in 42 with a probability of 1 / len(my_list)

Теперь, давайте скажем, мы хотим приоритетировать номер 42 более 33, определить номер 33 более 30, и в то же время приоритеты к числу 30 за «0xDeadbeef». В нашем списке у нас есть 4 числа, давайте назначаем веса этим числам следующим образом:

+--------------+-------------+
|    number    |    weight   |
+--------------+-------------+
|     42       |      4      |
|     33       |      3      |
|     30       |      2      |
|  0xdeadbeef  |      1      |
+--------------+-------------+

Чем выше вес, тем выше вероятность, которую мы выбираем данный номер. Вы можете видеть весит как ряд «ведра», которые мы назначаем каждому номеру из списка. Впоследствии мы случайным образом (случайная униформа) пытаемся ударить одно ведро. После удара по ведру, мы проверяем, на что соответствует это ведро. Общее количество ведер, которое мы можем поразить, равно сумме весов:

4 + 3 + 2 + 1 = 10

Вероятность удара ведра на основе числа из списка показана ниже:

+--------------+-----------------------+
|    number    |       probability     |
+--------------+-----------------------+
|     42       |       4/10 = 0.4      |
|     33       |       3/10 = 0.3      |
|     30       |       2/10 = 0.2      |
|  0xdeadbeef  |       1/10 = 0.1      |
+--------------+-----------------------+

Чтобы обобщить это за n Числа, мы можем придумать следующую формулу:

В других математических словах:

Эта формула выглядит знакомой вам? Это называется терминал позитивного целого числа n ; Из Википедии :

Терминал был придуман Дональдом Е. Кнутью в своем искусстве компьютерных программиров. Это аддитивная аналог факториальной функции, который является продуктом целых чисел от 1 до n. Он использовал его, чтобы проиллюстрировать расширение домена от положительных целых чисел на реальные числа.

Теперь давайте сделаем наши руки грязными с некоторым кодом. Вычислить терминал n :

termial_of_n = sum(range(1, len(my_list) + 1))  # O(N)

Еще один, более эффективный способ, чтобы вычислить терминал n – использовать Биномиальный коэффициент Принцип и вычислить (Len (my_list) + 1) над 2 :

l = len(my_list)
# (l + 1) over 2 = l! / (2!*(l-2)!) = l * (l - 1) / 2
termial_of_n = ((l**2) + l) >> 1  # O(1)

Наконец, мы можем выбрать случайную (случайную однородную) ведро из наших ведер:

import random
choice = random.randrange(termial_of_n)

Результатом, хранящийся в вариабельном выборе, удерживает целое число, начиная от 0 до 9 (включательно) и представляет индекс к списку ведер, которые мы создали ранее:

+--------------+---------------+---------------+
|    choice    |     bucket    |     number    |
+--------------+---------------+---------------+
|      0       |       1       |       42      |
+--------------+---------------+---------------+
|      1       |       2       |       42      |
+--------------+---------------+---------------+
|      2       |       3       |       42      |
+--------------+---------------+---------------+
|      3       |       4       |       42      |
+--------------+---------------+---------------+
|      4       |       5       |       33      |
+--------------+---------------+---------------+
|      5       |       6       |       33      |
+--------------+---------------+---------------+
|      6       |       7       |       33      |
+--------------+---------------+---------------+
|      7       |       8       |       30      |
+--------------+---------------+---------------+
|      8       |       9       |       30      |
+--------------+---------------+---------------+
|      9       |       10      |   0xdeadbeef  |
+--------------+---------------+---------------+

Как мы находим, какой номер мы попали через случайно выбранное ведро для любого n ?

Давайте пересмотрим, как мы вычислили терминное число N, используя формулу на основе биномиальной коэффициента:

l = len(my_list)
termial_of_n = ((l**2) + l) >> 1

На основании определения функции терминации мы знаем, что независимо от n , мы всегда назначаем 1 ведро к номеру при индексе 0, 2 ведра к номеру при индексе 1, 3 ведра к номеру при индексе 2 и так далее. Используя эти знания, мы можем преобразовать формулу биномиальной коэффициента к следующему уравнению:

choice = ((i**2) + i) >> 1

Следующим шагом является поиск I, который удовлетворяет данному уравнению. Уравнение – это квадратичная функция, описанная как:

a*i**2 + b*i + c = 0

куда:

a = 1/2
b = 1/2
c = -choice

Как Выбор Ожидается, что он всегда будет неотрицательным целым числом (индекс к списку ведра), мы можем искать решение, которое всегда приводит к неотрицательному целым числу (сокращение одного дискриминантного срока, который всегда приводит к отрицательному i ).

import math
# D = b**2 - 4*a*c
# x1 = (-b + math.sqrt(D)) / (2*a)
# x2 = (-b - math.sqrt(D)) / (2*a)
# Given:
#   a = 1/2
#   b = 1/2
#   c = -choice
# D = (1/2)**2 + 4*0.5*choice = 0.25 + 2*choice
i = math.floor(-0.5 + math.sqrt(0.25 + (choice << 1)))

Решение должно быть округлено, используя математика Как соответствует перевернутому индексу относительно n Отказ По мере того, как я перевернут, окончательное решение (индекс к исходному списку):

my_list[n - 1 - i]

Давайте предположим:

  • Лен Функция может вернуть длину списка в O (1) время
  • Random.randrange * работает в O (1) время
  • Мы используем уравнение на основе биномиального коэффициента для вычисления терминала n

Весь вычисление сделано в O (1) Время с O (1) Космос.

Если бы мы использовали сумма На основе вычисления терминала n Алгоритм станет O (n) Время с O (1) Космос.

Я разработал этот алгоритм для Рекомендательный двигатель Thoth Отказ Его основной целью является предпочтение более поздних версиях пакетов в Resolver во время разрешения пакетов программного обеспечения Python.

Я был бы счастлив за любые отзывы или любые подобные подходы.

Полное решение можно найти на мой Github Gist :

Оригинал: “https://dev.to/fridex/termial-random-for-prioritized-picking-an-item-from-a-list-22jh”