Przeskocz do treści

Delta mi!

Myśl logarytmicznie!

Maciej Sysło i Anna Kwiatkowska

o artykule ...

  • Publikacja w Delcie: grudzień 2014
  • Publikacja elektroniczna: 01-12-2014
  • Autor: Maciej M. Sysło
    Afiliacja: Uniwersytet Mikołaja Kopernika w Toruniu
    Autor: Anna B. Kwiatkowska
    Afiliacja: Uniwersytet Mikołaja Kopernika w Toruniu
  • Wersja do druku [application/pdf]: (441 KB)
  • Ten artykuł dedykujemy Johnowi Napierowi (1550-1617) z okazji 400-lecia odkrycia logarytmu ogłoszonego w książce Mirifici Logarithmorum Canonis Descriptio

W tym artykule ilustrujemy potęgę logarytmów w projektowaniu efektywnych algorytmów i obliczeń. Myślenie, w tle którego stoi logarytm, ukryty lub widoczny, nazwaliśmy myśleniem logarytmicznym. Stanowi ono jedną z podstawowych kompetencji niezbędnych przy efektywnym rozwiązywaniu rzeczywistych problemów informatycznych. Pokazujemy również - co może być ciekawe dla nauczycieli matematyki - jak wprowadzić pojęcie logarytmu, nie odwołując się do matematycznego formalizmu, a posługując się koncepcyjnym modelem redukcji rozmiaru problemu w każdym (lub w co drugim) kroku co najmniej o połowę. Może Cię zdziwić, że ta idea prowadząca do logarytmu występuje w algorytmie Euklidesa, który został opisany niemal 2000 lat przed wynalezieniem logarytmu przez Napiera.

Krótko po wynalezieniu logarytmu Edmund Gunter w 1620 roku utworzył skalę logarytmiczną, a z połączenia dwóch takich skal William Oughtred w 1622 roku zbudował suwak logarytmiczny. Oryginalnie, suwaki służyły do wykonywania mnożenia (patrz Rys. 1) i dzielenia, inne skale na suwakach służą do podnoszenia do kwadratu i do wyciągania pierwiastków kwadratowych, do obliczania trzecich potęg i pierwiastków trzeciego stopnia, do obliczania wartości funkcji trygonometrycznych i do wielu innych obliczeń. Jeden z najbogatszych suwaków, Faber Castell 2/83N, zawiera aż 21 skal. Budowano suwaki podłużne, okrągłe i ze skalami nawiniętymi na cylindry, by móc wydłużyć ich długości dla osiągnięcia dokładniejszych wyników - skala na cylindrycznym suwaku Fullera ma długość 12 metrów.

obrazek

Rys. 1 Sposób obliczania iloczynu math za pomocą suwaka logarytmicznego

Rys. 1 Sposób obliczania iloczynu math za pomocą suwaka logarytmicznego

Dodajmy tutaj, że Napier wynalazł również tak zwane pałeczki Napiera, które służą do mnożenia liczb, nie mają one jednak nic wspólnego z logarytmami. Posłużyły natomiast Wilhelmowi Schickardowi do zbudowania w 1624 roku pierwszego kalkulatora mechanicznego.

Rok 1972 to początek agonii suwaków - zaczęły je wypierać stworzone z ich pomocą kalkulatory elektroniczne. Ponad 40 milionów wcześniej wyprodukowanych suwaków stało się nagle bezużytecznych i obecnie stanowi głównie eksponaty kolekcjonerskie. Dzisiaj jednak nie można wyobrazić sobie zajmowania się informatyką, bez przynajmniej "otarcia" się o logarytmy, co ilustrujemy w tym artykule.

Funkcje nie są obiektami matematycznymi lubianymi przez uczniów, zwłaszcza gdy są wprowadzane w sposób formalny jako przekształcenia (odwzorowania). Wśród nich "najgorszą sławą" cieszy się logarytm, bo nie dość, że jest to funkcja, to na dodatek jest to funkcja odwrotna. Jednakże do zrozumienia tego, o czym tutaj piszemy, nie będzie potrzebna znajomość pojęcia logarytmu.

W przeszłości uzasadnieniem dla posługiwania się logarytmami, były właściwości, które legły u podstaw jego wprowadzenia do obliczeń. Logarytm ułatwia wykonywanie złożonych obliczeń dzięki zastąpieniu działań multiplikatywnych (takich jak mnożenie i dzielenie) przez działania addytywne (dodawanie i odejmowanie). Nie tak dawno jeszcze, w szkołach posługiwano się tablicami logarytmicznymi, a na uczelniach i w zakładach pracy przyszli i zawodowi inżynierowie korzystali z suwaków logarytmicznych. Obecnie logarytm pełni rolę tzw. mental tool - narzędzia myślowego, sposobu rozumowania.

