Python – De Collatz-reeks
Geplaatst op december 1, 2020 door adminKan ik een recensie krijgen van mijn code voor de Collatz-reeks uit hoofdstuk drie van Automatiseer de saaie dingen met Python ?
De Collatz-reeks
Schrijf een functie genaamd collatz () met één parameter met de naam number. Als getal even is, dan moet collatz () getal // 2 afdrukken en deze waarde retourneren. Als het getal oneven is, dan zou collatz () moeten afdrukken en 3 * getal + 1 teruggeven.
Schrijf vervolgens een programma waarmee de gebruiker een geheel getal kan typen en dat collatz () op dat nummer blijft aanroepen tot de functie geeft de waarde 1 terug. (Verbazingwekkend genoeg werkt deze reeks eigenlijk voor elk geheel getal. Vroeg of laat, als je deze reeks gebruikt, kom je uit op 1! Zelfs wiskundigen weten niet precies waarom. Je programma onderzoekt wat de Collatz-reeks wordt genoemd , ook wel “het eenvoudigste onmogelijke rekenprobleem” genoemd.)
Vergeet niet om de geretourneerde waarde van input () om te zetten naar een geheel getal met de functie int (); anders is het een tekenreekswaarde.
Hint: een geheel getal is zelfs als getal% 2 == 0, en het is oneven als getal% 2 == 1.
De output van dit programma zou er ongeveer zo uit kunnen zien:
Enter number: 3 10 5 16 8 4 2 1
Invoervalidatie
Voeg try-and-behalve-instructies toe aan het vorige project om te detecteren of de gebruiker een niet-geheel getal invoert. Normaal gesproken zal de functie int () een ValueError-fout veroorzaken als er een niet-geheel getal wordt doorgegeven, zoals in int (“puppy”). Druk in de except-clausule een bericht af naar de gebruiker waarin staat dat hij een geheel getal moet invoeren.
Ik vraag me voornamelijk af of er een schonere manier is om mijn oplossing.
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)
Reacties
- Voeg alsjeblieft een beschrijving toe die aangeeft wat dit hoofdstuk 3-ding aanbeveelt met voorbeelden van invoer en uitvoer, niemand ‘ zal raden wat dat is
- @Edmad Broctor en @ Carcigenicate Bedankt voor uw feedback. Ik ging door en herzag mijn bericht.
- ” Verbazingwekkend genoeg werkt deze reeks eigenlijk voor elk geheel getal – vroeg of laat, als je deze reeks gebruikt, kom je aan at 1! ” Eigenlijk weten we niet ‘ dat het werkt voor elk geheel getal, het idee dat het altijd 1 zal bereiken is een vermoeden .
- @PierreCath é Ik heb een werkelijk geweldige demonstratie van dit voorstel, waarvoor dit commentaarvak te klein is.
- @Acccumulation Wauw ! Dat ‘ is indrukwekkend, in dat geval moet u het Wikipedia-artikel bijwerken en contact opnemen met de International Mathematical Union om uw Fields-medaille te verzamelen.
Answer
Merk eerst op hoe u “berekeningen dupliceert:
print(num//2) num = num //2
Dit kan veroorzaakt geen problemen met deze specifieke code, maar het is geen goede gewoonte. U doet twee keer zoveel werk als nodig is, wat prestatieproblemen kan veroorzaken als u eenmaal begint met het schrijven van meer gecompliceerde code. Voer de berekening één keer uit en sla het resultaat op. In dit geval hoeft u alleen die regels om te draaien en gebruik num
:
num = num // 2 print(num)
Zorg er ook voor dat je de juiste spatiëring hebt rond operatoren en wees consistent .
Uw if
en elif
gevallen zijn exclusief elkaar, en uw else
mag nooit voorkomen. Als de eerste voorwaarde waar is, dan moet de andere onwaar zijn en vice versa. Er is geen noodzaak voor de tweede controle. Eenmaal herschreven, zult u zien dat afdrukken in alle gevallen niet nodig is. U kunt gewoon afdrukken na:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Aangezien u “slechts num
een van de twee opties op basis van een voorwaarde, kan een voorwaardelijke uitdrukking hier ook netjes worden gebruikt:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
De accolades zijn niet nodig, maar ik denk dat ze hier nuttig zijn vanwege de aantal betrokken operators.
Het afdrukken van de nummers is hier niet ideaal. In de meeste code moet u de gegevens die u produceert gebruiken . Als je de geproduceerde reeks zou willen analyseren, zou je iets moeten doen om de stdout te onderscheppen, wat duur en te ingewikkeld is. Maak er een functie van die accumuleert en een lijst retourneert.In de volgende voorbeelden heb ik ook wat hints van het type type toegevoegd om het duidelijker te maken wat het type gegevens is:
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
Of een veel schonere benadering is om er een generator van te maken die de getallen oplevert :
# 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]
Er” zijn een paar opmerkelijke dingen over getNum
:
Python gebruikt “snake_case” , niet “camelCase”.
Uw gebruik van global num
is hier niet nodig en verwarrend . Net als voorheen, expliciet return
alle gegevens die de functie produceert:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Merk op hoe in plaats van een global num
, we geven alleen het nummer terug. Ik heb ook de dingen een beetje uit elkaar gehaald en wat meer toepasselijke namen gebruikt. Conceptueel zou ik “zeggen dat num = input("> ")
verkeerd is. Op het moment dat dat wordt uitgevoerd, num
niet bevatten een nummer (het bevat een string).
Dit is geen goed gebruik van recursie. Het zal waarschijnlijk je geen problemen bezorgen, maar als je gebruiker echt dom is en ~ 1000 keer verkeerde gegevens invoert, zal je programma crashen. Gebruik gewoon een lus:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
In talen zoals Python, wees voorzichtig met het gebruik van recursie in gevallen waarin u geen garanties heeft over hoe vaak de functie zal terugkeren.
Ik zou ook noem dit waarschijnlijk iets dat dichter bij ask_for_num
ligt. “get” maakt niet erg duidelijk waar de gegevens vandaan komen.
Alles bij elkaar genomen, “kom je uit op:
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")
Wat kan worden gebruikt als:
num = ask_for_num() for n in collatz_gen(num): print(n)
Antwoord
Prompt
De meest voor de hand liggende slechte praktijk hier is het gebruik van een globale variabele. In plaats van num
als neveneffect in te stellen, zou uw functie return
het resultaat moeten zijn.
getNum()
is niet zon goede naam voor de functie. PEP 8 , de officiële stijlgids voor Python, zegt dat functienamen lower_case_with_underscores
moeten zijn. Verder houdt “get” in dat de functie een stukje data ophaalt dat al ergens is opgeslagen, wat hier niet het geval is. Ten slotte zou “Num” specifieker moeten zijn.
Het gebruik van recursie is niet gepast. Als je een lus wilt, schrijf dan een lus.
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
functie
Strikt gesproken, je hebt de instructies niet gevolgd. Je oplossing is niet verkeerd of slecht – je hebt de functie collatz
niet geïmplementeerd volgens de opgegeven specificatie, die zegt dat je moet een enkel nummer afdrukken en retourneren.
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)
Reacties
- Misschien de test omkeren en elimineren de vergelijking met 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Eerlijk gezegd merk ik dat dat de bedoeling vertroebelt. We ‘ re niet controleren of
num % 2
falsey is; het ‘ is geen predikaat. We ‘ zijn opnieuw controleren of het ‘ s gelijk is aan 0. Het gebeurt zo dat ze ‘ gelijkwaardig zijn in Python. -
next = ...
overschaduwt de ingebouwdenext
. In deze context is het ‘ waarschijnlijk niet gevaarlijk, maar gewoon goed om op te letten - Hoewel je technisch gezien
if num % 2 else
, ik ben het ermee eens dat het tonen van de intentie hier belangrijker is. enif num % 2 == 0 else
communiceert die intentie beter. Ik ben het ermee eens dat je de naam van jenext
variabele moet wijzigen. Het ‘ is een slechte gewoonte. - @ 200_success Bedankt dat je erop hebt gewezen dat ik het nummer niet heb teruggestuurd, niet gewoon afdrukken. Dat was erg nuttig.
Antwoord
Voor de volledigheid, een recursieve implementatie voor collatz
(je hebt al genoeg goede suggesties voor het invoeren van 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)
Uitvoer
3 10 5 16 8 4 2 1
Reacties
- Ik heb geen ‘ t denk dat dit antwoord OP helpt. Hoewel een recursieve definitie zeker mogelijk is, past deze niet ‘ niet aan de specificatie waaraan ze proberen te voldoen. Bovendien heeft deze code enige redundantie: de functie retourneert altijd 1 (als hij überhaupt terugkeert), dus waarom zelfs een retourwaarde?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
Ik ‘ weet niet zeker of ik het volg. De recursie moet een basisscenario hebben. Toevallig is het basisscenario in dit voorbeeld ifnum == 1
. De voorbeelduitvoer laat zien dat de functie niet altijd 1 retourneert. - De functie drukt andere waarden af, maar retourneert alleen de waarde 1.
- Ik weet niet zeker wat je punt is . Mijn oorspronkelijke opmerking merkte op dat de functie die je hebt geschreven niet nodig is om iets terug te sturen, omdat het altijd het nummer 1 zal zijn, ongeacht de invoer. Daarom zou een betere implementatie helemaal geen waarde teruggeven, om de suggestie te vermijden dat het zinvol is. Ik zou bijvoorbeeld zeggen dat een betere implementatie (van het recursiegedeelte)
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
is. - Ik nodig je uit om de code die ik heb geschreven zelf uit te proberen om dat te zien het niet (althans, voor positieve integer-invoer). Als je dit verder wilt bespreken, raad ik je aan om ermee te chatten.
Geef een reactie