Python – The Collatz Sequence
On desember 1, 2020 by adminKan jeg få en gjennomgang av koden min for Collatz Sequence fra kapittel tre av Automatiser kjedelige ting med Python ?
Collatz-sekvensen
Skriv en funksjon med navnet collatz () som har en parameter med navnet nummer. Hvis tallet er jevnt, skal collatz () skrive ut nummer // 2 og returnere denne verdien. Hvis tallet er merkelig, skal collatz () skrive ut og returnere 3 * nummer + 1.
Skriv deretter et program som lar brukeren skrive inn et heltall og som fortsetter å ringe collatz () på det nummeret til funksjonen returnerer verdien 1. (Utrolig nok fungerer denne sekvensen faktisk for ethvert heltall – før eller senere, når du bruker denne sekvensen, kommer du til 1! Selv matematikere er ikke sikre på hvorfor. Programmet ditt utforsker det som kalles Collatz-sekvensen. , noen ganger kalt «det enkleste umulige matteproblemet.»)
Husk å konvertere returverdien fra input () til et heltall med int () -funksjonen; Ellers vil det være en strengverdi.
Tips: Et heltall er selv om tallet% 2 == 0, og det er rart om tallet% 2 == 1.
Utgangen fra dette programmet kan se omtrent slik ut:
Enter number: 3 10 5 16 8 4 2 1
Inngangsvalidering
Legg til prøve og unntak-utsagn til forrige prosjekt for å oppdage om brukeren skriver inn en ikke-tallstreng. Normalt vil int () -funksjonen heve en ValueError-feil hvis den sendes en ikke-tallstreng, som i int («valp»). I unntaksparagrafen, skriv ut en melding til brukeren som sier at de må oppgi et heltall.
Jeg lurer hovedsakelig på om det er en renere måte å skrive min løsning.
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)
Kommentarer
- Vennligst legg til en beskrivelse som indikerer hva denne kapittel 3-tingen anbefaler med eksempler på input og output, ingen ‘ kommer til å gjette hva det er
- @Edmad Broctor og @ Carcigenicate Takk for tilbakemeldingen din. Jeg fortsatte og reviderte innlegget mitt.
- » Utrolig nok fungerer denne sekvensen faktisk for ethvert heltall – før eller senere, når du bruker denne sekvensen, kommer du ved 1! » Egentlig vet vi ikke ‘ at det fungerer for ethvert heltall, ideen om at den alltid vil nå 1 er en formodning .
- @PierreCath é Jeg har en virkelig fantastisk demonstrasjon av denne proposisjonen som denne kommentarfeltet er for liten til å inneholde.
- @Acccumulation Wow! At ‘ er imponerende. I så fall bør du oppdatere Wikipedia-artikkelen, og kontakte International Mathematical Union for å samle din Fields-medalje.
Svar
Legg først merke til hvordan du dupliserer beregninger:
print(num//2) num = num //2
Dette kan ikke forårsake problemer med denne spesifikke koden, men det er ikke god praksis. Du gjør dobbelt så mye arbeid som du trenger, noe som kan forårsake ytelsesproblemer når du begynner å skrive mer komplisert kode. Gjør beregningen en gang, og lagre resultatet. I dette tilfellet er alt du trenger å gjøre imidlertid å reversere disse linjene. og bruk num
:
num = num // 2 print(num)
Sørg også for at du har riktig avstand rundt operatører, og vær konsekvent .
Din if
og elif
saker er eksklusive hverandre, og else
skal aldri skje. Hvis den første betingelsen er oppfylt, må andre være falske og omvendt. Det er ikke behov for andre sjekk. Når du er omskrevet, vil du se at utskrift i alle tilfeller ikke er nødvendig. Du kan bare skrive ut etter:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Siden du bare beroliger num
ett av to alternativer basert på en vilkår kan et betinget uttrykk også brukes her:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Bøylene er ikke nødvendige, men jeg tror de er nyttige her pga. antall involverte operatører.
Utskrift av tallene er ikke ideelt her. I de fleste koder må du kunne bruke dataene du produserer. Hvis du ønsket å analysere den produserte sekvensen, måtte du gjøre noe for å avskjære stdout, som er dyrt og altfor komplisert. Gjør det til en funksjon som samler seg og returnerer en liste.I de følgende eksemplene la jeg også til noen -tips for å gjøre det tydeligere hva typen data er:
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
Eller, en mye renere tilnærming er å gjøre det til en generator som gir tallene :
# 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]
Det er noen få bemerkelsesverdige ting om getNum
:
Python bruker «snake_case» , ikke «camelCase».
Din bruk av global num
her er unødvendig og forvirrende . Som tidligere, eksplisitt return
alle data som funksjonen produserer:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Legg merke til hvordan i stedet for å tildele en global num
, returnerer vi bare tallet. Jeg fordelte også litt på ting, og brukte noen mer passende navn. Konseptuelt ville jeg si at num = input("> ")
er feil. På det tidspunktet som kjører, gjør num
ikke inneholder et tall (det inneholder en streng).
Dette er ikke en god bruk av rekursjon. Det vil sannsynligvis ikke føre til problemer, men hvis brukeren din er dum og skriver inn feil data ~ 1000 ganger, vil programmet krasje. Bare bruk en løkke:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
På språk som Python, vær forsiktig med å bruke rekursjon i tilfeller der du ikke har noen garantier for hvor mange ganger funksjonen vil gjenskape.
Jeg ville også ha sannsynligvis kaller dette noe nærmere ask_for_num
. «get» gjør det ikke veldig tydelig om hvor dataene kommer fra.
Alt i alt vil du ende opp med:
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")
Som kan brukes som:
num = ask_for_num() for n in collatz_gen(num): print(n)
Svar
Spør
Den mest åpenbare dårlige fremgangsmåten her er bruken av en global variabel. I stedet for å sette num
som en bivirkning, bør funksjonen din return
resultatet.
getNum()
er ikke et så godt navn for funksjonen. PEP 8 , den offisielle stilveiledningen for Python, sier at funksjonsnavn skal være lower_case_with_underscores
. Videre innebærer «get» at funksjonen henter et stykke data som allerede er lagret et sted, noe som ikke er tilfelle her. Til slutt bør «Num» være mer spesifikk.
Bruk av rekursjon er ikke hensiktsmessig. Hvis du vil ha en sløyfe, skriv en sløyfe.
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
-funksjon
Strengt Når du snakket, fulgte du ikke instruksjonene. Løsningen din er ikke feil eller dårlig – du implementerte bare ikke collatz
-funksjonen i henhold til spesifikasjonen som ble gitt, som sier at du bør skrive ut og returnere ett enkelt nummer.
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)
Kommentarer
- Kanskje snu testen og eliminere sammenligningen med 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Ærlig talt, jeg finner ut at det gjør det meningen. Vi ‘ ikke sjekker om
num % 2
er falsk; det ‘ er ikke et predikat. Vi ‘ er sjekke om det ‘ er lik 0. Det skjer slik at de ‘ tilsvarer i Python. -
next = ...
overskygger den innebygdenext
. I denne sammenhengen er det ‘ sannsynligvis ikke farlig, men bare greit å være klar over - Mens du teknisk sett kan bruke
if num % 2 else
, jeg er enig i at det å vise intensjonen her er viktigere. ogif num % 2 == 0 else
kommuniserer den hensikten bedre. Jeg er enig i at du bør gi nytt navn tilnext
-variabelen. Det ‘ er en dårlig vane. - @ 200_success Takk for at du påpekte at jeg ikke returnerte nummeret, ikke bare skrev det ut. Det var veldig nyttig.
Svar
For fullstendighets skyld, en rekursiv implementering for collatz
(du har allerede nok gode forslag til å legge inn 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)
Utganger
3 10 5 16 8 4 2 1
Kommentarer
- Jeg don ‘ t tror dette svaret hjelper OP. Selv om en rekursiv definisjon absolutt er mulig, passer den ikke ‘ til spesifikasjonen de prøver å oppfylle. Videre har denne koden noe redundans: funksjonen returnerer alltid 1 (når den kommer tilbake i det hele tatt), så hvorfor til og med ha en returverdi?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
Jeg ‘ er ikke sikker på at jeg følger med. Rekursjonen må ha en basissak. Det er bare slik at basissaken i dette eksemplet er hvisnum == 1
. Eksemplet på eksemplet viser at funksjonen ikke alltid returnerer 1. - Funksjonen skriver ut andre verdier, men den returnerer bare verdien 1.
- Jeg er ikke sikker på hva poenget ditt er . Min opprinnelige kommentar bemerket at det ikke er behov for at funksjonen du skrev skal returnere noe, fordi det alltid vil være nummer 1, uavhengig av input. Derfor ville en bedre implementering unngå å returnere noen verdi i det hele tatt, for å unngå antydningen om at den er meningsfull. F.eks. Vil jeg si at en bedre implementering (av rekursjonsdelen) er
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Jeg inviterer deg til å prøve koden jeg skrev ut for deg selv for å se at det gjør det ikke (i det minste for positivt heltallinngang). Hvis du ønsker å diskutere dette videre, foreslår jeg at vi tar det til en prat.
Legg igjen en kommentar