Получение 1 числа из 2 диапазонов с помощью selection () и randint () - PullRequest
2 голосов
/ 06 апреля 2020

У меня есть блок кода с

import random as r
value = r.choice(r.randint(10, 30), r.randint(50, 100))
print (value)

Почему выдается следующая ошибка и как ее исправить?

TypeError: choice() takes 2 positional arguments but 3 were given 

Ответы [ 2 ]

2 голосов
/ 06 апреля 2020

choice принимает последовательность элементов на выбор, а не varargs. Оберните аргументы, чтобы сделать аноним tuple или list, и он заработает, изменив:

value = r.choice(r.randint(10, 30), r.randint(50, 100))

на любой из:

value = r.choice((r.randint(10, 30), r.randint(50, 100)))  # Tuple
value = r.choice([r.randint(10, 30), r.randint(50, 100)])  # List
1 голос
/ 06 апреля 2020

random.choice принимает одну последовательность. У вас есть несколько вариантов того, как сгенерировать нужное число.

Чтобы исправить немедленную ошибку, сделайте входные данные последовательностью:

value = r.choice([r.randint(10, 30), r.randint(50, 100)])

Это не очень хорошее решение потому что он генерирует два числа, что расточительно. Кроме того, результирующее распределение зависит от размеров диапазонов, что, вероятно, неверно.

Более общий способ решить эту проблему - сгенерировать одно число и отобразить его в желаемом диапазоне:

v = r.randint(0, 21 + 51)
value = v + 10 if v <= 20 else v - 21 + 50

Если вы хотите указать входные данные как диапазоны и получить равномерное распределение по всем из них, вы можете обобщить так:

def from_ranges(*ranges):
    n = sum(map(len, ranges))
    v = random.randrange(n)
    for range in ranges:
        k = len(range)
        if v < k:
            return range[v]
        v -= k

Это хорошо, потому что диапазоны - это небольшие объекты, которые не занимает много места, но дает вам большую гибкость с точки зрения того, что вы можете указать.

Для примера в вашем вопросе вы бы назвали функцию как

>>> from_ranges(range(10, 31), range(50, 101))

Вы также можете исключить окончательный линейный поиск в пользу бинарного поиска, накапливая длины в кумулятивную сумму:

from bisect import bisect
from itertools import accumulate
from random import randrange

def from_ranges(*ranges):
    lengths = list(accumulate(map(len, ranges)))
    v = randrange(lengths[-1])
    r = bisect(lengths, v)
    offset = lengths[r - 1] if r > 0 else 0
    return ranges[r][v - offset]

Это решение будет быстрее, если вы запомните накопление до случайное число генерируется. В противном случае он дает небольшое преимущество перед первым решением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...