使用列表理解来解决Collatz猜想



有没有一种方法可以使用列表理解来锻炼Collatz猜想,而不使用while语句,或者另一种方法将n值附加到ls,而不在每个语句后添加ls?

from random import choice
from time import sleep
n = choice([x for x in range(2, 99*99) if all(x%y != 0 for y in range(2, x))])
ls = []
ls.append(n)
while True:
if n % 2 == 0:
n = n // 2
ls.append(n)
elif n % 2 != 0:
n = (3 * n) + 1
ls.append(n)
if n == 1:
break
print(ls)

单向("缺少"初始数字,但我认为这对目的不重要(:

print(f'{n}:')
print([n := 3*n+1 if n%2 else n//2
for _ in iter(lambda: n, 1)])

n = 92:的输出

92:
[46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]

和一个(ab(使用列表comp,基于kolypto的:

print([memo
for memo in [[n]]
for n in memo
if n == 1 or memo.append(n//2 if n%2==0 else n*3+1)
][0])

n = 92:的输出

[92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]

我仍然认为while循环是合适的方式,但事实证明它并没有快多少。解决从1到5000的所有n的时间:

78 ms  while_loop_ewz93
87 ms  list_comp_Kelly
126 ms  list_comp_kolypto
82 ms  list_comp_kolypto_Kellied

我的iter(lambda: n, 1)模拟while n != 1:。更一般地,while condition:可以用iter(lambda: bool(condition), False)来模拟。(如果条件已经是bool,例如iter(lambda: mystring.startswith('x'), False),则不需要显式bool。(

基准代码(在线试用!(修改了ewz93和kolypto的代码,也不包括起始号(为了更公平的比较(:

from timeit import repeat
def while_loop_ewz93(n):
ls = []
while n != 1:
n = n // 2 if n % 2 == 0 else (3 * n) + 1
ls.append(n)
return ls
def list_comp_Kelly(n):
return [n := 3*n+1 if n%2 else n//2
for x in iter(lambda: n, 1)]
def list_comp_kolypto(n):
return [
*(lambda memo: [
memo.append(n // 2 if n%2==0 else n*3+1) or memo[-1]
for n in memo
if memo[-1] != 1
])([n])
]
def list_comp_kolypto_Kellied(n):
return [
memo
for memo in [[n]]
for n in memo
if n == 1 or memo.append(n//2 if n%2==0 else n*3+1)
][0]
funcs = [
while_loop_ewz93,
list_comp_Kelly,
list_comp_kolypto,
list_comp_kolypto_Kellied,
]
for _ in range(3):
for func in funcs:
t = min(repeat(lambda: list(map(func, range(1, 5001))), number=1))
print('%3d ms ' % (t * 1e3), func.__name__)
print()

当你还不知道要走多少步时,while就是你所使用的,因为这有点像是在寻找一个值的猜想的逻辑中,所以没有真正的方法。我个人认为使用while循环没有什么不好的。

在保持while循环的同时,您仍然可以使代码更加紧凑和可读,例如:

from random import choice
n = choice([x for x in range(2, 99 * 99) if all(x % y != 0 for y in range(2, x))])
ls = [n]
while n != 1:
n = n // 2 if n % 2 == 0 else (3 * n) + 1
ls.append(n)
print(ls)

编辑:

@Kelly Bundys答案的一个稍微修改过的版本对我来说是一个技巧,让它变得更加紧凑(但更不用说可读性了(:

from random import choice
n = choice([x for x in range(2, 99 * 99) if all(x % y != 0 for y in range(2, x))])
ls = [n] + [n := n // 2 if n % 2 == 0 else (3 * n) + 1 for _ in iter(lambda: n, 1)]
print(ls)
seq = [
*(lambda memo: [n] + [
memo.append(n // 2 if n%2==0 else n*3+1) or memo[-1]
for n in memo
if memo[-1] != 1
])([n])
]

这不是一个纯粹的理解,但有点。

核心思想是引入一个命名列表memo,我可以将其称为变量并将值推送到

n=1024
ls=[n]
[ls.append(n//2 if n%2==0 else 3*n+1) for n in ls if n!=1]
print (ls)

相关内容

最新更新