簡體   English   中英

如何生成一個不以 0 開頭且具有唯一數字的隨機 4 位數字?

[英]How to generate a random 4 digit number not starting with 0 and having unique digits?

這幾乎可以正常工作,但有時數字以 0 開頭:

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

我找到了很多例子,但它們都不能保證序列不會以0開頭。

我們生成 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)))

生成的數字是等概率的,我們一步得到一個有效的數字。

循環直到你有你喜歡的東西:

import random

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

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

這是非常相似的其他答案,但不是sampleshuffle ,你可以在1000-9999范圍內畫一個隨機整數直到你得到一個只包含獨特的數字:

import random

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

print(val)

正如@Claudio 在評論中指出的那樣,范圍實際上只需要 1023 - 9876,因為該范圍之外的值包含重復的數字。

通常random.randint將比random.shufflerandom.choice因此即使更有可能需要多次繪制(如@karakfa 所指出的),它也比任何shuffle快 3 倍, choice方法也需要join個位數。

我不太了解 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)

拒絕抽樣法。 從 10 位數字創建一個 4 位數字隨機組合,如果不符合條件則重新采樣。

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

注意到這與@Austin Haskings 的回答基本相同

[已修復] 在一個位置上移動所有四位數字是不正確的。 用固定位置交換前導零也不對。 但是前導零與九個位置中的任何一個隨機交換是正確的,並且概率相等:

""" 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])))

您可以對 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)

你想要多少次。 它非常快,並提供均勻分布的隨機數。

我不知道 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
  • 使用查找變量將隨機數一一轉換為數字。 每次查找后,通過刪除已使用的數字來改變查找變量:

     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()); }

這是我的方法

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

更一般地,給定一個生成器,您可以使用內置filternext來獲取滿足某個測試函數的第一個元素。

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

將生成器與next結合

一種 Pythonic 的編寫方式是使用 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]

10239877被用作邊界,因為低於 1023 或大於 9876 的整數不能有 4 個唯一的區別數字。

然后,您只需要random.choice生成非常快的一代:

print(random.choice(possible_numbers))
# 7234

免責聲明:這是一種糟糕的反 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 之后保留的原始數字,用於調試目的。這不是那種立即工作的代碼......)。 評論這兩個驗證使它更快。

編輯:關於檢查這個和那個

這是一種在最小有效輸入集 (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) 添加驗證,現在我只是假設
"(something mod 9)+1" 不會是 0。

這將允許第一個數字后為零 -

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM