Liczby i ich tajemnice: algorytmy w działaniu
Liczby pierwsze
Zgodnie z definicją liczba pierwsza to taka, która spełnia następujący warunek:
ma tylko dwa dzielniki: jeden i samą siebie.
Zaimplementujmy w języku C++ algorytm pozwalający sprawdzić, czy podana liczba jest liczbą pierwszą.
Zacznijmy od utworzenia dwóch zmiennych: jednej typu całkowitego (int) o nazwie liczbaTest oraz drugiej typu bool, która będzie się nazywać czyPierwsza. Do liczbaTest zostanie zapisana liczba, którą użytkownik wpisał na klawiaturze, w celu sprawdzenia, czy jest ona liczbą pierwszą. Natomiast zmienna czyPierwsza będzie przyjmować wartość true lub false w zależności od tego, czy mamy do czynienia z liczbą pierwszą, czy też nie.
Ponieważ wspomnieliśmy o interakcji programu z użytkownikiem, napiszmy kod odpowiedzialny za możliwość wprowadzenia przez użytkownika własnych danych, czyli w tym przypadku liczby, która ma zostać poddana sprawdzeniu.
Następnym krokiem będzie napisanie pętli, w której będziemy sprawdzać liczby od 2 do liczby mniejszej o jeden od podanej. Ponieważ dzielnikami liczby pierwszej jest 1 i podana liczba, więc w przedziale nie powinno być żadnego dzielnika podanej liczby. W przypadku wykrycia dzielnika całkowitego pętla zostanie przerwana instrukcją break, a zmienna logiczna czyPierwsza przyjmie wartość false, co będzie oznaczało, że podana przez użytkownika liczba nie jest liczbą pierwszą.
Możemy zoptymalizować nasz algorytm i sprawdzanie, czy liczba jest dzielnikiem liczbaTest wykonywać w przedziale . Pętlę for można wtedy przekształcić następująco: for(int i = 2; i*i <= liczbaTest; i++). Zapis i*i <= liczbaTest wynika z podniesienia dwóch stron do kwadratu, aby pozbyć się pierwiastka. Dlaczego sprawdzanie wystarczy dokonać tylko do pierwiastka liczby? Przeanalizujmy przykłady liczb 18 oraz 16 i wypiszmy ich dzielniki:
Dzielniki 18 = { 1, 2, 3, |4.24|, 6, 9, 18}
Dzielniki 16 = { 1, 2, |4|, 8, 16}
Pomiędzy znakami | | zapisano wartość bezwzględną pierwiastka liczby .
Można zauważyć, że jeśli liczba ma dzielniki większe od 1, to posiada ich tyle samo po obu stronach od . W przypadku liczb kwadratowych np. 16, 25, 36 itp. pierwiastek liczby będzie jej dzielnikiem.
Na koniec określamy, jaki komunikat ma się pojawić na standardowym wyjściu, w zależności od tego, jaka jest wartość zmiennej czyPierwsza.
Instrukcję warunkową if(czyPierwsza == true) można zapisać również jako if(czyPierwsza).
Zobaczmy kod całego programu.
Warto zauważyć, że w tej implementacji algorytmu wychodzimy z założenia, że podana liczba jest liczbą pierwszą, ponieważ przy deklaracji zmiennej logicznej czyPierwsza nadajemy jej wartość true. Po wykonaniu pętli for, jeżeli wartość czyPierwsza nie zostanie zmieniona, jej wartość nadal będzie wynosić true, a my uzyskamy pewność, że mamy do czynienia z liczbą pierwszą.
Liczby doskonałe
Zacznijmy od przypomnienia warunku, jaki musi spełniać liczba doskonała:
Suma dzielników mniejszych od sprawdzanej liczby musi być równa tej liczbie.
Przykładami liczb doskonałych są:
6, ponieważ:
gdzie 1, 2 i 3 to dzielniki właściwedzielniki właściwe liczby 6.
28, ponieważ:
gdzie 1, 2, 3, 4, 7, 14 to dzielniki właściwe liczby 28.
Można zauważyć, że nasz algorytm będzie polegał na znalezieniu dzielników podanej liczby, zsumowaniu ich i sprawdzeniu, czy wynik jest równy sprawdzanej liczbie. Przeanalizujmy kod programu, który stanowi rozwiązanie tego problemu:
W pętli for sprawdzamy, czy kolejne liczby (zaczynając od 1) są dzielnikami zmiennej liczbaTest. Jeżeli znajdziemy taki dzielnik, dodajemy go do zmiennej sumaDzielnikow. Następnie porównujemy wartość zmiennej sumaDzielnikow z podaną do sprawdzenia liczbą. Jeżeli są one równe, to jest to liczba doskonała.
Liczba doskonała – optymalizacja algorytmu
Zastanówmy się, czy można zoptymalizować przedstawiony wyżej algorytm. Pierwszą rzeczą, którą możemy zrobić, jest rozpoczęcie przetwarzanie pętli od liczby 2, ponieważ każda liczba jest podzielna przez 1. Unikniemy dzięki temu jednej, niepotrzebnej iteracji.
Możemy także dodać warunek wewnątrz pętli. Pozwoli to sprawdzić, czy aktualna wartość zmiennej sumaDzielnikow nie jest większa od badanej liczby. W ten sposób uda się uniknąć wykonywania wielu niepotrzebnych cykli pętli.
Niżej przedstawiamy kod programu po wprowadzeniu zmian optymalizacyjnych.
Liczby zaprzyjaźnione
Liczby zaprzyjaźnione to takie, których suma dzielników właściwych pierwszej liczby jest równa wartości drugiej liczby, a suma dzielników właściwych drugiej liczby jest równa wartości pierwszej liczby.
Przykładem pary takich liczb są: 220 i 284.
Dzielniki właściwe liczby 220 to: . Natomiast dzielniki właściwe liczby 284 to: .
Przeanalizujmy implementację algorytmu, która pozwoli sprawdzić, czy podane przez użytkownika dwie liczby są zaprzyjaźnione.
Na początku programu deklarujemy zmienne liczba1 i liczba2. To w nich zapiszemy liczby, które użytkownik podał do sprawdzenia. Kolejne zmienne to l1Suma oraz l2Suma. Będą one odpowiadały kolejno sumie dzielników właściwych liczby pierwszej i sumie dzielników właściwych liczby drugiej.
Następnie w pętlach for sprawdzamy kolejne dzielniki liczb i sumujemy je. W końcowej części programu porównujemy wartość pierwszej liczby z sumą dzielników liczby drugiej i wartość liczby drugiej z sumą dzielników liczby pierwszej, a następnie wypisujemy odpowiedni komunikat.
Słownik
dzielniki liczby mniejsze od niej samej