Python – Sekvence Collatz
On 1 prosince, 2020 by adminMohu získat kontrolu mého kódu pro Collatzovu sekvenci z kapitoly tři z automatizace nudných věcí pomocí Pythonu ?
Sekvence Collatz
Napište funkci s názvem collatz (), která má jeden parametr s názvem number. Pokud je číslo sudé, měl by collatz () vytisknout číslo // 2 a vrátit tuto hodnotu. Pokud je číslo liché, pak by collatz () měl vytisknout a vrátit 3 * číslo + 1.
Potom napište program, který uživateli umožní zadat celé číslo a který bude na tomto čísle volat collatz (), dokud funkce vrací hodnotu 1. (Je překvapivé, že tato posloupnost skutečně funguje pro jakékoli celé číslo – dříve nebo později pomocí této posloupnosti dosáhnete hodnoty 1! I matematici si nejsou jisti proč. Váš program zkoumá to, co se nazývá posloupnost Collatz , někdy nazývaný „nejjednodušší nemožný matematický problém.“)
Nezapomeňte převést návratovou hodnotu z input () na celé číslo pomocí funkce int (); jinak to bude hodnota řetězce.
Tip: Celé číslo je sudé, i když číslo% 2 == 0, a je liché, pokud číslo% 2 == 1.
Výstup tohoto programu může vypadat asi takto:
Enter number: 3 10 5 16 8 4 2 1
Ověření vstupu
Do předchozího projektu přidejte příkazy try a except, abyste zjistili, zda uživatel zadává řetězec bez čísla. Normálně funkce int () vyvolá chybu ValueError, pokud je předán neintegrovaný řetězec, jako v int („štěně“). V klauzuli výjimky vytiskněte uživateli zprávu, že musí zadat celé číslo.
Zajímalo by mě, jestli existuje čistší způsob psaní mého řešení.
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)
Komentáře
- Přidejte nějaký popis označující, co tato věc v kapitole 3 doporučuje, s příklady vstupu a výstupu, nikdo ‚ nebude hádat, o co jde
- @Edmad Broctor a @ Carcigenicate Děkujeme za vaši zpětnou vazbu. Pokračoval jsem a upravil svůj příspěvek.
- “ Kupodivu tato posloupnost ve skutečnosti funguje pro jakékoli celé číslo – dříve nebo později pomocí této posloupnosti dorazíte v 1! “ Vlastně ‚ nevíme, že to funguje na celé číslo, myšlenka, že vždy dosáhne 1, je domněnka .
- @PierreCath é Mám opravdu úžasnou ukázku tohoto návrhu, který je tento rámeček s komentářem příliš malý na to, aby jej obsahoval.
- @Acccumulation Páni ! To je ‚ působivé, v takovém případě byste měli aktualizovat článek na Wikipedii a kontaktovat Mezinárodní matematickou unii, aby vám vyzvedla Fieldsovu medaili.
Odpověď
Nejprve si povšimněte, jak duplikujete výpočty:
print(num//2) num = num //2
To může nezpůsobovat problémy s tímto konkrétním kódem, ale není to dobrá praxe. Děláte dvakrát tolik práce, kolik potřebujete, což může způsobit problémy s výkonem, jakmile začnete psát složitější kód. Proveďte výpočet jednou a uložte výsledek. V tomto případě však vše, co musíte udělat, je obrátit tyto řádky a použijte num
:
num = num // 2 print(num)
Rovněž se ujistěte, že máte řádné mezery kolem operátorů a být konzistentní .
Vaše if
a elif
případy se navzájem vylučují a vaše else
by se nikdy nemělo stát. Je-li první podmínka pravdivá, ostatní musí být nepravdivé a naopak. Není třeba druhá kontrola. Po přepsání uvidíte, že tisk v každém případě není nutný. Můžete jen tisknout po:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Protože jen znovu potvrzujete num
jednu ze dvou možností založených na podmínka, lze zde čistě použít i podmíněný výraz:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Složené závorky nejsou nutné, ale myslím si, že jsou zde užitečné kvůli počet zúčastněných operátorů.
Tisk čísel zde není ideální. Ve většině kódu musíte být schopni použít data, která vytváříte. Pokud byste chtěli analyzovat produkovanou sekvenci, museli byste udělat něco, co by zachytilo standardní výstup, což je drahé a příliš komplikované. Udělejte z něj funkci, která hromadí a vrátí seznam.V následujících příkladech jsem také přidal několik tipů typu , aby bylo jasnější, o jaký typ dat jde:
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
Nebo mnohem čistším přístupem je vytvořit z něj generátor, který získá čísla :
# 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]
O getNum
je několik pozoruhodných věcí:
Python používá „snake_case“ , nikoli „camelCase“.
Vaše použití global num
je zde zbytečné a matoucí . Stejně jako dříve, výslovně return
jakákoli data, která funkce produkuje:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Všimněte si, jak místo opětovného přiřazení global num
, číslo právě vracíme. Také jsem trochu rozložil věci a použil několik vhodnějších jmen. Koncepčně bych řekl, že num = input("> ")
je špatný. V době, kdy to běží, num
ne obsahovat číslo (obsahuje řetězec).
Rekurze se dobře nepoužívá. Pravděpodobně vám to nebude dělat žádné problémy, ale pokud je váš uživatel opravdu hloupý a zadá nesprávná data ~ 1000krát, váš program se zhroutí. Použijte smyčku:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
V jazycích, jako je Python, buďte opatrní při použití rekurze v případech, kdy nemáte žádné záruky, kolikrát se funkce bude opakovat.
Já také pravděpodobně pojmenujete něco blíže k ask_for_num
. „get“ nedává jasně najevo, odkud data pocházejí.
Celkově vzato skončíte s:
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")
Které lze použít jako:
num = ask_for_num() for n in collatz_gen(num): print(n)
Odpověď
Výzva
Nejviditelnějším špatným postupem je použití globální proměnné. Místo nastavení num
jako vedlejšího efektu by měla vaše funkce return
výsledek.
není pro funkci tak dobrý název. PEP 8 , oficiální průvodce stylem pro Python, říká, že názvy funkcí by měly být lower_case_with_underscores
. „Get“ dále znamená, že funkce načítá část dat, která jsou již někde uložena, což zde není tento případ. Nakonec by „Num“ mělo být konkrétnější.
Použití rekurze není vhodné. Pokud chcete smyčku, napište smyčku.
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
funkce
Přísně řečeno, neřídili jste se pokyny. Vaše řešení není špatné nebo špatné – právě jste neimplementovali funkci collatz
podle zadané specifikace, která říká, že měli byste vytisknout a vrátit jedno jediné číslo.
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)
Komentáře
- Možná test otočte a odstraňte srovnání s 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Upřímně řečeno, zjišťuji, že to mudly záměr. My ‚ re nekontrolujeme, zda
num % 2
je falsey; ‚ to není predikát. ‚ znovu kontrola, zda je ‚ s rovna 0. Stává se, že ‚ jsou ekvivalentní v Pythonu. -
next = ...
zastíní integrovanýnext
. V této souvislosti to ‚ pravděpodobně není nebezpečné, ale je dobré si uvědomit - Zatímco technicky můžete použít
if num % 2 else
, souhlasím s tím, že je zde důležitější ukázat záměr. aif num % 2 == 0 else
lépe komunikuje tento záměr. Souhlasím však, že byste měli proměnnounext
přejmenovat. Je to ‚ špatný zvyk. - @ 200_success Děkujeme, že jste upozornil na to, že jsem nevrátil číslo, ale jednoduše jej vytisknout. To bylo velmi užitečné.
Odpověď
Pro úplnost rekurzivní implementace pro collatz
(pro zadávání num
) již máte dost dobrých návrhů:
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)
Výstupy
3 10 5 16 8 4 2 1
Komentáře
- Ne ‚ t myslím, že tato odpověď pomáhá OP. Ačkoli je určitě možná rekurzivní definice, neodpovídá specifikaci, které se snaží splnit, ‚. Kromě toho má tento kód určitou redundanci: funkce vždy vrátí 1 (když se vrátí vůbec), tak proč dokonce mít návratovou hodnotu?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
‚ si nejsem jistý, zda sleduji. Rekurze musí mít základní případ. Stává se, že základní případ v tomto příkladu je ifnum == 1
. Ukázkový výstup ukazuje, že funkce ne vždy vrátí 1. - Funkce tiskne další hodnoty, ale vrací pouze hodnotu 1.
- Nejsem si jistý, o co jde . Můj původní komentář poznamenal, že není nutné, aby funkce, kterou jste napsali, vrátila cokoli, protože to bude vždy číslo 1, bez ohledu na vstup. Lepší implementace by tedy zabránila návratu jakékoli hodnoty, aby se zabránilo náznaku její smysluplnosti. Například bych řekl, že lepší implementací (části rekurze) je
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Vyzývám vás, abyste vyzkoušeli kód, který jsem sám napsal není (alespoň pro kladný celočíselný vstup). Chcete-li o tom dále diskutovat, navrhuji, abychom si to vzali na chat.
Napsat komentář