如何生成一个不以0开头且每个数字都不相同的随机4位数?

40

这个基本可以工作,但是有时候数字以0开头:

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

我找到了很多例子,但没有一个保证序列不会以0开头。


3
range()函数中,你可以指定起始值。在你的情况下,起始值可以是1。 - kuro
33
这个功能基本上是有效的,但有时候数字会以0开头。如果你运行足够多次,大约有10%的概率会发生这种情况。 - WBT
5
相关 PPCG 挑战 - mbomb007
1
给楼主:请取消您对已接受答案的接受。它不应该是被接受的答案。它是前六个答案中最慢的,而且差距相当大。 - David Hammen
显示剩余5条评论
12个回答

1
免责声明:这是一种可怕的反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个可能的四位数,其数字不同且不以0开头)之间1:1关系的方法。因此,一个简单的循环可以并且应该生成所有数字,它们可以逐个检查,并且可以收集到一个集合中,例如为了查看它们是否都是不同的结果。

实际上:

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。


有趣。你检查过结果是否均匀分布了吗? - Eric Duminil
@EricDuminil,对于有效输出集合的分布遵循randint()的分布。我检查了将输入映射到输出的正确性,并添加了一些相关代码。 - tevemadar

-6

这将允许在第一个数字后出现零 -

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

4
它还将允许一个数字重复。 - Mark Ransom
你需要在将两个数字相加之前将第一个数字乘以1000吗?还是加号只是将它们连接起来? - Dima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接