首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python - Collatz序列

Python - Collatz序列
EN

Code Review用户
提问于 2019-09-18 20:38:03
回答 3查看 8.5K关注 0票数 9

我可以查看一下第三章的Collatz序列用Python自动完成无聊的工作的代码吗?

Collatz序列编写一个名为collatz()的函数,该函数有一个名为number的参数。如果数字是偶数,那么collatz()应该打印number // 2并返回这个值。如果数字是奇数,那么collatz()应该打印并返回3* number + 1。然后编写一个程序,让用户输入一个整数,并一直对该数字调用collatz(),直到函数返回值1。(令人惊讶的是,这个序列实际上适用于任何整数-使用这个序列,迟早会达到1!)就连数学家也不知道为什么。您的程序正在探索所谓的Collatz序列,有时被称为“最简单的不可能的数学问题”)。记住用int()函数将输入()的返回值转换为整数;否则,它将是一个字符串值。提示:整数是偶数%2 == 0,如果数字为%2 == 1,则为奇数。该程序的输出可能如下所示:输入编号:3 10 5 16 8 4 2 1输入验证向上一个项目添加try和The语句,以检测用户是否键入非整数字符串。通常,int()函数如果传递一个非整数字符串,就会引发ValueError错误,如int(‘幼犬’)中所示。在enter子句中,打印一条消息给用户,说明他们必须输入一个整数。

我主要想知道是否有一个更干净的方法来写我的解决方案。

