Python – Sekwencja Collatz
On 1 grudnia, 2020 by adminCzy mogę uzyskać przegląd mojego kodu dla sekwencji Collatz z rozdziału trzy z Zautomatyzuj nudne rzeczy w Pythonie ?
Sekwencja Collatz
Napisz funkcję o nazwie collatz (), która ma jeden parametr o nazwie liczba. Jeśli liczba jest parzysta, collatz () powinna wypisać liczbę // 2 i zwrócić tę wartość. Jeśli liczba jest nieparzysta, collatz () powinno wypisać i zwrócić 3 * liczba + 1.
Następnie napisz program, który pozwoli użytkownikowi wpisać liczbę całkowitą i będzie wywoływał collatz () na tej liczbie, aż funkcja zwraca wartość 1. (O dziwo, ta sekwencja faktycznie działa dla każdej liczby całkowitej – prędzej czy później, używając tej sekwencji, dojdziesz do 1! Nawet matematycy nie są pewni, dlaczego. , czasami nazywany „najprostszym niemożliwym problemem matematycznym”).
Pamiętaj, aby przekonwertować wartość zwracaną z input () na liczbę całkowitą za pomocą funkcji int (); w przeciwnym razie będzie to ciąg znaków.
Wskazówka: Liczba całkowita jest parzysta, jeśli liczba% 2 == 0, a nieparzysta, jeśli liczba% 2 == 1.
Wynik tego programu może wyglądać mniej więcej tak:
Enter number: 3 10 5 16 8 4 2 1
Walidacja danych wejściowych
Dodaj instrukcje try i except do poprzedniego projektu, aby wykryć, czy użytkownik wpisze ciąg znaków niezupełnych. Zwykle funkcja int () zgłosi błąd ValueError, jeśli zostanie przekazany ciąg znaków niecałkowity, jak w int („szczeniak”). W klauzuli wyjątkiem wypisz wiadomość do użytkownika, mówiącą, że musi wprowadzić liczbę całkowitą.
Zastanawiam się głównie, czy istnieje bardziej przejrzysty sposób pisania rozwiązanie.
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)
Komentarze
- Dodaj opis wskazujący, co zaleca ten rozdział 3, z przykładami danych wejściowych i wyjściowych, nikt ' nie zgadnie, co to jest
- @Edmad Broctor i @ Carcigenicate Dzięki za Twoją opinię. Poszedłem dalej i poprawiłem swój post.
- ” O dziwo, ta sekwencja faktycznie działa dla każdej liczby całkowitej – prędzej czy później, używając tej sekwencji, dotrzesz na 1! ” Właściwie nie ' nie wiemy, że to działa dla dowolnej liczby całkowitej, pomysł, że zawsze osiągnie 1, jest przypuszczeniem .
- @PierreCath é Mam naprawdę cudowną demonstrację tej propozycji, której to pole komentarza jest zbyt małe, aby je pomieścić.
- @Acccumulation Łał ! To ' imponujące, w takim przypadku należy zaktualizować artykuł w Wikipedii i skontaktować się z Międzynarodową Unią Matematyczną w celu odebrania medalu Fields.
Odpowiedź
Po pierwsze, zwróć uwagę, jak „powtarzasz powielanie obliczeń:
print(num//2) num = num //2
Może nie powoduje problemów z tym konkretnym kodem, ale nie jest to dobra praktyka. Wykonujesz dwa razy więcej pracy, niż potrzebujesz, co może powodować problemy z wydajnością, gdy zaczniesz pisać bardziej skomplikowany kod. Wykonaj obliczenia raz i zapisz wynik. W tym przypadku wszystko, co musisz zrobić, to odwrócić te wiersze i użyj num
:
num = num // 2 print(num)
Upewnij się też, że masz odpowiednie odstępy wokół operatorów i zachowaj spójność .
Twoje if
i elif
przypadki wykluczają się wzajemnie, a Twoje else
nigdy nie powinno się zdarzyć. Jeśli pierwszy warunek jest prawdziwy, to drugi musi być fałszywy i odwrotnie. Nie ma potrzeby druga kontrola. Po przepisaniu zobaczysz, że drukowanie w każdym przypadku nie jest konieczne. Możesz po prostu wydrukować po:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Ponieważ „po prostu zmieniasz num
jedną z dwóch opcji w oparciu o warunku, wyrażenie warunkowe również może być tutaj użyte w prosty sposób:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Nawiasy klamrowe nie są potrzebne, ale myślę, że są tutaj przydatne ze względu na liczba zaangażowanych operatorów.
Drukowanie liczb nie jest tutaj idealne. W większości kodu musisz umieć używać danych, które tworzysz. Jeśli chciałbyś przeanalizować utworzoną sekwencję, musiałbyś coś zrobić, przechwycić standardowe wyjście, co jest kosztowne i zbyt skomplikowane. Niech będzie to funkcja, która gromadzi i zwraca listę.W poniższych przykładach dodałem także wskazówki dotyczące typów , aby wyjaśnić, jaki jest typ danych:
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
Lub znacznie czystszym podejściem jest uczynienie z niego generatora, który podaje liczby :
# 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]
Jest kilka ważnych rzeczy na temat getNum
:
Python używa „snake_case” , a nie „camelCase”.
Korzystanie z global num
tutaj jest niepotrzebne i mylące . Tak jak poprzednio, jawnie return
wszelkie dane generowane przez funkcję:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Zwróć uwagę, że zamiast ponownego przypisywania global num
, zwracamy tylko liczbę. Odłożyłem też trochę rzeczy i użyłem bardziej odpowiednich nazw. Koncepcyjnie powiedziałbym, że num = input("> ")
jest błędny. W tym czasie num
nie zawiera liczbę (zawiera ciąg znaków).
To nie jest dobre użycie rekursji. Prawdopodobnie nie spowoduje to żadnych problemów, ale jeśli Twój użytkownik jest naprawdę głupi i wprowadzi błędne dane ~ 1000 razy, program ulegnie awarii. Po prostu użyj pętli:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
W językach takich jak Python uważaj, używając rekursji w przypadkach, gdy nie masz gwarancji, ile razy funkcja się powtórzy.
Ja też prawdopodobnie nazwij to bliżej ask_for_num
. „get” nie daje jasności, skąd pochodzą dane.
Podsumowując, otrzymasz:
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")
Które mogą być użyte na przykład:
num = ask_for_num() for n in collatz_gen(num): print(n)
Odpowiedź
Podpowiedź
Najbardziej oczywistą złą praktyką jest użycie zmiennej globalnej. Zamiast ustawiać num
jako efekt uboczny, Twoja funkcja powinna return
wynik.
nie jest dobrą nazwą dla tej funkcji. PEP 8 , oficjalny przewodnik po stylach dla Pythona, mówi, że nazwy funkcji powinny być lower_case_with_underscores
. Ponadto „get” oznacza, że funkcja pobiera fragment danych, który jest już gdzieś przechowywany, co nie ma miejsca w tym przypadku. Wreszcie, „Num” powinno być bardziej szczegółowe.
Użycie rekursji nie jest właściwe. Jeśli chcesz pętlę, napisz ją.
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
funkcja
Ściśle mówiąc, nie postępowałeś zgodnie z instrukcjami. Twoje rozwiązanie nie jest złe ani złe – po prostu nie zaimplementowałeś funkcji collatz
zgodnie z podaną specyfikacją, która mówi, że powinieneś wydrukować i zwrócić jedną liczbę.
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)
Komentarze
- Być może odwróć test i wyeliminuj porównanie do 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Szczerze mówiąc uważam, że to utrudnia intencję. ' re brak sprawdzania, czy
num % 2
to błąd; to ' nie jest predykatem. ' re sprawdzenie, czy ' jest równe 0. Tak się składa, że ' są równoważne w Pythonie. -
next = ...
przesłania wbudowanynext
. W tym kontekście ' prawdopodobnie nie jest niebezpieczny, ale po prostu warto o tym wiedzieć - Chociaż technicznie rzecz biorąc, możesz użyć
if num % 2 else
, zgadzam się, że pokazanie intencji jest tutaj ważniejsze. aif num % 2 == 0 else
lepiej wyraża ten zamiar. Zgadzam się jednak, że powinieneś zmienić nazwę swojej zmiennejnext
. To ' to zły nawyk. - @ 200_success Dziękuję za wskazanie, że nie zwróciłem numeru, a nie po prostu go wydrukowałem. To było bardzo pomocne.
Odpowiedź
Ze względu na kompletność rekurencyjna implementacja collatz
(masz już wystarczająco dużo dobrych sugestii dotyczących wprowadzania 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)
Wyniki
3 10 5 16 8 4 2 1
Komentarze
- Nie ' t myślę, że ta odpowiedź pomaga OP. Chociaż definicja rekurencyjna jest z pewnością możliwa, nie ' nie pasuje do specyfikacji, którą próbują spełnić. Ponadto ten kod ma pewną nadmiarowość: funkcja zawsze zwraca 1 (jeśli w ogóle zwraca), więc po co w ogóle zwracać wartość?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
' Nie wiem, czy śledzę. Rekursja musi mieć przypadek bazowy. Tak się składa, że podstawowym przypadkiem w tym przykładzie jestnum == 1
. Przykładowe dane wyjściowe pokazują, że funkcja nie zawsze zwraca 1. - Funkcja drukuje inne wartości, ale zwraca tylko wartość 1.
- Nie jestem pewien, o co ci chodzi . Mój pierwotny komentarz zauważył, że nie ma potrzeby, aby napisana przez Ciebie funkcja zwracała cokolwiek, ponieważ zawsze będzie to liczba 1, niezależnie od danych wejściowych. Dlatego lepsza implementacja pozwoliłaby uniknąć zwracania jakiejkolwiek wartości, aby uniknąć sugestii, że jest ona sensowna. Np. Powiedziałbym, że lepszą implementacją (części rekurencyjnej) jest
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Zapraszam do wypróbowania kodu, który napisałem dla siebie, aby to zobaczyć tak nie jest (przynajmniej dla dodatnich liczb całkowitych). Jeśli chcesz omówić to dalej, proponuję porozmawiać.
Dodaj komentarz