baner
home exercises theme

Sprawdzanie pierwszości, podzielności i liczby dzielników liczb

Podzielność

Aby sprawdzić, czy liczba $a$ dzieli się przez liczbę $b$, możemy użyć operatora %. Sprawdzimy więc, czy reszta z dzielenia liczby $a$ przez liczbę $b$ jest równa 0.

bool czy_a_dzieli_sie_przez_b = false; // poczatkowo ustawiamy falsz
if (a % b == 0) czy_a_dzieli_sie_przez_b = true; // uzywamy operatora %

Powyższy fragment kodu sprawdza, czy liczba $a$ dzieli się przez liczbę $b$.

Sprawdzanie pierwszości

Korzystając z pętli while możemy sprawdzić, czy dana liczba jest pierwsza. Liczba pierwsza to taka, która ma dokładnie 2 różne dzielniki (1 i samą siebie). Dla przykładu, liczby 2, 3, 5, 7 są liczbami pierwszymi, a liczby 0, 1, 4, 8, 9 nie są. Więcej o liczbach pierwszych można wyczytać na Wikipedii. Spróbujmy się zastanowić, jak by miał działać prosty program, który wczytuje jedną liczbę naturalną $a$, taką, że $0 \leq a \leq 10^6$ i wypisuje TAK jeśli liczba jest pierwsza lub NIE w przeciwnym wypadku. Oto pomysł na program:

  • Wczytywanie liczby $a$
  • Liczenie jej dzielników - sprawdzanie wszystkich od 1 do $a$ (jeżeli $a$ jest podzielne przez sprawdzany dzielnik zwiększamy licznik o 1)
  • Jeśli liczba dzielników jest równa 2, wypisujemy TAK, w przeciwnym wypadku wypisujemy NIE
#include <iostream>
using namespace std;
int main()
{
    // wczytywanie
    int liczba;
    cin >> liczba;
    
    int liczba_dzielnikow = 0;
    int sprawdzany_dzielnik = 1; // dzielniki zaczynamy sprawdzać od 1

    // sprawdzamy od 1 do liczby podanej na wejsciu włącznie
    while (sprawdzany_dzielnik <= liczba)
    {
        // sprawdzamy, czy liczba podana na wejsciu dzieli sie przez obecnie sprawdzana liczbe
        if (liczba % sprawdzany_dzielnik == 0)
            liczba_dzielnikow++; // jesli tak, to znalezlismy kolejny dzielnik
        sprawdzany_dzielnik++; // sprawdzamy kolejny dzielnik
    }
    // wypisywanie TAK lub NIE
    
    if (liczba_dzielnikow == 2) {
        cout << "TAK";
    }
    else {
        cout << "NIE";
    }
    return 0;
}

Optymalizacja

Zauważmy, że można przyspieszyć ten program. Po pierwsze, nie musimy sprawdzać czy liczba dzieli się przez 1 i samą siebie (jest naturalna, więc zawsze się dzieli). To ogranicza program do sprawdzania dzielników od 2 do $a-1$. Teraz w przypadku wykrycia jakiegokolwiek dzielnika będzie wiadomo, że jest to trzeci dzielnik i należy wypisać NIE i zakończyć program (nie działa to jednak dla 0 i 1, więc można przed pętlą while użyć instrukcji warunkowej if). Dodatkowo, zauważmy, że wystarczy sprawdzać wszystkie możliwe dzielniki do $\sqrt{a}$ (uwzględniając $\sqrt{a}$), ponieważ jeśli jakaś liczba ma dzielnik większy niż $\sqrt a$, to ma też dzielnik mniejszy niż $\sqrt a$, więc na pewno zostanie on wykryty. Tak będzie wyglądał przyspieszony program:

#include <iostream>
using namespace std;

int main()
{
    int liczba;
    cin >> liczba;

    // sprawdzamy, czy liczba jest równa 0 lub 1
    if (liczba < 2)
    {
        // jesli tak, wypisujemy NIE i konczymy dzialanie programu
        cout << "NIE";
        return 0;
    }

    int sprawdzany_dzielnik = 2;
    // sprawdzamy do pierwiastka (a * a <= b to dla liczb naturalnych to samo co a<=pierwiastek z b)
    while (sprawdzany_dzielnik * sprawdzany_dzielnik <= liczba)
    {
        if (liczba % sprawdzany_dzielnik == 0)
        {
            // jesli znajdziemy jakikolwiek dzielnik, wypisujemy NIE i konczymy dzialanie programu
            cout << "NIE";
            return 0;
        }
        sprawdzany_dzielnik++;
    }
    // jesli do tej pory nie wykryto zadnych dzielnikow, liczba jest pierwsza
    cout << "TAK";
    return 0;
}

Ten program działa poprawnie i dużo szybciej.

Sprawdzanie liczby dzielników

Aby sprawdzić liczbę dzielników danej liczby, możemy, tak jak w sprawdzaniu pierwszości, skorzystać ze sprawdzania do pierwiastka, ponieważ dla każdego dzielnika mniejszego niż $\sqrt a$ przypada dokładnie jeden dzielnik większy niż $\sqrt a$. Możemy więc po prostu zwiększać wynik o 2 przy wykryciu jakiegoś dzielnika, sprawdzając oczywiście wszystkie dzielniki mniejsze niż $\sqrt a$ - w przeciwnym wypadku pierwiastek mógłby być policzony podwójnie. Sam pierwiastek musi zostać sprawdzony osobno. Oto program wczytujący liczbę $a$, taką, że $1 \leq a \leq 10^6$ i wypisujący jej liczbę dzielników.

#include <iostream>
using namespace std;

int main()
{
    int liczba;
    cin >> liczba;

    if (liczba == 1) {
        cout << 1;
        return 0;
    }

    int liczba_dzielnikow = 2; // 1 i liczba na wejsciu
    int sprawdzany_dzielnik = 2; // sprawdzamy dzielniki od 2
    while (sprawdzany_dzielnik * sprawdzany_dzielnik < liczba) {
        if (liczba % sprawdzany_dzielnik == 0) {
            liczba_dzielnikow += 2; // zwiekszamy licznik o dwa
        }
        sprawdzany_dzielnik++;
    }
    if (sprawdzany_dzielnik * sprawdzany_dzielnik == liczba) {
        // sprawdzamy pierwiastek i liczymy go pojedynczo
        liczba_dzielnikow++;
    }
    cout << liczba_dzielnikow;
    return 0;
}