Python – Collatz-sekvensen
On december 1, 2020 by adminKan jeg få en gennemgang af min kode til Collatz-sekvensen fra kapitel tre af Automatiser de kedelige ting med Python ?
Collatz-sekvensen
Skriv en funktion ved navn collatz (), der har en parameter med navnet nummer. Hvis tallet er lige, skal collatz () udskrive nummer // 2 og returnere denne værdi. Hvis antallet er ulige, skal collatz () udskrive og returnere 3 * nummer + 1.
Skriv derefter et program, der lader brugeren skrive et heltal, og som fortsætter med at kalde collatz () på det nummer, indtil funktion returnerer værdien 1. (forbavsende nok fungerer denne sekvens faktisk for ethvert heltal – før eller senere, ved hjælp af denne sekvens, når du frem til 1! Selv matematikere er ikke sikre på hvorfor. Dit program udforsker hvad der hedder Collatz-sekvensen. , undertiden kaldet “det enkleste umulige matematiske problem.”)
Husk at konvertere returværdien fra input () til et heltal med funktionen int (); ellers er det en strengværdi.
Tip: Et heltal er lige, hvis tallet% 2 == 0, og det er ulige, hvis tallet% 2 == 1.
Programmets output kan se sådan ud:
Enter number: 3 10 5 16 8 4 2 1
Inputvalidering
Tilføj forsøg undtagen udsagn til det forrige projekt for at registrere, om brugeren skriver en ikke-strengstreng. Normalt vil funktionen int () hæve en ValueError-fejl, hvis den sendes til en ikke-ikke-strengstreng, som i int (“hvalp”). I undtagelsesklausulen skal du udskrive en besked til brugeren om, at de skal indtaste et heltal.
Jeg spekulerer hovedsagelig på, om der er en renere måde at 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
- Tilføj venligst en beskrivelse, der angiver, hvad dette kapitel 3 anbefales, med eksempler på input og output, ingen ‘ vil gætte hvad det er
- @Edmad Broctor og @ Carcigenicate Tak for din feedback. Jeg gik videre og reviderede mit indlæg.
- ” Utroligt nok fungerer denne sekvens faktisk for ethvert heltal – før eller senere, ved at bruge denne sekvens, ankommer du ved 1! ” Faktisk ved vi ikke ‘ at det fungerer for ethvert heltal, ideen om at det altid når 1 er en formodning .
- @PierreCath é Jeg har en virkelig fantastisk demonstration af dette forslag, som denne kommentarfelt er for lille til at indeholde.
- @Acccumulation Wow! At ‘ er imponerende. I så fald skal du opdatere Wikipedia-artiklen og kontakte Den Internationale Matematiske Union for at indsamle din Fields-medalje.
Svar
Bemærk først, hvordan du duplikerer beregninger:
print(num//2) num = num //2
Dette kan ikke forårsage problemer med denne specifikke kode, men det er ikke god praksis. Du laver dobbelt så meget arbejde, som du har brug for, hvilket kan forårsage præstationsproblemer, når du begynder at skrive mere kompliceret kode. Lav beregningen en gang, og gem resultatet. I dette tilfælde er alt, hvad du skal gøre, at vende disse linjer og brug num
:
num = num // 2 print(num)
Sørg også for, at du har den rette afstand omkring operatører, og vær konsekvent .
Din if
og elif
sager er eksklusive af hinanden, og din else
bør aldrig ske. Hvis den første betingelse er sand, skal andre være falske og omvendt. Der er ikke behov for anden kontrol. Når du er omskrevet, vil du se, at udskrivning i alle tilfælde ikke er nødvendig. Du kan bare udskrive efter:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Da du bare er ved at revidere num
en af to muligheder baseret på en betingelse, kan et betinget udtryk også bruges her rent:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Bøjlerne er ikke nødvendige, men jeg tror, de er nyttige her på grund af antal involverede operatører.
Udskrivning af numrene er ikke ideel her. I de fleste koder skal du være i stand til at bruge de data, du producerer. Hvis du ville analysere den producerede sekvens, skulle du gøre noget for at aflytte stdout, hvilket er dyrt og alt for kompliceret. Gør det til en funktion, der akkumuleres og returnerer en liste.I de følgende eksempler tilføjede jeg også nogle tip tip for at gøre det tydeligere, hvad typen af 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 meget renere tilgang er at gøre det til en generator, der giver 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]
Der er nogle få bemærkelsesværdige ting om getNum
:
Python bruger “snake_case” , ikke “camelCase”.
Din brug af global num
her er unødvendig og forvirrende Ligesom før udtrykkeligt return
alle data, som funktionen producerer:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Bemærk hvordan i stedet for at omfordele en global num
, vi returnerer netop nummeret. Jeg adskilte også tingene lidt og brugte nogle mere passende navne. Konceptuelt ville jeg sige, at num = input("> ")
er forkert. På det tidspunkt, det kører, num
ikke indeholder et tal (det indeholder en streng).
Dette er ikke en god brug af rekursion. Det sandsynligt vil ikke give dig nogen problemer, men hvis din bruger er virkelig dum og indtaster forkerte data ~ 1000 gange, går dit program ned. Brug bare en loop:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
På sprog som Python skal du være forsigtig med at bruge rekursion i tilfælde, hvor du ikke har nogen garanti for, hvor mange gange funktionen genoprettes.
Jeg ville også have nævn sandsynligvis dette noget tættere på ask_for_num
. “get” gør det ikke meget klart, hvor dataene kommer fra.
Alt i alt ender du 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")
Hvilket kan bruges som:
num = ask_for_num() for n in collatz_gen(num): print(n)
Svar
Spørg
Den mest åbenlyse dårlige praksis her er brugen af en global variabel. I stedet for at indstille num
som en bivirkning, skal din funktion return
resultatet.
getNum()
er ikke et så godt navn for funktionen. PEP 8 , den officielle stilguide for Python, siger, at funktionsnavne skal være lower_case_with_underscores
. Desuden betyder “get”, at funktionen henter et stykke data, der allerede er gemt et eller andet sted, hvilket ikke er tilfældet her. Endelig skal “Num” være mere specifikt.
Brug af rekursion er ikke passende. Hvis du vil have en løkke, skal du skrive en løkke.
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
-funktion
Strengt Når du taler, fulgte du ikke instruktionerne. Din løsning er ikke forkert eller dårlig – du implementerede bare ikke collatz
-funktionen i henhold til specifikationen, der blev givet, som siger at du skal udskrive og returnere et 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
- Måske vende testen om og fjerne sammenligningen med 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Helt ærligt finder jeg, at det mudrer hensigten. Vi ‘ kontrollerer ikke, om
num % 2
er falsk; det ‘ er ikke et prædikat. Vi ‘ re kontrollere, om det ‘ er lig med 0. Det sker bare, at de ‘ er ækvivalente i Python. -
next = ...
overskygger den indbyggedenext
. I denne sammenhæng er det ‘ sandsynligvis ikke farligt, men bare godt at være opmærksom på - Mens du teknisk set kan bruge
if num % 2 else
, jeg er enig i, at det er vigtigere at vise hensigten her. ogif num % 2 == 0 else
kommunikerer denne hensigt bedre. Jeg er enig i, at du dog skal omdøbe dinnext
-variabel. Det ‘ er en dårlig vane. - @ 200_succes Tak fordi du påpegede, at jeg ikke returnerede nummeret, ikke bare udskrive det. Det var meget nyttigt.
Svar
For fuldstændighedens skyld er en rekursiv implementering af collatz
(du har allerede fået gode gode forslag til indtastning af 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)
Outputs
3 10 5 16 8 4 2 1
Kommentarer
- Jeg don ‘ t tror dette svar hjælper OP. Selvom en rekursiv definition helt sikkert er mulig, passer den ikke ‘ til den specifikation, de prøver at opfylde. Desuden har denne kode en vis redundans: funktionen returnerer altid 1 (når den overhovedet vender tilbage), så hvorfor endda have en returværdi?
- @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. Rekursionen skal have en basissag. Det sker bare, at basissagen i dette eksempel er, hvisnum == 1
. Eksemplet på output viser, at funktionen ikke altid returnerer 1. - Funktionen udskriver andre værdier, men den returnerer kun værdien 1.
- Jeg er ikke sikker på, hvad dit pointe er . Min oprindelige kommentar bemærkede, at der ikke er behov for, at den funktion, du skrev, skulle returnere noget, fordi det altid vil være nummeret 1, uanset input. Derfor vil en bedre implementering undgå at returnere nogen værdi overhovedet for at undgå antydningen om, at den er meningsfuld. F.eks. Vil jeg sige, at en bedre implementering (af rekursionsdelen) er
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Jeg inviterer dig til at prøve den kode, jeg skrev ud for dig selv for at se, at det gør det ikke (i det mindste for positivt heltal input). Hvis du ønsker at diskutere dette yderligere, foreslår jeg, at vi tager det til chat.
Skriv et svar