Python – La séquence Collatz
On décembre 1, 2020 by adminPuis-je consulter mon code pour la séquence Collatz du chapitre trois des Automatiser les choses ennuyeuses avec Python ?
La séquence Collatz
Ecrivez une fonction nommée collatz () qui a un paramètre nommé nombre. Si nombre est pair, alors collatz () doit afficher le nombre // 2 et renvoyer cette valeur. Si nombre est impair, alors collatz () devrait afficher et renvoyer 3 * nombre + 1.
Ensuite, écrivez un programme qui permet à lutilisateur de taper un entier et qui continue dappeler collatz () sur ce nombre jusquà ce que le La fonction renvoie la valeur 1. (Étonnamment, cette séquence fonctionne en fait pour nimporte quel entier – tôt ou tard, en utilisant cette séquence, vous arriverez à 1! Même les mathématiciens ne savent pas pourquoi. Votre programme explore ce que lon appelle la séquence Collatz , parfois appelé «le problème mathématique le plus simple impossible.»)
Noubliez pas de convertir la valeur de retour de input () en un entier avec la fonction int (); sinon, ce sera une valeur de chaîne.
Astuce: Un nombre entier est pair si le nombre% 2 == 0, et il est impair si le nombre% 2 == 1.
La sortie de ce programme pourrait ressembler à ceci:
Enter number: 3 10 5 16 8 4 2 1
Validation dentrée
Ajoutez des instructions try and except au projet précédent pour détecter si lutilisateur tape une chaîne non entière. Normalement, la fonction int () lèvera une erreur ValueError si elle est passée une chaîne non entière, comme dans int (« puppy »). Dans la clause except, affichez un message à lutilisateur indiquant quil doit saisir un entier.
Je me demande surtout sil existe une manière plus propre décrire mon solution.
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)
Commentaires
- Veuillez ajouter une description indiquant ce que cette chose du chapitre 3 recommande avec des exemples dentrée et de sortie, personne ‘ ne va deviner ce que cest
- @Edmad Broctor et @ Carcigenicate Merci pour vos commentaires. Jai continué et révisé mon message.
- » Étonnamment, cette séquence fonctionne en fait pour nimporte quel entier – tôt ou tard, en utilisant cette séquence, vous arriverez à 1! » En fait, nous ne savons ‘ que cela fonctionne pour nimporte quel entier, lidée quil atteindra toujours 1 est une conjecture .
- @PierreCath é Jai une démonstration vraiment merveilleuse de cette proposition que cette zone de commentaire est trop petite pour contenir.
- @Acccumulation Wow ! Cela ‘ est impressionnant, dans ce cas, vous devez mettre à jour larticle de Wikipédia et contacter lUnion mathématique internationale pour récupérer votre médaille Fields.
Réponse
Tout dabord, notez comment vous « dupliquez des calculs:
print(num//2) num = num //2
Cela peut pas de problèmes avec ce code spécifique, mais ce nest pas une bonne pratique. Vous faites deux fois plus de travail que nécessaire, ce qui peut entraîner des problèmes de performances une fois que vous commencez à écrire du code plus compliqué. Faites le calcul une fois et enregistrez le résultat. Dans ce cas cependant, il vous suffit dinverser ces lignes. et utilisez num
:
num = num // 2 print(num)
Assurez-vous également davoir un espacement approprié autour des opérateurs et soyez cohérent .
Vos if
et elif
les cas sont exclusifs les uns des autres et votre else
ne devrait jamais se produire. Si la première condition est vraie, alors lautre doit être fausse et vice-versa. deuxième chèque. Une fois réécrit, vous verrez que limpression dans tous les cas nest pas nécessaire. Vous pouvez simplement imprimer après:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Puisque vous « venez de réutiliser num
lune des deux options basées sur un condition, une expression conditionnelle peut également être utilisée ici proprement:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Les accolades ne sont pas nécessaires, mais je pense quelles « sont utiles ici en raison du nombre d’opérateurs impliqués.
L’impression des nombres n’est pas idéale ici. Dans la plupart des codes, vous devez pouvoir utiliser les données que vous produisez. Si vous vouliez analyser la séquence produite, vous auriez à faire quelque chose dintercepter la sortie standard, ce qui est coûteux et trop compliqué. Faites-en une fonction qui accumule et renvoie une liste.Dans les exemples suivants, jai également ajouté quelques indices de type pour clarifier le type de données:
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 bien, une approche beaucoup plus propre consiste à en faire un générateur qui donne les nombres :
# 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]
Il y a » quelques choses notables à propos de getNum
:
Python utilise « snake_case » , pas « camelCase ».
Votre utilisation de global num
ici est inutile et déroutante . Comme précédemment, return
explicitement toutes les données que la fonction produit:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Notez comment au lieu de réattribuer un global num
, nous renvoyons simplement le nombre. Jai également espacé un peu les choses et utilisé des noms plus appropriés. Conceptuellement, je dirais que num = input("> ")
est faux. Au moment où cela sexécute, num
ne le fait pas contient un nombre (il contient une chaîne).
Ce nest pas une bonne utilisation de la récursivité. Cela ne vous posera probablement aucun problème, mais si votre utilisateur est vraiment stupide et entre des données erronées ~ 1000 fois, votre programme plantera. Utilisez simplement une boucle:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
Dans des langages comme Python, soyez prudent en utilisant la récursivité dans les cas où vous navez aucune garantie sur le nombre de fois où la fonction va se répéter.
Je « d aussi nommez probablement cela quelque chose de plus proche de ask_for_num
. « get » nindique pas très clairement la provenance des données.
Dans lensemble, vous vous retrouverez avec:
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")
Qui peut être utilisé comme:
num = ask_for_num() for n in collatz_gen(num): print(n)
Answer
Invite
La mauvaise pratique la plus évidente ici est lutilisation dune variable globale. Au lieu de définir num
comme effet secondaire, votre fonction doit return
le résultat.
getNum()
nest pas un si bon nom pour la fonction. PEP 8 , le guide de style officiel pour Python, indique que les noms de fonctions doivent être lower_case_with_underscores
. De plus, « get » implique que la fonction récupère une donnée qui est déjà stockée quelque part, ce qui nest pas le cas ici. Enfin, « Num » devrait être plus spécifique.
Lutilisation de la récursivité nest pas appropriée. Si vous voulez une boucle, écrivez une boucle.
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
function
Strictement en parlant, vous n’avez pas suivi les instructions. Votre solution n’est ni fausse ni mauvaise – vous n’avez simplement pas implémenté la fonction collatz
conformément à la spécification qui a été donnée, qui dit que vous devez imprimer et renvoyer un seul nombre.
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)
Commentaires
- Peut-être inverser le test et éliminer la comparaison avec 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Honnêtement, je trouve que cela brouille lintention. Nous ‘ re ne pas vérifier si
num % 2
est faux; ce ‘ nest pas un prédicat. Nous ‘ re vérifier si ‘ est égal à 0. Il se trouve quils ‘ sont équivalents en Python. -
next = ...
éclipse lenext
. Dans ce contexte, ‘ nest probablement pas dangereux, mais il est simplement bon den être conscient - Bien que techniquement, vous puissiez utiliser
if num % 2 else
, je suis daccord quil est plus important de montrer lintention ici. etif num % 2 == 0 else
communique mieux cette intention. Je suis daccord que vous devriez renommer votre variablenext
, cependant. Cest ‘ une mauvaise habitude. - @ 200_success Merci davoir signalé mon échec de renvoyer le numéro, pas simplement de limprimer. Cela a été très utile.
Réponse
Par souci dexhaustivité, une implémentation récursive pour collatz
(vous avez déjà assez de bonnes suggestions pour saisir 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)
Sorties
3 10 5 16 8 4 2 1
Commentaires
- Je ne ‘ t pense que cette réponse aide OP. Bien quune définition récursive soit certainement possible, elle ne correspond ‘ pas à la spécification quils essaient de respecter. De plus, ce code a une certaine redondance: la fonction renvoie toujours 1 (quand elle renvoie du tout), alors pourquoi même avoir une valeur de retour?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
Je ‘ ne suis pas sûr de suivre. La récursivité doit avoir un cas de base. Il se trouve que le cas de base dans cet exemple est ifnum == 1
. Lexemple de sortie montre que la fonction ne renvoie pas toujours 1. - La fonction imprime dautres valeurs, mais elle renvoie uniquement la valeur 1.
- Je ne suis pas sûr de votre argument . Mon commentaire initial a noté quil nest pas nécessaire que la fonction que vous avez écrite renvoie quoi que ce soit, car ce sera toujours le numéro 1, quelle que soit lentrée. Par conséquent, une meilleure implémentation éviterait de renvoyer une valeur, pour éviter de suggérer quelle est significative. Par exemple, je dirais quune meilleure implémentation (de la partie récursivité) est
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Je vous invite à essayer le code que jai écrit pour vous-même pour voir cela ce nest pas le cas (du moins pour une entrée entière positive). Si vous souhaitez en discuter davantage, je vous suggère de participer au chat.
Laisser un commentaire