Rozważmy operację potęgowania modularnego, stosowaną np. w algorytmie RSA. Liczbę podnosimy do potęgi , po czym bierzemy resztę z dzielenia otrzymanej liczby przez ustaloną liczbę , dzięki czemu otrzymujemy wynik
,
gdzie , – dodatnie liczby całkowite, – nieujemna liczba całkowita.
Mówimy wtedy, że równa się .
Przykład 1
Dla liczymy resztę z dzielenia (czyli ) przez , zatem .
Dla i mamy , natomiast dla i wynikiem jest .
Zapisz w wybranej przez siebie notacji (w postaci pseudokodupseudokodpseudokodu, listy krokówlista krokówlisty kroków lub w wybranym języku programowania) algorytm, który gdy są dane liczby , i , obliczy . Aby otrzymać maksymalną liczbę punktów, twój algorytm powinien wykonywać operacji arytmetycznych wymienionych w poniższej uwadze.
Uwaga!
W zapisie algorytmu możesz wykorzystać tylko operacje arytmetyczne: dodawanie, odejmowanie, mnożenie, dzielenie, dzielenie całkowite, resztę z dzielenia, oraz porównywanie liczb; instrukcje sterujące i przypisania do zmiennych lub samodzielnie napisane funkcje zawierające wymienione operacje.
Specyfikacja problemu:
Dane:
a – liczba całkowita dodatnia
x – nieujemna liczba całkowita
M – liczba całkowita dodatnia
Wynik:
b – nieujemna liczba całkowita o wartości równej
Zadanie zostało opracowane przez Centralną Komisję Egzaminacyjną i pojawiło się w przykładowym arkuszu maturalnym egzaminu z informatyki w formule . Cały arkusz można znaleźć na stronie internetowej CKE.
Rozwiązanie
Pseudokod
Ważne!
W zapisanym za pomocą pseudokodu algorytmie używamy operatorów div i mod. Operator div oznacza dzielenie całkowitoliczbowe, a mod – resztę z dzielenia.
Zauważmy, że ograniczenie liczby operacji () wyklucza podnoszenie do potęgi poprzez mnożenie podstawy tyle razy, ile wynosi wykładnik (). W związku z tym skorzystamy z algorytmu szybkiego potęgowania liczbPeGPO00R1algorytmu szybkiego potęgowania liczb.
Zapisujemy początkowe warunki funkcji:
Linia 1. funkcja potęga otwórz nawias okrągły a przecinek x przecinek M zamknij nawias okrągły.
Linia 2. wynik ← 1.
Linia 3. mnoznik ← a.
W każdym przebiegu pętli będziemy podnosić mnożnik do kwadratu i wykonywać na nim działanie (zgodnie z przechodzeniem na wyższe bity z postaci binarnej) oraz pomniejszać wartość za pomocą dzielenia całkowitoliczbowego przez . Jeśli bit na danej pozycji jest niezerowy, przemnażamy wynik przez mnożnik i wykonujemy operację :
Linia 1. funkcja potęga otwórz nawias okrągły a przecinek x przecinek M zamknij nawias okrągły.
Linia 2. wynik ← 1.
Linia 3. mnoznik ← a.
Linia 4. dopóki x zamknij nawias ostrokątny 0 wykonuj dwukropek.
Linia 5. jeżeli x mod 2 znak równości 1 wykonaj dwukropek.
Linia 6. wynik ← wynik asterysk mnoznik mod M.
Linia 7. mnoznik ← mnoznik asterysk mnoznik mod M.
Linia 8. x ← x div 2.
Po zakończeniu pętli (czyli wymnożeniu dla wszystkich bitów z postaci binarnej liczby ) zwracamy wynik:
Linia 1. funkcja potęga otwórz nawias okrągły a przecinek x przecinek M zamknij nawias okrągły.
Linia 2. wynik ← 1.
Linia 3. mnoznik ← a.
Linia 4. dopóki x zamknij nawias ostrokątny 0 wykonuj dwukropek.
Linia 5. jeżeli x mod 2 znak równości 1 wykonaj dwukropek.
Linia 6. wynik ← wynik asterysk mnoznik mod M.
Linia 7. mnoznik ← mnoznik asterysk mnoznik mod M.
Linia 8. x ← x div 2.
Linia 9. zwróc wynik.
Oto rozwiązania w innych notacjach:
Lista kroków
Zainicjuj wynik jako 1.
Zainicjuj mnożnik jako a.
Dopóki x > 0, wykonuj kroki 4 - 7.
Jeżeli x mod 2 = 1, wykonaj krok 5.
Wynik pomnóż przez mnoznik mod M.
Mnożnik pomnóż przez mnoznik mod M.
Podziel całkowicie wynik przez 2.
Zwróć wynik.
C++
Linia 1. int potega otwórz nawias okrągły int a przecinek int x przecinek int M zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. int wynik znak równości 1 średnik.
Linia 3. int mnoznik znak równości a średnik.
Linia 4. while otwórz nawias okrągły x zamknij nawias ostrokątny 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. if otwórz nawias okrągły x procent 2 znak równości znak równości 1 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. wynik znak równości wynik asterysk mnoznik procent M średnik.
Linia 7. zamknij nawias klamrowy.
Linia 8. mnoznik znak równości mnoznik asterysk mnoznik procent M średnik.
Linia 9. x znak równości x prawy ukośnik 2 średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. return wynik średnik.
Linia 12. zamknij nawias klamrowy.
Java
Linia 1. int potega otwórz nawias okrągły int a przecinek int x przecinek int M zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. int wynik znak równości 1 średnik.
Linia 3. int mnoznik znak równości a średnik.
Linia 4. while otwórz nawias okrągły x zamknij nawias ostrokątny 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. if otwórz nawias okrągły x procent 2 znak równości znak równości 1 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. wynik znak równości wynik asterysk mnoznik procent M średnik.
Linia 7. zamknij nawias klamrowy.
Linia 8. mnoznik znak równości mnoznik asterysk mnoznik procent M średnik.
Linia 9. x znak równości x prawy ukośnik 2 średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. return wynik średnik.
Linia 12. zamknij nawias klamrowy.
Python
Linia 1. def potega otwórz nawias okrągły a przecinek x przecinek M zamknij nawias okrągły dwukropek.
Linia 2. wynik znak równości 1.
Linia 3. mnoznik znak równości a.
Linia 4. while x zamknij nawias ostrokątny 0 dwukropek.
Linia 5. if x procent 2 znak równości znak równości 1 dwukropek.
Linia 6. wynik znak równości wynik asterysk mnoznik procent M.
Linia 7. mnoznik znak równości mnoznik asterysk mnoznik procent M.
Linia 8. x znak równości x prawy ukośnik prawy ukośnik 2.
Linia 9. return wynik.
Schemat oceniania
4 pkt – za poprawny algorytm, w tym:
3 pkt – za poprawne obliczenie potęgi (w tym 1 pkt za warunki początkowe oraz 2 pkt za poprawną konstrukcję pętli z uwzględnieniem parzystości ),
1 pkt – za zwrócenie poprawnego wyniku,
2 pkt – za poprawny algorytm o złożoności większej niż logarytmiczna,
0 pkt – za odpowiedź niepoprawną lub niepełną albo za brak odpowiedzi.
Praca domowa
Na podstawie przedstawionych funkcji napisz na swoim komputerze program w języku, w którym programujesz. Przetestuj rozwiązanie dla wybranych przez siebie danych.
Zadanie 2
W tym zadaniu zajmujemy się algorytmami działającymi na n‑elementowej tablicy liczb całkowitych A[1..n], gdzie n jest dodatnią liczbą całkowitą.
Poniżej zapisano rekurencyjną procedurę W, której parametrem jest liczba całkowita j z przedziału [1, n]:
Linia 1. procedura W otwórz nawias okrągły j zamknij nawias okrągły dwukropek.
Linia 2. jeśli j zamknij nawias ostrokątny 1 to.
Linia 3. jeśli A otwórz nawias kwadratowy j minus 1 zamknij nawias kwadratowy zamknij nawias ostrokątny A otwórz nawias kwadratowy j zamknij nawias kwadratowy to.
Linia 4. v ← A otwórz nawias kwadratowy j zamknij nawias kwadratowy.
Linia 5. A otwórz nawias kwadratowy j zamknij nawias kwadratowy ← A otwórz nawias kwadratowy j minus 1 zamknij nawias kwadratowy.
Linia 6. A otwórz nawias kwadratowy j minus 1 zamknij nawias kwadratowy ← v.
Linia 7. W otwórz nawias okrągły j minus 1 zamknij nawias okrągły.
Oto przykładowa, 10‑elementowa tablica A[1..10] o zawartości [2,4,6,8,10,9,7,5,3,1].
W wybranej przez siebie notacji zapisz nierekurencyjną wersję procedury W.
Uwaga!
W zapisie algorytmu możesz wykorzystać tylko operacje arytmetyczne (dodawanie, odejmowanie, mnożenie, dzielenie, dzielenie całkowite, reszta z dzielenia), instrukcje porównania, instrukcje sterujące i przypisania do zmiennych lub samodzielnie napisane funkcje, wykorzystujące wyżej wymienione operacje.
Zadanie zostało opracowane przez CKE i pojawiło się w informatorze o egzaminie maturalnym z informatyki w formule . Cały arkusz można znaleźć na stronie internetowej Centralnej Komisji Egzaminacyjnej.
Rozwiązanie
Przedstawmy algorytm za pomocą dopuszczonych na egzaminie maturalnym notacji, czyli pseudokodu, listy kroków, a także języków programowania: C++, Java i Python.
Zauważmy, że procedura W ma pewne cechy wspólne z algorytmem sortowania bąbelkowegoPCfvfjgLksortowania bąbelkowego pojedynczego elementu. Jest on zamieniany miejscami z elementem o wcześniejszym indeksie dopóty, dopóki trafi na element mniejszy od siebie. Warunki początkowe procedury pozostają bez zmiany.
Weźmy pod uwagę przykładową tablicę podaną w treści zadania: A[1..10] o zawartości [2,4,6,8,10,9,7,5,3,1].
Jeżeli wywołamy dla niej procedurę W z parametrem 7, otrzymamy:
Linia 1. 2 4 6 7 8 10 9 5 3 1.
Widzimy, że element będący na siódmym miejscu w tablicy, czyli 7, był zamieniany z poprzednim elementem, aż trafił na wartość mniejszą od siebie, czyli 6.
Następnie wywołajmy procedurę z parametrem 9. Otrzymujemy następujący wynik:
Linia 1. 2 3 4 6 7 8 10 9 5 1.
Na dziewiątym miejscu znajdował się element 3. Widzimy, że był on zamieniany z poprzednim elementem, aż trafił na mniejszy od siebie, czyli 2.
Zapiszmy nierekurencyjną wersję procedury W. Tworzymy zmienną pomocniczą i. Dopóki indeks i jest większy od , a elementy na poprzedzających indeksach większe od wartości v, dopóty przesuwamy je w prawo.
Następnie wstawiamy element v w miejsce o tym indeksie, na którym skończyliśmy pętlę.
Linia 1. procedura W otwórz nawias okrągły j zamknij nawias okrągły.
Linia 2. jeżeli j zamknij nawias ostrokątny 1 dwukropek.
Linia 3. v ← A otwórz nawias kwadratowy j zamknij nawias kwadratowy.
Linia 4. i ← j.
Linia 5. dopóki i zamknij nawias ostrokątny 1 oraz A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy zamknij nawias ostrokątny v wykonuj dwukropek.
Linia 6. A otwórz nawias kwadratowy i zamknij nawias kwadratowy ← A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy.
Linia 7. i ← i minus 1.
Linia 8. A otwórz nawias kwadratowy i zamknij nawias kwadratowy ← v.
Rozwiązania zapisane za pomocą innych dopuszczonych na egzaminie maturalnym notacji wyglądają następująco:
Lista kroków
Jeżeli j > 1, wykonaj kroki 2‑6.
Zainicjuj v jako A[j].
Zainicjuj i jako j.
Dopóki i > 1 oraz A[i - 1] > v, wykonuj kroki 4 i 5.
Do A[i] przypisz A[i - 1].
Do i przypisz i - 1.
Do A[i] przypisz v.
C++
Linia 1. void W otwórz nawias okrągły int j zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. if otwórz nawias okrągły j zamknij nawias ostrokątny 1 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. int v znak równości A otwórz nawias kwadratowy j zamknij nawias kwadratowy średnik.
Linia 4. int i znak równości j średnik.
Linia 5. while otwórz nawias okrągły otwórz nawias okrągły i zamknij nawias ostrokątny 1 zamknij nawias okrągły ampersant ampersant A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy zamknij nawias ostrokątny v zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. A otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy średnik.
Linia 7. i minus znak równości 1 średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. A otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości v średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. zamknij nawias klamrowy.
Java
Linia 1. void W otwórz nawias okrągły int j zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. if otwórz nawias okrągły j zamknij nawias ostrokątny 1 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. int v znak równości A otwórz nawias kwadratowy j zamknij nawias kwadratowy średnik.
Linia 4. int i znak równości j średnik.
Linia 5. while otwórz nawias okrągły otwórz nawias okrągły i zamknij nawias ostrokątny 1 zamknij nawias okrągły ampersant ampersant A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy zamknij nawias ostrokątny v zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. A otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy średnik.
Linia 7. i minus znak równości 1 średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. A otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości v średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. zamknij nawias klamrowy.
Python
Linia 1. def W otwórz nawias okrągły j zamknij nawias okrągły dwukropek.
Linia 2. if j zamknij nawias ostrokątny 1 dwukropek.
Linia 3. v znak równości A otwórz nawias kwadratowy j zamknij nawias kwadratowy.
Linia 4. i znak równości j.
Linia 5. while i zamknij nawias ostrokątny 1 and A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy zamknij nawias ostrokątny v dwukropek.
Linia 6. A otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości A otwórz nawias kwadratowy i minus 1 zamknij nawias kwadratowy.
Linia 7. i minus znak równości 1.
Linia 8. A otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości v.
Schemat oceniania
3 pkt – poprawny algorytm w tym:
1 pkt – za poprawne inicjowanie zmiennych,
1 pkt – za poprawną konstrukcję pętli,
1 pkt – za poprawne instrukcje w pętli,
0 pkt – za niepoprawną odpowiedź albo brak odpowiedzi.
Praca domowa
Na podstawie przedstawionych funkcji napisz na swoim komputerze program w języku, w którym programujesz. Przetestuj rozwiązanie dla wybranych przez siebie danych.
Słownik
lista kroków
lista kroków
jeden ze sposobów zapisu algorytmu; jest to numerowana instrukcja postępowania
pseudokod
pseudokod
jedna z metod zapisywania algorytmów; pseudokod jest zapisem uniwersalnym, w przypadku którego nie używa się słów kluczowych języków programowania; nie istnieją sformalizowane reguły zapisu pseudokodu – ma być on przejrzysty i zrozumiały, a do jego odczytania nie może być wymagana znajomość języków programowania