Rozważmy pięć następujących pytań:

  • Ile należy przejrzeć kartek w słowniku, aby znaleźć poszukiwane hasło?
  • Ile miejsca w komputerze (a dokładniej ile bitów) zajmuje liczba naturalna?
  • Jak szybko można wykonywać potęgowanie dla dużych wykładników potęg?
  • Ile trwa obliczanie największego wspólnego dzielnika dwóch liczb za pomocą algorytmu Euklidesa?
  • Ile kroków wykonuje algorytm typu dziel i zwyciężaj uruchomiony na danych o math elementach?

Wspólną cechą odpowiedzi na te pytania jest to, że nie można ich udzielić, nie dotykając logarytmu, pośrednio lub bezpośrednio, a ponadto wyjaśnienie tych odpowiedzi służy lepszemu zrozumieniu pojęcia logarytmu i jego roli w projektowaniu efektywnych algorytmów.

Znajdź szybko hasło w słowniku, odgadnij ukrytą liczbę

Papierowa książka telefoniczna ma 1000 stron. Jak znaleźć numer telefonu do pana Skarbka, aby przeglądnąć możliwie najmniejszą liczbę stron w tej książce? Zapewne szybko wpadniesz na pomysł, że najlepszą metodą jest podział pliku stron, na których może być numer telefonu pana Skarbka, na połowę i odrzucenie tej połowy, w której na pewno nie ma informacji o panu Skarbku. Ten podział jest kontynuowany, aż pozostanie tylko jedna strona, na której może się znaleźć numer telefonu pana Skarbka. Jest to tak zwane poszukiwanie binarne lub przez połowienie.

W dwuosobowej grze w odgadywanie liczby z podanego przedziału, pomyślanej przez jedną z osób, w której druga osoba może zadawać pytania "czy pomyślana liczba jest większa czy mniejsza niż math" również możemy zastosować podobną strategię. Jeśli każde pytanie będzie dotyczyło wartości math leżącej w połowie aktualnego przedziału, w którym znajduje się poszukiwana liczba, to liczba pytań niezbędna do odgadnięcia pomyślanej liczby będzie równa liczbie połowień oryginalnego przedziału.

Przy okazji warto zwrócić uwagę na następujące kwestie:

  • Bardzo ważny jest alfabetyczny porządek nazwisk w książce telefonicznej i liczb w przedziale. Jak ci się wydaje, ile stron należałoby przejrzeć w 1000-stronicowej książce telefonicznej, by znaleźć nazwisko właściciela telefonu o numerze 1234567?
  • W przypadku słownika, w którym chcemy znaleźć hasło zaczynające się początkową literą alfabetu, na ogół próbujemy znaleźć poszukiwane hasło na początkowych stronach słownika. Taka metoda nazywa się interpolacyjnym poszukiwaniem - na ogół działa szybciej, niż binarne poszukiwanie (więcej na ten temat znajdziesz w książce M.M. Sysło, Algorytmy, WSiP 1997; Helion 2014).

Binarna reprezentacja liczb i rozmiar liczby w komputerze

Zapewne wiesz, jak otrzymać binarną reprezentację liczby naturalnej math Taka reprezentacja jest generowana w trakcie podziału liczby math i otrzymywanych ilorazów przez 2. Rozpoczynamy od podzielenia math przez 2 i resztę z tego dzielenia math (0 lub 1) przyjmujemy za najmniej znaczący bit w reprezentacji. Następnie powtarzamy tę procedurę dla ilorazu math tak długo, jak długo iloraz math jest większy od 0. Na przykład, dla math otrzymujemy math jak w tabeli 2.

Czy zastanawiałeś się, z ilu bitów składa się binarna reprezentacja dziesiętnej liczby naturalnej math lub inaczej, jak dużą pamięć w komputerze zajmuje liczba math By odpowiedzieć na to pytanie, załóżmy, że math zajmuje math bitów i określmy, jaka jest najmniejsza i największa liczba zajmująca dokładnie math bitów. Największa taka liczba składa się z math bitów równych 1, czyli

display-math

Z drugiej strony, najmniejsza liczba reprezentowana dokładnie na math bitach ma tylko jedną jedynkę na najbardziej znaczącej pozycji. Stąd otrzymujemy następujące nierówności:

display-math

Dodajmy 1 do wszystkich stron tych nierówności i weźmy logarytm math Otrzymamy:

display-math

Ponieważ liczba bitów math jest liczbą naturalną (całkowitą dodatnią), mamy:

display-math