代码语言:javascript
复制
def collatz(num):
    while num > 1:
        if num % 2 == 0:
            print(num//2)
            num = num //2
        elif num % 2 ==1:
            print(3*num+1)
            num = 3*num+1
        else:
            print(num)

def getNum():
    global num
    num = (input("> "))
    try:
        num = int(num)
    except ValueError:
        print('plese enter a number')
        getNum()
getNum()
collatz(num)
EN

回答 3

Code Review用户

回答已采纳

发布于 2019-09-18 21:29:59

首先,注意重复计算的方式:

代码语言:javascript
复制
print(num//2)
num = num //2

这可能不会引起这个特定代码的问题,但这不是一个好的实践。您所做的工作是所需的两倍,一旦开始编写更复杂的代码,就会导致性能问题。进行一次计算,并保存结果。不过,在这种情况下,您所需要做的就是反转这些行并使用num

代码语言:javascript
复制
num = num // 2
print(num)

此外,确保您有适当的间隔围绕运算符,并保持一致

您的ifelif案例是相互排斥的,您的else不应该发生。如果第一个条件为真,则其他条件必须是假的,反之亦然。没有必要再做第二次检查。一旦重写,您将看到打印在每一种情况下是不必要的。你只需在以下几个地方打印:

代码语言:javascript
复制
while num > 1:
    if num % 2 == 0:
        num = num // 2

    else:
        num = 3 * num + 1

    print(num)

由于您只是根据条件重新组合num的两个选项之一,这里还可以清晰地使用条件表达式:

代码语言:javascript
复制
while num > 1:
    num = (num // 2) if num % 2 == 0 else (3 * num + 1)
    print(num)

大括号是不必要的,但我认为它们在这里是有用的,因为涉及的操作符很多。

在这里打印数字并不理想。在大多数代码中,您需要能够使用您生成的数据。如果您想分析产生的序列,您将不得不做一些拦截标准输出,这是昂贵和过于复杂的。使其成为累加并返回列表的函数。在下面的示例中,我还添加了一些类型提示,以使数据的类型更加清晰:

代码语言:javascript
复制
from typing import List

def collatz(starting_num: int) -> List[int]:
    nums = [starting_num]

    num = starting_num
    while num > 1:
        num = (num // 2) if num % 2 == 0 else (3 * num + 1)
        nums.append(num)

    return nums

或者,一种更简洁的方法是使其成为一个产生数字的生成器

代码语言:javascript
复制
# Calling this function returns a generator that produces ints
# Ignore the two Nones, as they aren't needed for this kind of generator
def collatz_gen(starting_num: int) -> Generator[int, None, None]:
    yield starting_num

    num = starting_num
    while num > 1:
        num = (num // 2) if num % 2 == 0 else (3 * num + 1)
        yield num

>>> list(collatz_gen(5))
[5, 16, 8, 4, 2, 1]

关于getNum,有几个值得注意的地方:

Python使用“蛇”使用“蛇”,不是"camelCase“

您在这里使用global num是不必要的,也是令人困惑的。和以前一样,显式地return函数生成的任何数据:

代码语言:javascript
复制
def get_num() -> int:
    raw_num = input("> ")

    try:
        return int(raw_num)

    except ValueError:
        print('Please enter a number')
        return get_num()

注意,我们不是重新分配全局num,而是返回数字。我还用了一些比较合适的名字,把东西分开了一些。从概念上讲,我认为num = input("> ")是错误的。在运行时,num不包含数字(它包含一个字符串)。

这不是递归的好使用。这很可能不会给你带来任何问题,但是如果你的用户真的很笨,输入错误的数据1000次,你的程序就会崩溃。只需使用循环:

代码语言:javascript
复制
def get_num() -> int:
    while True:
        raw_num = input("> ")

        try:
            return int(raw_num)

        except ValueError:
            print('Please enter a number')

在Python等语言中,在无法保证函数递归次数的情况下,请小心使用递归。

我也可能会把它命名为更接近ask_for_num的东西。"get“并不能清楚地说明数据来自何处。

总之,你最终会得到:

代码语言:javascript
复制
from typing import Generator

def collatz_gen(starting_num: int) -> Generator[int, None, None]:
    yield starting_num

    num = starting_num
    while num > 1:
        num = (num // 2) if num % 2 == 0 else (3 * num + 1)
        yield num

def ask_for_num() -> int:
    while True:
        raw_num = input("> ")

        try:
            return int(raw_num)

        except ValueError:
            print('Please enter a number')

可用于:

代码语言:javascript
复制
num = ask_for_num()

for n in collatz_gen(num):
    print(n)
票数 9
EN

Code Review用户

发布于 2019-09-18 22:14:57

提示符

这里最明显的错误做法是使用全局变量。您的函数不应将num设置为副作用,而是应该对结果进行return

getNum()不是这个函数的好名称。PEP 8是Python的官方风格指南,它说函数名应该是lower_case_with_underscores。此外,"get“意味着函数正在检索已经存储在某个地方的数据,这里的情况并非如此。最后,"Num“应该更具体。

使用递归是不合适的。如果你想要一个循环,就写一个循环。

代码语言:javascript
复制
def ask_integer():
    """
    Return an integer entered by the user (repeatedly prompting if
    the input is not a valid integer).
    """
    while True:
        try:
            return int(input("> "))
        except ValueError:
            print("Please enter an integer")

num = ask_integer()

collatz函数

严格地说,你没有遵守指示。您的解决方案没有错或坏--您只是没有按照给定的规范实现collatz函数,该规范规定您应该打印并返回一个数字。

代码语言:javascript
复制
def collatz(num):
    """
    Given a number, print and return its successor in the Collatz sequence.
    """
    next = num // 2 if num % 2 == 0 else 3 * num + 1
    print(next)
    return next

num = ask_integer()
while num > 1:
    num = collatz(num)
票数 12
EN

Code Review用户

发布于 2019-09-19 08:22:01

为了完整起见,collatz的递归实现(您已经获得了足够多的输入num的好建议):

代码语言:javascript
复制
def collatz(num):
    print(num)
    if num == 1:
        return num
    if num % 2 == 0:
        return collatz(num // 2)
    return collatz(3 * num + 1)

collatz(3)

输出

代码语言:javascript
复制
3
10
5
16
8
4
2
1
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/229270

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档