使用列表理解来解决 Collatz 猜想
Using List comprehensions to solve Collatz conjecture
有没有一种方法可以在不使用 while 语句的情况下使用列表推导来验证 Collatz 猜想,或者是否有其他方法将 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)
好吧,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)
一种方式(“缺少”初始数字,但我认为这对目的而言并不重要):
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)使用基于 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
将 ewz93 和 kolypto 的基准代码 (Try it online!) 修改为也不包括起始编号(为了更公平的比较):
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()
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)
有没有一种方法可以在不使用 while 语句的情况下使用列表推导来验证 Collatz 猜想,或者是否有其他方法将 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)
好吧,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)
一种方式(“缺少”初始数字,但我认为这对目的而言并不重要):
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)使用基于 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
将 ewz93 和 kolypto 的基准代码 (Try it online!) 修改为也不包括起始编号(为了更公平的比较):
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()
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)