Подтвердить что ты не робот

Как создать случайное 4-значное число, не начинающееся с 0 и имеющее уникальные цифры?

Это работает почти отлично, но число начинается с 0 иногда:

import random
numbers = random.sample(range(10), 4)
print(''.join(map(str, numbers)))

Я нашел много примеров, но никто из них не гарантирует, что последовательность не будет начинаться с 0.

4b9b3361

Ответ 1

Мы генерируем первую цифру в диапазоне 1-9, затем берем следующие 3 из оставшихся цифр:

import random

# We create a set of digits: {0, 1, .... 9}
digits = set(range(10))
# We generate a random integer, 1 <= first <= 9
first = random.randint(1, 9)
# We remove it from our set, then take a sample of
# 3 distinct elements from the remaining values
last_3 = random.sample(digits - {first}, 3)
print(str(first) + ''.join(map(str, last_3)))

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

Ответ 2

Просто запустите цикл, пока не найдете что-то, что вам нравится:

import random

numbers = [0]
while numbers[0] == 0:
    numbers = random.sample(range(10), 4)

print(''.join(map(str, numbers)))

Ответ 3

Это очень похоже на другие ответы, но вместо sample или shuffle вы можете нарисовать случайное целое число в диапазоне 1000-9999, пока не получите тот, который содержит только уникальные цифры:

import random

val = 0  # initial value - so the while loop is entered.
while len(set(str(val))) != 4:  # check if it duplicate free
    val = random.randint(1000, 9999)

print(val)

Как заметил @Claudio в комментариях, диапазон фактически должен быть только 1023 - 9876, потому что значения вне этого диапазона содержат повторяющиеся цифры.

Обычно random.randint будет намного быстрее, чем random.shuffle или random.choice, поэтому, даже если это более вероятно, нужно рисовать несколько раз (как указано в @karakfa), он в 3 раза быстрее, чем любой shuffle, choice, который также должен join содержать одиночные цифры.

Ответ 4

Я не знаю Python, но что-то вроде

digits=[1,2,3,4,5,6,7,8,9] <- no zero
random.shuffle(digits)
first=digits[0] <- first digit, obviously will not be zero
digits[0]=0 <- used digit can not occur again, zero can
random.shuffle(digits)
lastthree=digits[0:3] <- last three digits, no repeats, can contain zero, thanks @Dubu

Более полезная итерация, фактически создающая число:

digits=[1,2,3,4,5,6,7,8,9]   # no zero
random.shuffle(digits)
val=digits[0]                # value so far, not zero for sure
digits[0]=0                  # used digit can not occur again, zero becomes a valid pick
random.shuffle(digits)
for i in range(0,3):
  val=val*10+digits[i]       # update value with further digits
print(val)

После кражи частей из других решений плюс применение наконечника от @DavidHammen:

val=random.randint(1,9)
digits=[1,2,3,4,5,6,7,8,9]
digits[val-1]=0
for i in random.sample(digits,3):
  val=val*10+i
print(val)

Ответ 5

метод выборки отказа. Создайте 4-значную случайную комбинацию из 10 цифр и повторите выбор, если она не соответствует критериям.

r4=0    
while r4 < 1000:
    r4=int(''.join(map(str,random.sample(range(10),4))))

заметил, что это по существу то же самое, что @Austin Haskings answer

Ответ 6

[Исправлено] Смещение всех четырех цифр на одной позиции неверно. Неправильное совпадение начального нуля с фиксированным положением тоже неверно. Но случайная свопа начального нуля с любой из девяти позиций правильна и дает равную вероятность:

""" Solution: randomly shuffle all numbers. If 0 is on the 0th position,
              randomly swap it with any of nine positions in the list.

  Proof
    Lets count probability for 0 to be in position 7. It is equal to probability 1/10 
  after shuffle, plus probability to be randomly swapped in the 7th position if
  0 come to be on the 0th position: (1/10 * 1/9). In total: (1/10 + 1/10 * 1/9).
    Lets count probability for 3 to be in position 7. It is equal to probability 1/10
  after shuffle, minus probability to be randomly swapped in the 0th position (1/9)
  if 0 come to be on the 0th position (1/10) and if 3 come to be on the 7th position
  when 0 is on the 0th position (1/9). In total: (1/10 - 1/9 * 1/10 * 1/9).
    Total probability of all numbers [0-9] in position 7 is:
  9 * (1/10 - 1/9 * 1/10 * 1/9) + (1/10 + 1/10 * 1/9) = 1
    Continue to prove in the same way that total probability is equal to
  1 for all other positions.
    End of proof. """

