免责声明:这是一种糟糕的反 Python 方法,仅限于基准测试部分(请参阅 @DavidHammen 的 cmets 和 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,可以通过运行 sn-p 来测试 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。