Python – Secvența Collatz
On decembrie 1, 2020 by adminPot primi o recenzie a codului meu pentru secțiunea Collatz din capitol trei dintre Automatizează lucrurile plictisitoare cu Python ?
Secvența Collatz
Scrieți o funcție numită collatz () care are un număr numit parametru. Dacă numărul este par, atunci collatz () ar trebui să tipărească numărul // 2 și să returneze această valoare. Dacă numărul este impar, atunci collatz () ar trebui să imprime și să returneze 3 * număr + 1.
Apoi scrieți un program care permite utilizatorului să introducă un număr întreg și care continuă să apeleze collatz () pe acel număr până la funcția returnează valoarea 1. (Destul de uimitor, această secvență funcționează de fapt pentru orice număr întreg – mai devreme sau mai târziu, folosind această secvență, veți ajunge la 1! Chiar și matematicienii nu sunt siguri de ce. Programul dvs. explorează ceea ce se numește secvența Collatz , uneori numită „cea mai simplă problemă matematică imposibilă.”)
Amintiți-vă să convertiți valoarea returnată de la input () într-un număr întreg cu funcția int (); în caz contrar, va fi o valoare șir.
Sugestie: un număr întreg este chiar dacă numărul% 2 == 0 și este impar dacă numărul% 2 == 1.
Rezultatul acestui program ar putea arăta cam așa:
Enter number: 3 10 5 16 8 4 2 1
Validare intrare
Adăugați instrucțiuni try și exceptie la proiectul anterior pentru a detecta dacă utilizatorul tastează într-un șir care nu este întreg. În mod normal, funcția int () va ridica o eroare ValueError dacă i se transmite un șir care nu este întreg, ca în int („cățeluș”). În clauza exceptare, tipăriți un mesaj către utilizator spunând că trebuie să introducă un număr întreg.
În principal mă întreb dacă există o modalitate mai curată de a scrie 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)
Comentarii
- Vă rugăm să adăugați o descriere care să indice ce recomandă acest lucru din capitolul 3, cu exemple de intrare și ieșire, nimeni ‘ nu va ghici ce este asta
- @Edmad Broctor și @ Carcigenicate Vă mulțumim pentru feedback. Am continuat și mi-am revizuit postarea.
- ” Destul de uimitor, această secvență funcționează de fapt pentru orice număr întreg – mai devreme sau mai târziu, folosind această secvență, veți ajunge la 1! ” De fapt, nu ‘ nu știm că funcționează pentru orice număr întreg, ideea că va ajunge întotdeauna la 1 este o presupunere .
- @PierreCath é Am o demonstrație cu adevărat minunată a acestei propuneri pe care această casetă de comentarii este prea mică pentru a o conține.
- @Accumulation Wow ! Acest ‘ este impresionant, în acest caz ar trebui să actualizați articolul Wikipedia și să contactați Uniunea Matematică Internațională pentru a vă colecta Medalia Fields.
Răspuns
Mai întâi, rețineți cum copiați calculele:
print(num//2) num = num //2
Acest lucru poate nu cauzează probleme cu acest cod specific, dar nu este o bună practică. Faceți de două ori mai multă muncă decât trebuie, ceea ce poate cauza probleme de performanță odată ce începeți să scrieți cod mai complicat. Efectuați calculul o dată și salvați rezultatul. Totuși, în acest caz, tot ce trebuie să faceți este să inversați aceste linii și utilizați num
:
num = num // 2 print(num)
De asemenea, asigurați-vă că aveți spațiu adecvat în jurul operatorilor și fiți consecvenți .
if
și elif
cazurile sunt exclusive una de cealaltă și else
nu ar trebui să se întâmple niciodată. Dacă prima condiție este adevărată, atunci alta trebuie să fie falsă și invers. Nu este nevoie de a doua verificare. Odată rescrise, veți vedea că tipărirea în fiecare caz nu este necesară. Puteți să tipăriți după:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Din moment ce tocmai reevaluați num
una dintre cele două opțiuni bazate pe condiție, o expresie condițională poate fi utilizată și aici curat:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Acoladele nu sunt necesare, dar cred că sunt utile aici datorită numărul de operatori implicați.
Tipărirea numerelor nu este ideală aici. În majoritatea codurilor, trebuie să puteți utiliza datele pe care le produceți. Dacă doriți să analizați secvența produsă, ar trebui să faceți ceva pentru a intercepta stdout-ul, care este scump și excesiv de complicat. Faceți din aceasta o funcție care acumulează și returnează o listă.În exemplele următoare, am adăugat și câteva indicii de tip pentru a clarifica care este tipul datelor:
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
Sau, o abordare mult mai curată este de a-l face un generator care produce numerele :
# 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]
Există” câteva lucruri notabile despre getNum
:
Python folosește „snake_case” , nu „camelCase”.
Utilizarea dvs. de global num
aici este inutilă și confuză . La fel ca înainte, în mod explicit return
orice date pe care funcția le produce:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Rețineți cum, în loc să reatribuiți un global num
, tocmai returnăm numărul. De asemenea, am distanțat puțin lucrurile și am folosit câteva nume mai potrivite. Conceptual, aș spune că num = input("> ")
este greșit. În momentul în care rulează, num
nu conține un număr (conține un șir).
Aceasta nu este o bună utilizare a recursivității. Probabil nu vă va cauza probleme, dar dacă utilizatorul dvs. este foarte prost și introduce date greșite ~ de 1000 de ori, programul dvs. se va prăbuși. Folosiți doar o buclă:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
În limbi cum ar fi Python, aveți grijă să folosiți recursivitatea în cazurile în care nu aveți garanții de câte ori funcția va recurge.
I „d probabil denumiți acest lucru ceva mai apropiat de ask_for_num
. „obțineți” nu face foarte clar despre de unde provin datele.
Luate în totalitate, veți termina cu:
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")
Care poate fi folosit ca:
num = ask_for_num() for n in collatz_gen(num): print(n)
Răspuns
Prompt
Cea mai evidentă practică proastă aici este utilizarea unei variabile globale. În loc să setați num
ca efect secundar, funcția dvs. ar trebui să return
rezultatul.
getNum()
nu este un nume atât de bun pentru funcție. PEP 8 , ghidul oficial de stil pentru Python, spune că numele funcțiilor ar trebui să fie lower_case_with_underscores
. În plus, „obține” implică faptul că funcția recuperează o bucată de date care este deja stocată undeva, ceea ce nu este cazul aici. În cele din urmă, „Num” ar trebui să fie mai specific.
Utilizarea recursivității nu este adecvată. Dacă doriți o buclă, scrieți o buclă.
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
funcție
Strict vorbind, nu ați urmat instrucțiunile. Soluția dvs. nu este „greșită sau rea – tocmai nu ați implementat funcția collatz
în conformitate cu specificația care a fost dată, care spune că ar trebui să imprimați și să returnați un singur număr.
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)
Comentarii
- Poate inversați testul și eliminați comparația cu 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Sincer, constat că asta tulbure intenția. Noi ‘ re nu verificăm dacă
num % 2
este fals; nu ‘ nu este un predicat. Noi ‘ re verificând dacă ‘ este egal cu 0. Se întâmplă ca ‘ să fie echivalent în Python. -
next = ...
umbreștenext
. În acest context, ‘ probabil nu este periculos, dar este bine să fie conștient de - În timp ce din punct de vedere tehnic, puteți utiliza
if num % 2 else
, sunt de acord că arătarea intenției aici este mai importantă. iarif num % 2 == 0 else
comunică mai bine această intenție. Sunt de acord că ar trebui să vă redenumiți variabilanext
. ‘ este un obicei prost. - @ 200_success Vă mulțumim că ați subliniat eșecul meu de a nu returna numărul, nu pur și simplu să îl imprimați. A fost foarte util.
Răspuns
Din motive de completitudine, o implementare recursivă pentru collatz
(aveți deja suficiente sugestii bune pentru introducerea 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)
Ieșiri
3 10 5 16 8 4 2 1
Comentarii
- Nu ‘ t cred că acest răspuns ajută OP. Deși o definiție recursivă este cu siguranță posibilă, ea nu se potrivește cu ‘ specificația pe care încearcă să o îndeplinească. În plus, acest cod are o oarecare redundanță: funcția returnează întotdeauna 1 (când se întoarce deloc), deci de ce să aveți chiar și o valoare returnată?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
Nu ‘ nu sunt sigur că urmez. Recursiunea trebuie să aibă un caz de bază. Se întâmplă ca cazul de bază din acest exemplu să fie dacănum == 1
. Exemplul de ieșire arată că funcția nu returnează întotdeauna 1. - Funcția tipărește alte valori, dar returnează doar valoarea 1.
- Nu sunt sigur care este punctul tău . Comentariul meu original a menționat că nu este nevoie ca funcția pe care ați scris-o să returneze ceva, deoarece va fi întotdeauna numărul 1, indiferent de intrare. Prin urmare, o implementare mai bună ar evita returnarea oricărei valori, pentru a evita sugestia că este semnificativă. De exemplu, aș spune că o implementare mai bună (a părții recursive) este
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Vă invit să încercați codul pe care l-am scris pentru a vedea că nu (cel puțin, pentru intrarea întreagă pozitivă). Dacă doriți să discutați mai departe despre acest lucru, vă sugerăm să-l ducem la chat.
Lasă un răspuns