import random
l = [0,1,2,3,4,5,6,7,8,9]
random.shuffle(l)
if l[0] == 0:
    pos = random.choice(range(1, len(l)))
    l[0], l[pos] = l[pos], l[0]
print(''.join(map(str, l[0:4])))

Ответ 7

Вы можете использовать полный диапазон для 3 чисел, затем выбрать число из числа оставшихся номеров:

import random
numbers = random.sample(range(0,10), 3)
first_number = random.choice(list(set(range(1,10))-set(numbers)))
print(''.join(map(str, [first_number]+numbers)))

Другой способ, если выбор необходимо повторить (и если вы останетесь разумным по количеству цифр), состоит в том, чтобы предварительно вычислить список возможных выходов с помощью itertools.permutations, отфильтровывая те, у которых начальный ноль, и построение из него целых чисел:

import itertools,random

l = [int(''.join(map(str,x))) for x in itertools.permutations(range(10),4) if x[0]]

Это некоторое время вычисления, но после этого вы можете позвонить:

random.choice(l)

столько раз, сколько хотите. Он очень быстрый и обеспечивает равномерно распределенное случайное.

Ответ 8

Я не знаю Python, поэтому я поставлю псевдокод-иш-решение для этой конкретной проблемы:

  • Создайте переменную поиска, содержащую список цифр, основанный на 0:

    lu = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
  • Сгенерируйте четыре случайных числа на основе 0 следующим образом:

    r1 = random number between 0 and 8
    r2 = random number between 0 and 8
    r3 = random number between 0 and 7
    r4 = random number between 0 and 6
    
  • Используйте переменную lookup для преобразования случайных чисел в цифры по одному. После каждого поиска измените переменную поиска, удалив используемую цифру:

    d1 = lu[r1]
    lu.remove(d1)
    lu.insert(0)
    
    d2 = lu[r2]
    lu.remove(d2)
    
    d3 = lu[r3]
    lu.remove(d3)
    
    d4 = lu[r4]
    lu.remove(d4)
    
  • Распечатайте результат:

    print concatenate(d1, d2, d3, d4)
    

Можно немного обобщить эту идею. Например, вы можете создать функцию, которая принимает список (цифр) и число (желаемая длина результата); функция вернет номер и изменит список, удалив использованные цифры. Ниже приведена реализация этого решения JavaScript:

function randomCombination(list, length) {
    var i, rand, result = "";
    for (i = 0; i < length; i++) {
        rand = Math.floor(Math.random() * list.length);
        result += list[rand];
        list.splice(rand, 1);
    }
    return result;
}

function desiredNumber() {
    var list = [1, 2, 3, 4, 5, 6, 7, 8, 9],
        result;
    result = randomCombination(list, 1);
    list.push(0);
    result += randomCombination(list, 3);
    return result;
}

var i;
for (i = 0; i < 10; i++) {
    console.log(desiredNumber());
}

Ответ 9

Вот как бы я это сделал

while True:
    n = random.randrange(1000, 10000)
    if len(set(str(n))) == 4: # unique digits
        return n

В более общем плане, учитывая генератор, вы можете использовать встроенные функции filter и next, чтобы взять первый элемент, который удовлетворяет некоторой тестовой функции.

numbers = iter(lambda: random.randrange(1000, 10000), None) # infinite generator
test = lambda n: len(set(str(n))) == 4
return next(filter(test, numbers))

Ответ 10

Объединить генераторы с next

Питоновским способом писать будет использование 2 вложенных генераторов и next:

from random import randint
from itertools import count

print(next(i for i in (randint(1023, 9876) for _ in count()) if len(set(str(i))) == 4))
# 8756

В основном это однострочный вариант ответа @MSeifert

Предварительно обработать все допустимые числа

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

import random    
possible_numbers = [i for i in range(1023, 9877) if len(set(str(i))) == 4]

1023 и 9877 используются как границы, потому что нет int ниже 1023 или больше, чем 9876, может иметь 4 уникальных номера distince.

Тогда вам понадобится random.choice для очень быстрого поколения:

print(random.choice(possible_numbers))
# 7234

Ответ 11