gdzie math jest sufitem liczby math czyli równa się najmniejszej liczbie naturalnej większej lub równej math Z tej części rozważań możemy wyciągnąć wniosek, że naturalna liczba math zajmuje w pamięci komputera około math bitów - ta liczba jest często przyjmowana za komputerowy rozmiar liczby math Zauważmy, że poszukiwanie binarne w przedziale zawierającym math liczb odpowiada tworzeniu binarnej reprezentacji liczby math liczba kroków w takim poszukiwaniu wynosi około math

Możemy teraz algorytmicznie zdefiniować math jako:

Definicja. Logarytm math jest równy liczbie kroków, jakie potrzebujemy by zmniejszyć math do math dzieląc sukcesywnie przez math

O znaczeniu i "potędze" logarytmów i funkcji logarytmicznej w informatyce, a ogólniej - w obliczeniach decyduje szybkość wzrostu jej wartości, nieporównywalnie mała względem szybkości wzrostu jej argumentu, co ilustrujemy w tabeli 3. A zatem dla liczb, które mają około stu cyfr, wartość logarytmu wynosi tylko około 333.

Szybkie podnoszenie do potęgi

Podnoszenie do potęgi jest bardzo prostym, szkolnym zadaniem. Na przykład, aby obliczyć math wykonujemy trzy mnożenia math A zatem w ogólności, aby w ten sposób obliczyć wartość potęgi math należy wykonać math mnożeń: o jedno mniej niż wynosi wykładnik potęgi. Czy ten "szkolny" algorytm jest na tyle szybki, by obliczyć na przykład wartość potęgi:

display-math

która może pojawić się przy szyfrowaniu metodą RSA informacji przesyłanych w Internecie?

Oszacujmy, ile czasu będzie trwało obliczanie tej potęgi, jeśli zastosujemy szkolny algorytm, czyli ile czasu zabierze wykonanie math mnożeń. Przypuśćmy, że dysponujemy superkomputerem, który działa z szybkością jednego petaflopa, zatem wykonuje około math operacji na sekundę. Obliczenie powyższej potęgi będzie trwało

display-math

Jeśli taki algorytm byłby stosowany do szyfrowania naszej poczty w Internecie, to nigdy nie otrzymalibyśmy żadnego listu. Dodajmy, że w praktycznych sytuacjach konieczne jest obliczanie potęg o wykładnikach, które mają kilkaset cyfr.

Postaramy się teraz tak wykonywać potęgowanie, aby w każdym kroku wykładnik zmniejszył się około o połowę. W tym celu zauważmy, że jeżeli math jest liczba parzystą, czyli na przykład math to math a gdy math jest liczbą nieparzystą, czyli math to math Na przykład, by obliczyć wartość math postępujemy następująco:

display-math

Zatem wartość potęgi math może być obliczona przy użyciu 7 mnożeń (podnoszenie do kwadratu to jedno mnożenie).

Oszacujmy, ile mnożeń jest wykonywanych w takim algorytmie dla dowolnego math W tym celu porównajmy binarną reprezentację liczby math z kolejnością wykonywania mnożeń w tym algorytmie, patrząc od prawej do lewej. Nietrudno się przekonać, że z wyjątkiem najbardziej znaczącej pozycji w reprezentacji binarnej wykładnika math każdemu bitowi o wartości 1 odpowiada mnożenie przez math a każdej pozycji odpowiada podniesienie do kwadratu. A zatem, liczba mnożeń potrzebnych do obliczenia wartości math za pomocą powyższego algorytmu jest równa liczbie pozycji w binarnej reprezentacji liczby math minus 1 plus liczba bitów równych 1 w tej reprezentacji także minus 1. Ponieważ, jak wiemy, długość reprezentacji binarnej liczby math wynosi około math liczba mnożeń potrzebnych do obliczenia wartości math wynosi około math

Sprawdźmy, jaki to da efekt, gdy math W tym przypadku math Zatem zamiast czekać math lat, wynik możemy otrzymać wykonując nie więcej niż math mnożeń! To szokujące osiągnięcie naszego algorytmu! Na podstawie tabeli 3 widać, że obliczenie wartości math dla math z setkami cyfr wymaga wykonania kilka tysięcy mnożeń, co na zwykłym komputerze trwa ułamek sekundy.

Przedstawiony algorytm można zapisać w postać rekurencyjnej:

display-math

Algorytmy potęgowania są dobitną ilustracją słów Ralpha Gomory'ego, naukowego szefa firmy IBM: Najlepszym sposobem przyspieszania pracy komputerów jest obarczanie ich mniejszą liczba działań. Czyli prawdziwe przyspieszanie obliczeń osiągamy dzięki efektywnym algorytmom, a nie szybszym komputerom, a w tym konkretnym przypadku, dzięki zastąpieniu algorytmu liniowego przez algorytm o złożoności logarytmicznej.

