Python – A sequência Collatz
On Dezembro 1, 2020 by adminPosso obter uma revisão do meu código para a Sequência Collatz do capítulo três de Automatizar as coisas chatas com Python ?
A sequência Collatz
Escreva uma função chamada collatz () que tem um parâmetro chamado número. Se o número for par, collatz () deverá imprimir o número // 2 e retornar este valor. Se o número for ímpar, então collatz () deve imprimir e retornar 3 * número + 1.
Em seguida, escreva um programa que permite ao usuário digitar um inteiro e que continua chamando collatz () naquele número até que o função retorna o valor 1. (Por incrível que pareça, esta sequência realmente funciona para qualquer inteiro — mais cedo ou mais tarde, usando esta sequência, você chegará a 1! Mesmo os matemáticos não têm certeza do motivo. Seu programa está explorando o que é chamado de sequência Collatz , às vezes chamado de “o problema matemático impossível mais simples.”)
Lembre-se de converter o valor de retorno de input () em um inteiro com a função int (); caso contrário, será um valor de string.
Dica: um número inteiro é igual se o número% 2 == 0 e é ímpar se o número% 2 == 1.
A saída deste programa pode ser parecida com esta:
Enter number: 3 10 5 16 8 4 2 1
Validação de entrada
Adicione instruções try e except ao projeto anterior para detectar se o usuário digita uma string não inteira. Normalmente, a função int () gerará um erro ValueError se for passada uma string não inteira, como em int (“filhote”). Na cláusula except, imprima uma mensagem para o usuário dizendo que ele deve inserir um número inteiro.
Estou pensando principalmente se existe uma maneira mais limpa de escrever meu solução.
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)
Comentários
- Por favor, adicione alguma descrição indicando o que este capítulo 3 está recomendando com exemplos de entrada e saída, ninguém ‘ vai adivinhar o que é
- @Edmad Broctor e @Carcigenicate Obrigado por seus comentários. Fui em frente e revisei minha postagem.
- ” Surpreendentemente, essa sequência realmente funciona para qualquer inteiro – mais cedo ou mais tarde, usando essa sequência, você chegará em 1! ” Na verdade, não ‘ não sabemos que funciona para qualquer número inteiro, a ideia de que sempre alcançará 1 é uma conjectura .
- @PierreCath é Tenho uma demonstração verdadeiramente maravilhosa desta proposição, que esta caixa de comentário é muito pequena para conter.
- @Acccumulation Uau ! Isso ‘ é impressionante, nesse caso você deve atualizar o artigo da Wikipedia e entrar em contato com a União Matemática Internacional para coletar sua medalha Fields.
Resposta
Primeiro, observe como você “está duplicando cálculos:
print(num//2) num = num //2
Isso pode não causa problemas com este código específico, mas não é uma boa prática. Você está fazendo o dobro do trabalho necessário, o que pode causar problemas de desempenho quando você começa a escrever um código mais complicado. Faça o cálculo uma vez e salve o resultado. Neste caso, porém, tudo o que você precisa fazer é inverter essas linhas e use num
:
num = num // 2 print(num)
Além disso, certifique-se de ter o espaçamento adequado em torno de operadores e seja consistente .
Seu if
e elif
casos são exclusivos um do outro, e seu else
nunca deve acontecer. Se a primeira condição for verdadeira, então a outra deve ser falsa e vice-versa. Não há necessidade de segunda verificação. Depois de reescrito, você verá que a impressão em todos os casos não é necessária. Você pode imprimir depois de:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Já que você está apenas reassing num
uma de duas opções com base em um condição, uma expressão condicional também pode ser usada aqui de forma limpa:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
As chaves não são necessárias, mas acho que são úteis aqui devido ao número de operadores envolvidos.
Imprimir os números não é o ideal aqui. Na maioria dos códigos, você precisa ser capaz de usar os dados que produz. Se você quiser analisar a sequência produzida, terá que fazer algo para interceptar o stdout, o que é caro e excessivamente complicado. Faça dela uma função que acumula e retorna uma lista.Nos exemplos a seguir, também adicionei algumas dicas de tipo para deixar mais claro qual é o tipo dos dados:
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
Ou, uma abordagem muito mais limpa é torná-lo um gerador que produz os números :
# 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]
Existem” algumas coisas notáveis sobre getNum
:
Python usa “snake_case” , não “camelCase”.
O uso de global num
aqui é desnecessário e confuso . Assim como antes, explicitamente return
quaisquer dados que a função produza:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Observe como, em vez de reatribuir um global num
, estamos apenas retornando o número. Também separei um pouco as coisas e usei alguns nomes mais apropriados. Conceitualmente, eu diria que num = input("> ")
está errado. No momento em que é executado, num
não contém um número (contém uma string).
Este não é um bom uso de recursão. Isso provavelmente não causará problemas, mas se seu usuário for realmente burro e inserir dados errados aproximadamente 1000 vezes, seu programa irá travar. Basta usar um loop:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
Em linguagens como Python, tome cuidado ao usar a recursão nos casos em que você não tem garantias sobre quantas vezes a função recursará.
Eu “d também provavelmente nomeie isso mais próximo de ask_for_num
. “get” não deixa muito claro de onde vêm os dados.
No conjunto, você “acabará com:
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")
Que pode ser usado como:
num = ask_for_num() for n in collatz_gen(num): print(n)
Resposta
Prompt
A má prática mais óbvia aqui é o uso de uma variável global. Em vez de definir num
como efeito colateral, sua função deve return
o resultado.
getNum()
não é um nome tão bom para a função. PEP 8 , o guia de estilo oficial para Python, diz que os nomes das funções devem ser lower_case_with_underscores
. Além disso, “get” implica que a função está recuperando um dado que já está armazenado em algum lugar, o que não é o caso aqui. Finalmente, “Num” deve ser mais específico.
O uso de recursão não é apropriado. Se você quiser um loop, escreva um loop.
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
função
Rigorosamente falando, você não seguiu as instruções. Sua solução não é errada ou ruim – você apenas não implementou a função collatz
de acordo com a especificação fornecida, que diz que você deve imprimir e retornar um único número.
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)
Comentários
- Talvez reverta o teste e elimine a comparação com 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Honestamente, acho que isso confunde a intenção. Nós ‘ re não verificando se
num % 2
é falsey; ‘ não é um predicado. Nós ‘ re verificando se ‘ é igual a 0. Acontece que eles ‘ são equivalentes em Python. -
next = ...
ofusca onext
. Neste contexto, ‘ provavelmente não é perigoso, mas apenas bom estar ciente - Embora tecnicamente você possa usar
if num % 2 else
, concordo que mostrar a intenção aqui é mais importante. eif num % 2 == 0 else
comunica melhor essa intenção. Eu concordo que você deve renomear sua variávelnext
, no entanto. É ‘ um mau hábito. - @ 200_success Obrigado por apontar minha falha em retornar o número e não simplesmente em imprimi-lo. Isso foi muito útil.
Resposta
Para fins de integridade, uma implementação recursiva para collatz
(você já tem boas sugestões suficientes para inserir num
):
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)
Saídas
3 10 5 16 8 4 2 1
Comentários
- Eu não ‘ t acho que essa resposta ajuda o OP. Embora uma definição recursiva seja certamente possível, ela não ‘ se ajusta à especificação que eles estão tentando atender. Além disso, este código tem alguma redundância: a função sempre retorna 1 (quando retorna), então por que ter um valor de retorno?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
Eu ‘ não tenho certeza se entendi. A recursão deve ter um caso base. Acontece que o caso base neste exemplo é senum == 1
. O exemplo de saída mostra que a função nem sempre retorna 1. - A função imprime outros valores, mas retorna apenas o valor 1.
- Não tenho certeza de qual é o seu ponto . Meu comentário original observou que não há necessidade de a função que você escreveu retornar nada, porque ela sempre será o número 1, independentemente da entrada. Portanto, uma implementação melhor evitaria o retorno de qualquer valor, para evitar a sugestão de que ele seja significativo. Por exemplo, eu diria que uma implementação melhor (da parte da recursão) é
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Convido você a experimentar o código que escrevi para você para ver que não (pelo menos, para entrada de número inteiro positivo). Se você deseja discutir isso mais profundamente, sugiro que o levemos para um bate-papo.
Deixe uma resposta