Отказ от ответственности: это ужасный подход против Python, строго для эталонной части (см. комментарий @DavidHammen, и http://ideone.com/qyopLF) Идея состоит в том, чтобы генерировать порядковые номера цифр за один шаг, а затем фиксировать любые столкновения:

rnd=random.randint(0,4535)
(rnd,d1)=divmod(rnd,9)
(rnd,d2)=divmod(rnd,9)
#(rnd,d3)=divmod(rnd,8)
#(rnd,d4)=divmod(rnd,7)
(d4,d3)=divmod(rnd,8) # miracle found: 1 divmod happens to run faster than 2

Теперь мы имеем d1 = 0..8, d2 = 0..8, d3 = 0..7, d4 = 0..6, его можно проверить, выполнив фрагмент с rnd = 4535 (4535 = 9 * 9 * 8 * 7-1, кстати)

Во-первых, d1 должен быть исправлен

d1=d1+1 # now d1 = 1..9

Тогда d2 должно "пропустить" d1, если необходимо

if d2>=d1
  d2=d2+1 # now d2 = 0..9 "-" d1

Тогда то же самое нужно сделать с оставшимися цифрами, получив уродливую скорость:

if d3>=d1:
  d3=d3+1    # now d3 = 0..8 "-" d1
  if d3>=d2:
    d3=d3+1  # now d3 = 0..9 "-" {d1,d2}
elif d3>=d2: # this branch prepares for the other variant
  d3=d3+1
  if d3>=d1: # ">=" is preserved for consistency, here "==" may occur only
    d3=d3+1

И последняя часть - катастрофическая:

if d4>=d1:
  d4=d4+1
  if d4>=d2:
    d4=d4+1
    if d4>=d3:
      d4=d4+1
  elif d4>=d3:
    d4=d4+1
    if d4>=d2:
      d4=d4+1
elif d4>=d2:
  d4=d4+1
  if d4>=d1:
    d4=d4+1
    if d4>=d3:
      d4=d4+1
  elif d4>=d3:
    d4=d4+1
    if d4>=d1:
      d4=d4+1
elif d4>=d3:
  d4=d4+1
  if d4>=d2:
    d4=d4+1
    if d4>=d1:
      d4=d4+1
  elif d4>=d1:
    d4=d4+1
    if d4>=d2:
      d4=d4+1

Для более длинных чисел он может работать быстрее с битовыми полями, но я не вижу тривиального пути. (Проверка отношений >= один раз недостаточно, потому что столкновение может легко произойти после выполнения инкремента. например d1 = 1, d2 = 2, d3 = 1: d3 сталкивается с d1, но он не сталкивается с d2 изначально. Однако после того, как "опуская отверстие" на 1, d3 становится 2, и теперь он сталкивается с d2. Нет никакого тривиального способа обнаружить это столкновение заранее)

Поскольку код воняет, как черт, я положил шаг проверки в конце

val = d1*1000 + d2*100 + d3*10 + d4
#if len(set(str(val))) != 4: print(str(val)+" "+str(o1)+","+str(o2)+","+str(o3)+","+str(o4))
if len(set(str(val))) != 4: print(val)

Это уже быстрее, чем другой действительно быстрый код (прокомментированная проверка отображает исходные цифры, сохраненные после divmod-s, для целей отладки. Это не тот код, который работает сразу...). Комментирование обеих проверок делает это еще быстрее.

EDIT: об этом и о том, что

Это подход, поддерживающий соотношение 1:1 между минимальным набором допустимых входов (0... 4535) и действительными выходами (9 * 9 * 8 * 7 возможных 4-значных чисел с четными цифрами, пуско-с-0). Таким образом, простой цикл может и должен генерировать все числа, их можно проверять один за другим, и их можно собирать в набор, например, чтобы увидеть, являются ли они различными результатами

Практически:

collect=set()
for rnd in range(0,4536):
    (rnd,d1)=divmod(rnd,9)
    ... rest of the code, also the verification step kept active ...
    collect.add(val)
print(len(collect))

1) Он не будет печатать что-либо в цикле (все результаты представляют собой 4-значные цифры с разными цифрами)

2) Он будет печатать 4536 в конце (все результаты различны)

Можно добавить проверку для первой цифры (d1), здесь и сейчас я просто предполагаю, что
"(что-то mod 9) +1" не будет 0.

Ответ 12

Это даст нули после первой цифры -

numbers = random.sample(range(1,10),1) + random.sample(range(10),3)