使用列表理解来解决 Collat​​z 猜想

Using List comprehensions to solve Collatz conjecture

有没有一种方法可以在不使用 while 语句的情况下使用列表推导来验证 Collat​​z 猜想,或者是否有其他方法将 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)