obrazek

Rys. 5 Geometryczne uzasadnienie faktu, że reszta z dzielenia math przez math jest nie większa niż math

Rys. 5 Geometryczne uzasadnienie faktu, że reszta z dzielenia math przez math jest nie większa niż math

Algorytm Euklidesa a metoda połowienia

Okazuje się, że Euklides był bliski wynalezienia logarytmu, niemal 2000 lat przed tym, jak zrobił to John Napier. Algorytm Euklidesa jest jednym z najstarszych znanych algorytmów. Służy do znajdowania największego wspólnego dzielnika (w skrócie math) dwóch liczb. W tabeli 4 są zamieszczone wyniki obliczeń tego algorytmu w trakcie znajdowania math W ogólności, algorytm Euklidesa generuje następujący ciąg reszt (trzecia kolumna w tabeli 4):

display-math

Te reszty są generowane zgodnie z następującym ciągiem równości:

display-math

i ostatecznie math

Pierwsza równość odpowiada pierwszemu wierszowi w tabeli 4, a ostatnia - ostatniemu wierszowi. Ilorazy math i reszty math w tych równościach spełniają równości:

display-math

Powstaje teraz pytanie, ile kroków wykonuje algorytm Euklidesa, by obliczyć wartość math Pewna sugestia może wyniknąć z porównania liczb w pierwszej i trzeciej kolumnie w tabeli 4. Można zauważyć, że w każdym wierszu liczba w trzeciej kolumnie jest co najmniej dwa razy mniejsza niż liczba w pierwszej kolumnie, a zatem reszta math w równaniu math jest co najmniej dwa razy mniejsza niż math

Uzasadnienie tego faktu jest bardzo proste, jeśli posłużymy się rozumowaniem geometrycznym (Rys. 5).

A zatem, chcemy pokazać, że reszta math z dzielenia math przez math nie jest większa niż math Rozważmy dwa przypadki:

(a)
math - ponieważ reszta nie jest większa niż math więc wynosi co najwyżej math ;
(b)
math - reszta wynosi math a math nie jest większe niż math

A zatem, w ciągu reszt generowanym w algorytmie Euklidesa każda reszta jest co najmniej dwa razy mniejsza niż reszta, która występuje w tym ciągu o dwie pozycje wcześniej. Przypomina to ciąg liczb generowany przez algorytm poszukiwania binarnego, z wyjątkiem tego, że generowany ciąg może być w najgorszym przypadku dwa razy dłuższy. Ważnym wnioskiem jest więc stwierdzenie, że:

Twierdzenie. Algorytm Euklidesa oblicza math dla math w co najwyżej math krokach.

Pewnym wyzwaniem jest pytanie dla jakich liczb math i math algorytm Euklidesa wykonuje największą możliwą liczbę kroków (dwie takie liczby zostały użyte w naszym przykładzie). Odpowiedź może być zaskakująca.

Liczby Fibonacciego

Tendencja do zastępowania algorytmów o złożoności liniowej przez algorytmy o złożoności logarytmicznej, zilustrowana algorytmem szybkiego potęgowaniem, występuje w wielu innych problemach algorytmicznych, np. przy wyznaczaniu wartości liczb Fibonacciego. Klasyczna zależność rekurencyjna, definiująca liczby Fibonacciego, może być wykorzystana do podania algorytmu liniowego, który dodatkowo unika wielokrotnych, takich samych odwołań rekurencyjnych.

Aby otrzymać w tym przypadku algorytm o złożoności logarytmicznej, należy posłużyć się układem dwóch zależności rekurencyjnych, w których indeksy liczb Fibonacciego po prawej stronie są redukowane o około połowę. I ponownie, jak powyżej, by uniknąć wielokrotnych takich samych wywołań rekurencyjnych, należy rekurencję zrealizować jako iterację.

display-math

Więcej na ten temat można znaleźć w książce M.M. Sysło, Piramidy, szyszki i inne konstrukcje algorytmiczne, WSiP 1998; Helion 2015.

Metody podziału i ograniczeń

Wszystkie algorytmy zaprezentowane w tej pracy bazują na idei metody dziel i zwyciężaj. W informatyce istnieje bardzo wiele algorytmów będących realizacją tej idei. We wszystkich przypadkach to podejście algorytmiczne wprowadza do ogólnego wyrażenia na złożoność obliczeniową rozwiązywanego problemu jedynie logarytmiczny czynnik. Tak jest na przykład w przypadku sortowania przez binarne umieszczanie czy sortowanie przez scalanie.