Matematyczne podstawy informatyki

advertisement
Matematyczne podstawy informatyki
1
Rozdział 1. Matematyczne podstawy informatyki
Tytuł ‘matematyczne podstawy informatyki’ jest zwrotem niejednoznacznym. Często
przyjmuje się, iż dotyczy on tych pojęć i wyników matematyki, które są używane w opisie
problemów informatyki. Jednak równie dobrze można przyjąć, że matematyczne podstawy
informatyki są tą dziedziną, która wyjaśnia najważniejsze idee informatyki: czym są algorytmy,
obliczenia, złożoność obliczeniowa. W tym rozdziale skoncentrujemy się na tym pierwszym
znaczeniu, kończąc go tylko bardzo krótką prezentacją ścisłego wyjaśnienia terminu ‘algorytm’.
1.1. Zbiory i relacje
Pojęcie zbioru jest fundamentalnym elementem nowoczesnej matematyki. Korzystając z niego
buduje się cały gmach matematyki złożony z wielu różnorodnych składowych, na przykład analizy
matematycznej, rachunku prawdopodobieństwa, geometrii, topologii. W sposób formalny można
mówić o relacjach zachodzących pomiędzy pewnymi obiektami dopiero wówczas, gdy używamy
teorii zbiorów. Z tych powodów pojęcie zbioru jest ważnym punktem odniesienia także dla
współczesnej informatyki.
1.1.1.
Zbiory
Przyjmuje się, że pojęcie zbioru jest pojęciem pierwotnym. Należy to rozumieć w taki sposób,
iż nie ma żadnej definicji określającej, czym jest zbiór. W zamian przyjmujemy pewne intuicyjne
rozumienie tego terminu, dodatkowo korzystając z własności zadanych poprzez przyjęte bez
żadnego uzasadnienia stwierdzenia o zbiorach zwane pewnikami (aksjomatami) teorii zbiorów
(teorii mnogości).
Zbiory zwykle oznaczamy dużymi literami łacińskiego alfabetu A, B, C ,... ; czasem
wprowadzamy zapis indeksowany A1 , A2 ,..., An . Z kolei elementy zbioru oznaczane są zwykle
małymi literami a, a1 ,..., b, b1 ,... (z indeksami lub bez indeksów). Do wskazania, że pewien element
a należy do zbioru A używamy symbolu  , stąd piszemy a  A .
Najprostszą formą wprowadzania zbioru jest wypisanie jego elementów, w tym celu używamy
nawiasów klamrowych: A  {1,4,8,10} . Jest to niezawodna metoda w przypadku zbioru
o skończonej i niezbyt dużej liczbie elementów. Jeżeli interesują nas zbiory nieskończone lub
przynajmniej bardzo obszerne zapis sprawia więcej kłopotów. Można czasem posługiwać się
zapisem domyślnym A  {1,3,5,7,...} , w tym przypadku nietrudno się domyślić tego, że zbiór A jest
zbiorem liczb naturalnych nieparzystych. Jednak ten sposób bywa zawodny i nieskuteczny. Dlatego
w użyciu pozostaje zapis predykatowy, którego zadaniem jest określenie cechy wyróżniającej
elementy rozważanego zbioru, na przykład A  {x : x jest liczbą naturalną nieparzystą } . Zwykle
taka notacja wykorzystuje wszelkie dostępne konwencje matematyczne: A  {x : x  2n  1, n  } ,
symbol  oznacza tu zbiór liczb naturalnych. Innym przykładem takiej notacji jest
zaprezentowanie zbioru liczb naturalnych będących kwadratami liczb całkowitych jako zbiór
A  {n : n  k 2 , k  } .
Matematyczne podstawy informatyki
2
Zadanie 1.1. Używając standardowej notacji matematycznej podać zapisy predykatowe poniżej
opisanych zbiorów.
a. Zbiór liczb pierwszych.
b. Zbiór liczb wymiernych.
c. Zbiór liczb rzeczywistych będących miejscami zerowymi dowolnego wielomianu
o współczynnikach wymiernych (liczb algebraicznych).
d. Zbiór punktów na płaszczyźnie kartezjańskiej należących do wnętrza koła o promieniu 5
i środku (2,-3).
Poniżej wypiszemy kilka oznaczeń podstawowych operacji na zbiorach wraz z ich
wyjaśnieniem. Suma dwóch zbiorów A, B to nowy zbiór A  B zawierający wszystkie elementy,
które można zaczerpnąć ze zbioru A lub ze zbioru B , A  B  {x : x  A lub x  B} . Z kolei iloczyn
zbiorów A  B zawiera tylko te elementy, które należą równocześnie do zbiorów A i B ,
A  B  {x : x  A i x  B} . Aby określić dopełnienie zbioru należy najpierw podać uniwersum U ,
czyli zbiór wszystkich możliwych elementów, jakie dopuszczamy w toku naszych rozważań.
Wówczas dopełnieniem zbioru A będzie nowy zbiór A' zawierający te elementy zbioru U , które
nie należą do zbioru A . Oznaczając zwrot ‘nie należy’ przez  otrzymamy A'  {x : x  A, x U } .
W sytuacji, gdy wszystkie elementy pewnego zbioru A są równocześnie elementami zbioru B
mówimy, iż A jest podzbiorem B co zapisujemy w poniższy sposób A  B .
Zadanie 1.2. Podać zapis predykatowy sumy, iloczynu, dopełniania (w uniwersum liczb
rzeczywistych) następujących zbiorów.
a. A  {x : x 2  1} , B  {x : x  0.5} .
b.
1.1.2.
A  {x : sin x  0.5} , B  {x : x 4  16} .
Relacje
Każdy opis problemu, którego rozwiązania szuka matematyk lub informatyk wymaga
ustalenia listy obiektów pojawiających się w tym zagadnieniu oraz określenia, jakie relacje
zachodzą pomiędzy nimi. Taka lista obiektów jest zwykle pewnym zbiorem. Natomiast ścisłe
rozważanie wzajemnych stosunków różnych obiektów wymaga wprowadzenia matematycznej
definicji relacji. Chociaż intuicyjnie relację kojarzymy z pewną własnością układu kilku
przedmiotów to okazuje się, iż w matematycznym ujęciu wystarczy ograniczyć się do użycia
pojęcia zbioru.
Warto dodać, że relację pomiędzy obiektami a i b można zwykle wypowiedzieć: „obiekt a
ma cechę b ” lub nawet krócej „ a jest b ”. Dla przykładu: „liczba 2 jest mniejsza od liczby 7”,
„Kain jest starszy niż Abel”. Relacje można więc uważać za matematyczny odpowiednik prostych
zdań języka potocznego.
Pierwszym krokiem w naszym matematycznym zdefiniowaniu pojęcia relacji jest
wprowadzenie pojęcia pary uporządkowanej elementów pochodzących z pewnych zbiorów A i B .
Taką parę zapisujemy dla odróżnienia od normalnego zbioru dwuelementowego w nawiasach
okrągłych: (a, b), a  A, b  B . Należy pamiętać, że kolejność zapisu (inaczej niż w zbiorze) ma
tutaj zasadnicze znaczenie i para uporządkowana (a, b) jest zwykle różna od pary (b, a) . Zbiór
wszystkich par uporządkowanych zbudowanych z elementów zbioru A umieszczonych na
pierwszym miejscu oraz elementów zbioru B umieszczonych na drugim miejscu nazywamy
Matematyczne podstawy informatyki
3
iloczynem kartezjańskim zbiorów A i B . Iloczyn kartezjański zbiorów A i B oznaczamy
A  B  {( a, b) : a  A, b  B} . Teraz pozostaje określić relację pomiędzy elementami zbiorów A
i B jako wybrany podzbiór iloczynu kartezjańskiego A B . Dodajmy, że iloczyn kartezjański
może być zbudowany w oparciu o ten sam zbiór użyty na pierwszym i drugim miejscu A A ,
wówczas zwykle używamy zapisu A2 .
Prostym przykładem konstrukcji tego typu jest określenia relacji mniejszości pomiędzy
elementami zbioru A  {0,1,2,3} jako podzbioru iloczynu kartezjańskiego A2 złożonego
z następujących par uporządkowanych: {( 0,1), (0,2), (0,3), (1,2), (1,3), (2,3)} .
Zadanie 1.3. Zaprezentować poniższe relacje jako podzbiory stosownego iloczynu kartezjańskiego.
a. Relacja mniejszości nieostrej (  ) w zbiorze {0,1,2,3,4,5} .
b. Relacja podzielności w zbiorze {1,2,3,4,5,6,7,8,9,10} .
c. Relacja „ x jest kwadratem y ”, gdzie x należy do zbioru {1,2,3,4,5,6,7,8,9,10} , zaś y do
zbioru {4,3,2,1,0,1,2,3,4} .
Rozważania, w których zamiast pary elementów pojawiałaby się inna liczba elementów
o ustalonym porządku można przeprowadzić w analogiczny sposób. Wówczas zaczynamy od
pojęcia n-tki: (a1 ,..., an ) , w której poszczególne składowe pochodzą odpowiednio z kolejnych
zbiorów a1  A1 ,..., an  An . Zbiór takich n-tek tworzy iloczyn kartezjański A1  A2  ...  An (gdy
wszystkie zbiory są identyczne A  A1  ...  An używamy zapisu An ), wybrane podzbiory takiego
iloczynu kartezjańskiego tworzą relacje n -elementowe.
Zwykle relacje oznacza się dużymi literami alfabetu łacińskiego z zakresu P, Q, R (czasem
z indeksami), wówczas argumenty występują wypisane w nawiasach okrągłych po nazwie relacji,
na przykład P(a, b, c), Q(1,3,0,15) . Jednak w przypadku najczęściej używanych relacji zamiast liter
używane są symbole specjalne: dla relacji mniejszości symbol „<”, dla relacji równości „=”, itd.
W tym przypadku symbole są używane zwykle pomiędzy argumentami, a nie na ich początku raczej 1  3  5 niż  (1,3,5) .
Bardzo ważną własnością relacji jest to, że można je traktować funkcje, których wynikiem jest
jedna z dwóch wartości logicznych: prawda lub fałsz. Tak, więc zamiast mówić, iż relacja 3  2
jest spełniona, zaś relacja 5  7 jest niespełnioną możemy powiedzieć, że relacja większości „  ” dla
argumentów 3,2 (czyli wyrażenie 3  2 ) daje wartość „prawda”, zaś 5  7 daje wartość „fałsz”.
1.2. Podstawy logiki
Poprawne programowanie wymaga znajomości przynajmniej podstawowych elementów
logiki. Sposoby budowania zdań złożonych ze zdań prostych, użycie kwantyfikacji,
przyporządkowywanie wartości logicznej zdaniom złożonym to umiejętności niezbędnie
w praktyce informatyki.
1.2.1.
Spójniki zdaniowe
Często z prostych relacji konstruujemy relacji bardziej złożone. Dla przykładu wystarczy
przypomnieć relację mniejszości nieostrej „  ” zbudowaną przecież w oparciu o relacje
mniejszości i równości. Aby prowadzić ścisłe rozważania w takich sytuacjach należy
Matematyczne podstawy informatyki
4
sformalizować użycie spójników, którymi łączymy proste relacje w bardziej skomplikowane.
Podstawowe konstrukcje zdaniowe budowane są zwykle przy pomocy takich zwrotów jak „i”,
„lub”, „jeśli … to”, „wtedy i tylko wtedy”. Precyzyjne wykorzystanie takich konstrukcji wymaga
jednoznacznego określenia znaczenia występujących w nich spójników. Użyjemy następującej
notacji: zamiast „i” pisany będzie symbol „  ”, zamiast „lub” symbol „  ”, zamiast „jeśli … to”
symbol „  ” oraz zamiast „wtedy i tylko wtedy” symbol „  ”. Dodatkowo fraza „nieprawda, że”
(lub po prostu „nie”) będzie reprezentowana przez symbol „  ”. Podamy poniżej tabele, które
pokażą jak poszczególne spójniki wpływają na wyznaczenie wartości logicznej (czyli prawdy lub
fałszu) całego zdania na podstawie jego składowych.
Jeżeli zdanie ma jako główny spójnik „i” wówczas takie zdanie nazywamy koniunkcją.
Oznaczmy wartość logiczną „prawda” przez P, zaś wartość „fałsz” przez F. Ponadto przyjmijmy,
że pierwsza składowa koniunkcji będzie zapisywana jako p , zaś druga jako q . Przy powyższych
umowach znaczenie spójnika „i” jest wyjaśnione przez tabelę, w której kolejne wiersze pokazują
wartości logiczne zdań prostych wymienionych w nagłówku.
p
pq
q
P
P
P
P
F
F
F
P
F
F
F
F
Powyższą konstrukcję można zilustrować wieloma naturalnymi zdaniami, na przykład
„Tomasz z Akwinu jest świętym i Tomasz z Akwinu jest filozofem”. Wystarczy zapamiętać, że do
prawdziwości koniunkcji wymagana jest prawdziwość obu składowych.
Zdanie ze spójnikiem „lub” nazywamy alternatywą, przy podobnej jak poprzednio notacji
alternatywa opisana jest przez następującą tabelę.
p
q
pq
P
P
P
P
F
P
F
P
P
F
F
F
Powyższa definicja wymaga pewnego wyjaśnienia, mianowicie alternatywa jest prawdziwa,
jeżeli chociaż jedno zdanie do niej należące jest prawdziwe. Tymczasem w języku potocznym
czasem używamy spójnika „lub” w sposób wykluczający: „Jan Chryzostom był Grekiem lub Jan
Chryzostom był Persem”. Takie zdanie sugeruje, że prawdziwość całej alternatywy wymaga, aby
dokładnie jedna ze składowych alternatywy była prawdziwa. W sensie powyższej, standardowej w
logice, definicji sytuacja jest inna – zdanie „Będę w filharmonii lub będę w teatrze” jest prawdziwe
nawet wówczas, gdy autor wypowiedzi będzie zarówno w filharmonii i teatrze.
Kolejna tabela pokazuje wartości logiczne dla zdania postaci „jeśli … to”, ten typ zdań
nazywamy implikacją.
p
pq
q
P
P
P
P
F
F
F
P
P
Matematyczne podstawy informatyki
5
F
F
P
Konstrukcja opisanej powyżej implikacji jest, wbrew pozorom, nie całkiem oczywista. Logicy
znają kilka różnych typów implikacji, powyższy wariant nazywa się implikacją materialną. Cechą
szczególną implikacji materialnej jest jej prawdziwość zawsze, gdy zdanie po „jeśli” (poprzednik
implikacji) jest fałszywe, bez względu na wartość zdania po „to” (następnika implikacji). To
oznacza, że zdanie „jeśli pada deszcz to jestem w kinie” musimy uznać za prawdziwe zawsze, gdy
deszcz nie pada - bez względu na miejsce w którym znajduje się jego autor.
Pozostaje nam jeszcze opisać przypadek równoważności, czyli zdania ze spójnikiem „wtedy
i tylko wtedy”.
p
pq
q
P
P
P
P
F
F
F
P
F
F
F
P
Równoważność jest konstrukcją, w której wartość prawda występuje tylko wtedy, gdy oba
zdania nią połączone są równocześnie prawdziwe lub równocześnie fałszywe.
Nieco inny przypadek prezentuje negacja, to jest zdanie poprzedzone frazą „nieprawda, że”.
Tutaj nie odwołujemy się do dwóch zdań składowych (jak we wszystkich powyżej opisanych
przypadkach), ale używamy tylko jednego zdania, któremu zaprzeczamy.
p
p
P
F
F
P
Jak widać, negacja (zaprzeczenie) daje zawsze przeciwną wartość logiczną do zdania, które
jest w niej zaprzeczane.
Oczywiście w praktyce rzadko zdarza się, aby zdanie było zbudowane dokładnie według
któregoś z powyższych przykładów. Zwykle mamy do czynienia z pewną kombinacją różnych
typów zdań. Dla przykładu zdanie „jeżeli nie pada deszcz i świeci słońce to idziemy na spacer” jest
implikacją, której pierwsza składowa jest koniunkcją dwóch zdań. Z kolei pierwsze zdanie tej
koniunkcji jest negacją. Symboliczny zapis formy takiego zdania wyglądałby następująco:
(p  q)  r . W takich przypadkach analiza możliwych wartości logicznych polega na rozbijaniu
zdania na struktury prostsze i kolejne wyznaczanie wartości dla coraz bardziej skomplikowanych
fragmentów zdania.
p
p
p  q
q
(p  q)  r
r
P
P
P
P
F
F
F
F
F
F
F
F
P
P
P
P
P
P
F
F
P
P
F
F
F
F
F
F
P
P
F
F
P
F
P
F
P
F
P
F
P
P
P
P
P
F
P
P
Matematyczne podstawy informatyki
6
Zadanie 1.4. Zbudować tabele prawdziwościowe dla poniżej podanych schematów zdań.
a. ( p  q)  ( p  q) .
b. (( p  q)  (q  s))  ( p  s) .
c. (( p  q)  q)  p .
d. (( p  q)  p)  q .
e. (( p  q)  q)  p .
1.2.2.
Kwantyfikatory
Wyznaczanie wartości logicznej niektórych zdań (raczej należałoby w tym przypadku
powiedzieć form zdaniowych) jest niemożliwe ze względu na użycie w nich zmiennych (takich jak
x, y, z ) lub nazw ogólnych „człowiek”, „osoba”, „przedmiot” zamiast nazw jednostkowych.
Wyznaczenie wartości zdania „Jan Kowalski jest wysoki” jest tylko pochodną znajomości Jana
Kowalskiego, podczas gdy zdanie „Człowiek jest wysoki” nie daje nam żadnej szansy odpowiedzi,
o kogo chodzi w tym zdaniu i ustalenia jego wartości logicznej. Jednym ze sposobów ustalenia
wartości logicznej jest zastąpienie zmiennej przez pewną stałą, reprezentującą konkretny
przedmiot. Na przykład wyrażenie „ x  2 ” można przekształcić w zdanie prawdziwe „ 5  2 ” przez
podstawienie za x liczby 5.
Powyższa metoda nie jest wystarczająca do opisania wielu istotnych zjawisk. Pozwalałaby ona
na szukanie prawdy lub fałszu wyłącznie w wyrażeniach, które nie zawierają zmiennych, co jest nie
do przyjęcia zwłaszcza w kontekście matematyki i informatyki.
Powyższy problem jest rozwiązany przez wprowadzenie zwrotów określających zakres
zmiennych występujących w rozważanych wyrażeniach. Zwykle wystarczą dwa takie zwroty: „dla
każdego …” – nazywany kwantyfikatorem ogólnym oraz „istnieje …” – nazywany kwantyfikatorem
szczegółowym. Do zapisu tych kwantyfikatorów używa się odpowiednio symboli  oraz  . Stąd
zdanie „każda liczba jest większa od zera” można zapisać x( x  0) , a zdanie „istnieje liczba
będąca rozwiązaniem równania x 2  x  2  0 ” jako x( x 2  x  2  0) .
Dodajmy do powyższych rozważań dwa dodatkowe wyjaśnienia. Skoro kwantyfikator ogólny
mówi o wszystkich przedmiotach opisywanych przez wyrażenie można wyobrażać sobie, iż zapis
używający tego kwantyfikatora jest skrótem koniunkcji (często nieskończonej) przebiegającej
przez wszystkie dopuszczone elementy. Na przykład, ograniczając uwagę do liczb całkowitych
wyrażenie x( x 2  0) odpowiada koniunkcji (0 2  0)  ((1) 2  0)  (12  0)  ... z nieskończoną
ilością składowych. Podobnie kwantyfikator szczegółowy może być pojmowany jako alternatywa.
Wówczas x( x  100) staje się alternatywą (0  100)  (1  0)  (1  0)... o nieskończonej ilości
składowych.
Czasem zdarza się, że chcemy użyć kwantyfikatora, w którym zmienne są dobierane
z pewnego podzbioru wszystkich dopuszczalnych wartości. Mówimy wówczas, że w użyciu są
kwantyfikatory ograniczone. Warto wiedzieć, że taką sytuację można zawsze przekształcić do
postaci, w której korzystamy tylko ze zwykłych kwantyfikatorów. Rozpatrzmy przykładowe zdanie
„każda liczba całkowita mniejsza od zera ma tą własność, iż jej kwadrat jest większy od zera”.
Pierwsza możliwość zapisu formalnego wygląda następująco: (x  0)[ x 2  0] , jednak można użyć
wyłącznie kwantyfikatora ogólnego pisząc: x[( x  0)  ( x 2  0)] . Ogólnie mówiąc, każdy
przypadek ograniczonego kwantyfikatora ogólnego: x spełniającego warunek zachodzi (…) –
można zapisać jako kwantyfikator ogólny obejmujący implikację: x [warunek  (…)]. Nieco
Matematyczne podstawy informatyki
7
inna jest zasada przekształcania ograniczonego kwantyfikatora szczegółowego. Zamiast pisać
1
1
(x  0)[  2] wystarczy użyć zwykłego kwantyfikatora szczegółowego x[( x  0)  (  2)]. To
x
x
oznacza, że w tym przypadku warunek ograniczający kwantyfikator jest włączony do wyrażenia
pod zwykłym kwantyfikatorem szczegółowym jako składowa koniunkcji.
Zadanie 1.5. Używając kwantyfikatorów zaprezentować w postaci symbolicznej następujące
stwierdzenia.
a. Dla każdej liczby naturalnej można znaleźć liczbę od niej większą.
b. Każdy dzielnik dowolnego dzielnika zadanej liczby naturalnej jest jej dzielnikiem.
c. Pierwiastek kwadratowy dowolnej liczby pierwszej jest liczbą niewymierną.
1.2.3.
Równoważności i tautologie
Jednym z głównych celów logiki jest pomoc w precyzyjnym rozumowaniu i formułowaniu
wypowiedzi. Dlatego ważne staje się ustalenie, jakie formuły logiczne mają to dokładnie to samo
znaczenie. Ponieważ nie jest łatwo rozstrzygać kwestię znaczenia wyrażeń logicznych główną
metodą pozostaje badanie wartości logicznych. To oznacza, że zajmujemy się badaniem, kiedy dwa
zdania logiczne przy tych samych wartościach narzuconych odpowiadającym sobie składowym
mają tą samą wartość. Kontynuując ten kierunek myślenia wprowadza się pojęcie równoważności
logicznej. Dwa zdania logiczne są sobie równoważne, jeżeli zawsze przy takich samych
wartościach logicznych zdań składowych posiadają one tą samą wartość logiczną jako całość.
Zwykle jako symbol równoważności przyjmuje się znak „  ”.
Podamy teraz kilkanaście głównych równoważności dotyczących zdań logicznych.
 p  q  q  p - koniunkcja jest przemienna;

p  q  q  p - alternatywa jest przemienna;

p  q  q  p - równoważność jest przemienna;


p  q  q  p - implikacji dwóch zdań odpowiada implikacja zdań przestawionych i
zanegowanych (prawo kontrapozycji);
( p  q)  p  q - negacja koniunkcji to alternatywa negacji zdań składowych;

( p  q)  p  q - negacja alternatywy to koniunkcja negacji zdań składowych;

( p  q)  p  q - implikacja jest fałszywa tylko wtedy, gdy prawdziwy jest jej
poprzednik i fałszywy następnik;
p  q  p  q - implikacja jest prawdziwa wtedy, gdy jest poprzednik jest fałszywy lub
następnik prawdziwy;
p  q  ( p  q)  (q  p) - równoważność to koniunkcja implikacji w obie strony.


Dopuszczając zmienne i kwantyfikatory w wyrażeniach logicznych można podać kolejne ważne
równoważności.
 xP( x)  yP( y ), xP( x)  yP( y ) - jeżeli w wyrażeniu P nie występuje pewna nowa
zmienna (na przykład y ), to można wszystkie wystąpienia innej zmiennej (na przykład x )
zastąpić tą nową zmienną;
Matematyczne podstawy informatyki
8

xP( x)  xP( x) - negacja kwantyfikatora ogólnego to kwantyfikator szczegółowy
zastosowany do zanegowanego wyrażenia pod kwantyfikatorem (analogia do negacji
koniunkcji);
 xP( x)  xP( x) - negacja kwantyfikatora szczegółowego to kwantyfikator ogólny
zastosowany do zanegowanego wyrażenia pod kwantyfikatorem (analogia do negacji
alternatywy);
 x( P( x)  Q( x))  xP( x)  xQ( x) - kwantyfikator ogólny można wpisywać na zewnątrz
i wewnątrz koniunkcji;
 x( P( x)  Q( x))  xP( x)  xQ( x) - kwantyfikator szczegółowy można wpisywać na
zewnątrz i wewnątrz alternatywy.
Dla przykładu zilustrujmy drugą z wypisanych powyżej formuł następującymi zdaniami
równoważnymi: „nieprawda, że każda liczba naturalne jest parzysta”, „istnieje nieparzysta liczba
naturalna”.
Zadanie 1.6. Przekształcić poniższe wyrażenia eliminując znak negacji.
a. n  Nm  N (m  n) .
b. x[( x 2  0)  ( x  0)] .
Chociaż w niektórych przypadkach nie można mówić o równoważności formuł, jednak
zachodzi słabszy związek pomiędzy nimi: związek konsekwencji. Formuła P jest konsekwencją
formuły Q jeżeli, zawsze gdy przy pewnym ustaleniu wartości składowych Q jest prawdziwe
także i P przy tym ustawieniu wartości składowych jest prawdziwe. Ważnymi przykładami takiej
mogą być poniższe formuły.
 x( P( x)  Q( x)) jest konsekwencją x( P( x)  xQ( x) ;

xP( x)  Q( x) jest konsekwencją x( P( x)  Q( x)) .
Tautologie są to zdania zawsze prawdziwe, czyli zdania, które przy dowolnym wartościowaniu
składowych będą prawdziwe. Okazuje się, że pomiędzy wypisanymi powyżej parami formuł
równoważnych a tautologiami istnieje prosty związek. Jeżeli dwie formuły równoważne połączymy
funktorem równoważności  wówczas zawsze otrzymujemy tautologię. W ten sposób
otrzymujemy metodę do sprawdzania, czy dwie formuły są równoważne.
Zadanie 1.7. Udowodnić przy użyciu tabel prawdziwościowych powyżej wypisane równoważności
sformułowane bez kwantyfikatorów.
Podobne związki można sformułować w przypadku, gdy jedna z formuł jest konsekwencją
drugiej. Wówczas łącząc te formuły funktorem implikacji  także otrzymamy formułę
tautologiczną.
Zadanie 1.8. Podać naturalne przykłady pokazujące, że wskazane przykłady konsekwencji nie
mogą być wzmocnione do równoważności.
Matematyczne podstawy informatyki
1.2.4.
9
Logika a programowanie
Należy dodać w tym miejscu kilka uwag o specyficznych cechach formuł logicznych
wykorzystywanych w programowaniu. Skoncentrujmy się tu na przykładach koniunkcji oraz
alternatywy.
Rozważmy najpierw formułę p  q . Nietrudno zauważyć, że w przypadku, gdy pierwsza
składowa p jest prawdziwa, także cała formuła musi być prawdziwa. To podsuwa prostą metodę
uproszczonego sprawdzania prawdziwości alternatywy: jeżeli pierwsza składowa jest prawdziwa
jako wynik przyjmujmy prawdę (bez sprawdzania drugiej), jeżeli pierwsza składowa jest fałszywa
wynikiem jest wartość drugiej składowej. Taki sposób ma dwie zalety: zmniejsza ilość operacji
wykonywanych przez komputer oraz umożliwia pomijanie w niektórych przypadkach obliczeń
1
mogących prowadzić do błędów wykonania. Na przykład: ( x  0)  (  2) dla x równego zera
x
otrzyma wartość prawda tylko na podstawie warunku x  0 , co na szczęście oszczędzi próby
dzielenia przez zero. Tego rodzaju sytuacja niesie jednak dodatkowa poważną konsekwencję. Otóż
opisana powyżej informatyczna alternatywa traci własność przemienności. Wyraźnie to widać dla
przykładowej formuły – odwrócenie kolejności spowoduje błąd wykonania.
Podobne rozumowanie można przedstawić dla koniunkcji p  q . W tym przypadku fałsz
zdania p wystarcza do zdecydowania o fałszywości całej formuły. Gdyby zaś pierwsza składowa
była prawdziwa, wówczas wartość logiczna koniunkcji jest identyczna z wartością drugiej
składowej q .
Zadanie 1.9. Podać po dwa przykład koniunkcji i alternatywy, w których przestawienie kolejności
można doprowadzić do wykonania niedozwolonych operacji matematycznych (jak dzielenie przez
zero, odwoływanie się do nieistniejących wyrazów skończonych ciągów itp.).
1.3. Język i gramatyka
Programowanie polega na wykorzystaniu pewnego sztucznego języka do opisu czynności,
które mają być wykonane przez komputer. Aby tak skonstruowany program działania był
zrozumiały dla kompilatora musi on precyzyjnie spełniać reguły określające gramatykę
i słownictwo wybranego przez nas języka programowania. Jednak to oznacza, że w odróżnieniu od
języków naturalnych reguły gramatyczne (składniowe) języków programowania muszą być
określone w matematycznie ścisły sposób. Zagadnieniem opisu oraz własności sztucznych języków
(takich jak języki programowania) zajmuje się lingwistyka matematyczna. Poniżej przedstawimy
jej kilka podstawowych pojęć.
1.3.1.
Alfabet i zmienne
Pierwszym zadaniem, które należy rozwiązać przy formalnym opisie języka programowania
jest wyliczenie podstawowych elementów, z których można budować programy. Pełny spis takich
elementów nazywa się alfabetem języka programowania. Należy zwrócić uwagę na fakt, że alfabet
może zawierać elementy różnego rodzaju. Będą wśród nich litery i cyfry oraz inne symbole,
z których będzie można budować identyfikatory (nazwy) wykorzystywane w tworzonych przez nas
programach. Muszą tam znaleźć się także symbole specjalne. Chociaż są one czasem zapisywane
z pomocą kilku znaków powinny być traktowane jako niepodzielne, pierwotne elementy języka (na
Matematyczne podstawy informatyki
10
przykład: >=, <=, := ). Podobne zjawisko występuje przy słowach, które opisują główne
konstrukcje języka (iteracje, selekcje). Nie mogą one być traktowane jako zbudowane przez nas
identyfikatory, ale muszę stanowić niepodzielne słowa kluczowe (while, if, then, …).
Kolejnym elementem opisu języka są zmienne. Ich celem jest wskazanie na całą kategorię
wyrażeń. Na przykład, w sytuacji, gdy chcemy mówić o dowolnej instrukcji warunkowej, musimy
użyć pewnego zapisu denotującego cały zbiór możliwych do zaprogramowania instrukcji
warunkowych. Spośród wielu konwencji oznaczenia takich zmiennych wybierzemy tutaj zapis w
nawiasach trójkątnych. Wówczas niektóre ze zmiennych mogłyby wyglądać następująco:
<instrukcja_warunkowa>, <wyrażenie_arytmentyczne>, <identyfikator>.
1.3.2.
Reguły i ich stosowanie
Najważniejszą składową określającą budowę języka są reguły składniowe. Pokazują one
wzajemne zależności pomiędzy poszczególnymi elementami języka. Można je formułować na
wiele sposobów, przy czym zapis ma związek ze stopniem złożoności i skomplikowania języka
programowania. Ponieważ podstawowa część większości języków programowania daje się opisać
poprzez wyrażenia nie odwołujące się do kontekstu wystąpienia frazy, a tylko poprzez możliwy
sposób zamiany, dlatego będziemy tu używali reguł nazywanych bezkontekstowymi. Taka reguła
ma następującą strukturę: po lewej stronie wypisujemy zmienną, która reprezentuje opisywany
zbiór napisów, po prawej stronie występuje grupa zmiennych i stałych z alfabetu, które pokazują
jak można zastąpić zmienną z lewej strony reguły. Obie strony reguły są rozdzielane specjalnym
symbolem, my będziemy używali w tym miejscu symbolu „::==”. Oto kilka przykładów
sformułowań różnych reguł.
<instrukcja_warunkowa>::== if <warunek_logiczny> then <instrukcja> else <instrukcja>;
<instrukcja_warunkowa>::== if <warunek_logiczny> then <instrukcja>;
<wyrażenie_arytmetyczne>::==<identyfikator>;
<wyrażenie_arytmetyczne>::==<liczba>;
<wyrażenie_arytmetyczne>::==<wyrażenie_arytmetyczne>+<wyrażenie_arytmetyczne>;
Dodajmy do powyższego opisu prostą i często użyteczną konwencję. Jeżeli ta sama zmienna
ma kilka wariantów opisu wówczas zamiast wypisywać je jedne pod drugimi możemy pisać je
obok siebie rozdzielając je znakiem „|”. Wówczas jeden z powyższych przykładów można
przekształcić w następujący sposób.
<instrukcja_warunkowa>::== if <warunek_logiczny> then <instrukcja> else <instrukcja>;
| if <warunek_logiczny> then <instrukcja>;
Warto zwrócić uwagę na jeszcze jedną cechę reguł składniowych. Mianowicie, można w nich
używać samoreferencji, to znaczy w opisie pewnej zmiennej może występować ta sama zmienna.
Tak, więc <wyrażenie_arytmetyczne> jest albo liczbą albo zmienną liczbową, ale w bardziej
skomplikowanych przypadkach może też być sumą, różnicą, iloczynem, ilorazem pewnych
podwyrażeń arytmetycznych. Należy jednak pamiętać o tym, aby zawsze przynajmniej jeden
w wariantów opisu zmiennej pozwalał ją zinterpretować bez odwoływania się do niej samej, co
umożliwi uniknięcie błędnego koła struktury składniowej.
Matematyczne podstawy informatyki
1.3.3.
11
Gramatyka bezkontekstowa
Pełną strukturę opisującą wybrany język formalny nazywamy gramatyką. Taki opis musi
zawierać wyliczenie używanych stałych językowych (alfabet), zmiennych występujących w języku
oraz reguły składniowe. Jeżeli reguły są w postaci bezkontekstowej, wówczas gramatykę
nazywamy gramatyką bezkontekstową. Także język opisywany przez taką gramatykę jest określany
jako język bezkontekstowy. Podajmy poniżej krótki przykład pokazujący pewną gramatykę
bezkontekstową.
Alfabet: 0,1, +, -, *, /,(,).
Zmienne: <liczba_binarna>, <wyrażenie_binarne>.
Reguły:
<liczba_binarna>::==0 | 1 | 0<liczba_binarna> | 1<liczba_binarna>
<wyrażenie_binarne>::==
<liczba_binarna> | (<wyrażenie_binarne>) |
<wyrażenie_binarne>+<wyrażenie_binarne> |
<wyrażenie_binarne>-<wyrażenie_binarne> |
<wyrażenie_binarne>*<wyrażenie_binarne> |
<wyrażenie_binarne>/<wyrażenie_binarne>
Z tak podanymi regułami możemy teraz dokonywać wyprowadzeń pewnych napisów
będących albo liczbami binarnymi albo wyrażeniami. W każdym kroku wyprowadzenia należy
użyć jednej z reguł, końcem wyprowadzenia będzie otrzymanie napisu bez żadnej zmiennej
(poszczególne etapy wyprowadzenia będziemy oddzielali symbolem „->”.
<liczba_binarna> -> 1<liczba_binarna> -> 10<liczba_binarna> -> 101
<wyrażenie_binarne> -> <wyrażenie_binarne>*<wyrażenie_binarne> ->
(<wyrażenie_binarne>)*<wyrażenie_binarne> ->
(<wyrażenie_binarne>+<wyrażenie_binarne>)*<wyrażenie_binarne> ->
(<liczba_binarna>+<wyrażenie_binarne>)*<wyrażenie_binarne> ->
(<liczba_binarna>+<liczba_binarna>)*<wyrażenie_binarne> ->
(<liczba_binarna>+<liczba_binarna>)*<liczba_binarna> ->
(1+<liczba_binarna>)*<liczba_binarna> -> (1+1<liczba_binarna>)*<liczba_binarna> ->
(1+10)*<liczba_binarna> -> (1+10)*1<liczba_binarna> -> (1+10)*11<liczba_binarna> ->
(1+10)*111
Jak widać, posiadając gramatykę można ją wykorzystywać do wyprowadzania (generowania)
napisów będącymi poprawnymi wyrażeniami języka opisywanego przez tą gramatykę.
W przypadku informatyki oznacza to możliwość wyprowadzania poprawnych składniowo
programów komputerowych. Ważniejszym zastosowaniem jest jednak zwykle wykorzystywanie
gramatyk w innym celu. Mianowicie, posiadając pewien zapis chcemy sprawdzić (osobiście lub
z użyciem komputera) czy jest to fragment wybranego języka opisanego przez pewną gramatykę.
Ten proces analizy napisów jest jednym z etapów pracy kompilatorów.
Dodajmy jeszcze, że cały powyższy wywód dotyczący gramatyk, mimo swoistego zapisu jest
w gruncie rzeczy fragmentem matematyki wykorzystującym wyłącznie pojęcia zbioru i relacji.
Matematyczne podstawy informatyki
12
Zadanie 1.10. Używając poniższych gramatyk wyprowadzić podane słowa.
a. <palindrom>::==a | b | aa | bb,
<palindrom>::==a<palindrom>a,
<palindrom>::==b<palindrome>b,
wyprowadzane słowo: ababbaba.
b. <suma>::==<skladnik>+<skladnik>,
<skladnik>::==<zmienna>*(<suma>) | <zmienna>,
<zmienna>::==x | y | z,
wyprowadzane słowo: x*(x+y)+y*(z*(x+y)+y).
Zadanie 1.11. Podać gramatyki opisujące poniżej wyliczone języki wyrażeń.
a. Język słów postaci: {ab,aabb,aaabbb,aaaabbbb,…}.
b. Język słów postaci: {x+y,x*x+y*y,x*x*x+y*y*y,…}.
1.4. Maszyna Turinga
Gdy dochodzi do analizowania podstawowych własności algorytmów pojawia się potrzeba
ścisłego i pełnego matematycznego opisu komputerów. Ponieważ istnieje wiele rodzajów
komputerów różniących się znacznie architekturą i językiem wewnętrznym, to stosowny model
matematyczny musi być bardzo ogólny. Co więcej wskazane byłoby, aby model ten oddawał,
przynajmniej w przybliżeniu, aktywność obliczającego w sposób mechaniczny człowieka.
Najczęściej rozważaną strukturą matematyczną, która spełnia te warunki jest maszyna Turinga.
1.4.1.
Budowa maszyny Turinga
Każdy model komputera musi zawierać pamięć: strukturę pozwalającą na wpisywanie danych
początkowych, przechowywanie danych podczas obliczeń oraz odczytanie wyniku obliczeń.
W przypadku maszyny Turinga pamięć można wyobrazić sobie jako nieskończoną taśmę papieru
podzieloną na równe komórki, w których można zapisywać i wymazywać pojedyncze symbole
dopuszczane w czasie obliczeń.
Matematyczna konstrukcji tej części maszyny Turinga polega na wskazaniu zbioru symboli 
używanych podczas obliczeń, zwanego alfabetem maszyny Turinga oraz na wprowadzeniu relacji
funkcyjnej wiążącej każdą komórkę taśmy z zapisanym w niej symbolem. Jeżeli komórki taśmy
ponumerujemy liczba całkowitymi, przy czym 0 będzie numerem dowolnie wybranej komórki,
ujemnie indeksowane komórki będą umieszczone kolejno na lewo od niej, zaś dodatnio
indeksowane będą położone na prawo, wówczas w każdej chwili działania można taśmę
reprezentować jako przyporządkowanie T :    ,  - zbiór liczb całkowitych. Dodajmy, że
w zbiorze symboli musi występować symbol specjalny  oznaczający pustą, niezapisaną komórkę.
...
-2
1
0
1
-1
0
1
...
2
3
Kolejny element maszyny Turinga to mechanizm odczytu-zapisu danych znajdujących się na
taśmie. Zwykle jest on wyobrażany jako głowica poruszająca się ponad taśmą. W jednym kroku
swojego działania głowica odczytuje symbol zapisany w komórce znajdującej się pod głowicą,
podczas odczytu zawsze symbol odczytany jest wymazany, następnie komórka jest wypełniona
Matematyczne podstawy informatyki
13
jednym z dopuszczalnych symboli (może wystąpić także  ), na zakończenie głowica przesuwa się
o jedną komórkę w lewo lub w prawo. Jeśli obecny stan taśmy oznaczymy przez T , obecnie
obserwowana przez głowicę komórka ma indeks i , zapisywany symbol ma wartość s , zaś stan
T ( j ) j  i,
taśmy po wykonaniu kroku to T ' , wówczas dla dowolnego j otrzymamy T ' ( j )  
 s j  i.
Dodajmy, że w zależności od kierunku ruchu głowicy, jej nowe położenie i ' jest równe i 1 albo
i  1.
q2
...
1
0
1
...
Ostatnią składową maszyny Turinga jest stan (lub tryb) jej pracy. Chodzi o zabezpieczenie
możliwości różnej reakcji maszyny na tą samą zawartość komórki. Dlatego maszyna ma
zdefiniowany pewien zbiór stanów Q i w każdej chwili swojej pracy znajduje się w jednym ze
stanów wybranych ze tego zbioru. W tym zbiorze wyróżnione są dwa elementy. Jeden z nich
(często oznaczany q 0 ) jest stanem inicjalnym, w którym maszyna zaczyna swoją pracę. Drugi
(zwykle q f ) jest stanem końcowym. Jeżeli maszyna znajdzie się w tym stanie, to kończy wszelką
aktywność, zaś bieżąca zawartość taśmy uważana jest za wynik obliczeń.
1.4.2.
Program maszyny Turinga
Maszyna Turinga musi być wyposażona w program jej działania. Taki program nie wyznacza
znaczenia jej pracy – to jest pozostawione intelektowi programisty, natomiast precyzyjnie
pokazuje, jakie działania maszyna ma wykonywać w zależności od jej bieżącej konfiguracji. W
zgodzie z powyższym opisem obserwowana sytuacja zawiera dwie informacje: jaki symbol
znajduje się pod głowicą i jaki jest bieżący stan maszyny. Z kolei wskazówki dalszego działania
muszą zwierać trzy składowe: jaki nowy symbol ma zostać zapisany, jaki nowy stan ma przyjąć
maszyna, w którą stronę ma ruszyć się głowica. Program musi więc składać się z pojedynczych
instrukcji postaci: jeśli bieżący stan to q oraz bieżący symbol s to zmień stan na q ' , zapisz symbol
s ' oraz dokonaj ruchu r (albo w prawo „  ” albo w lewo „  ”). W zapisie symbolicznym
program staje się wówczas zbiorem instrukcji [q, s ]  [q' , s' , r ] . Poniżej można przeczytać zbiór
instrukcji zwiększający zapisaną na taśmie liczbę binarną o jeden (obliczający następnik binarny):
[ p,0]  [ p,0, ] ,
[ p,1]  [ p,1, ] ,
[ p, ]  [ z, , ] ,
[ z ,1]  [ z ,0, ] ,
[ z ,0]  [ f ,1, ] ,
[ z , ]  [ f ,1, ] .
Dodajmy, że zwykle stany mają pewną naturalną interpretację. W tym przypadku stan p jest
zarówno stanem początkowym jak i stanem przesuwania głowicy na koniec słowa, stan z jest
stanem zmiany cyfr liczby binarnej, stan f jest stanem finalnym, w którym maszyna kończy pracę.
Matematyczne podstawy informatyki
1.4.3.
14
Działanie maszyny Turinga
Pozostało nam jeszcze określić sposób, w jaki maszyna Turinga dokonuje obliczeń. Sam
mechanizm działania jest bardzo prosty. Praca maszyny Turinga zaczyna się zawsze w stanie
początkowym, przy czym głowica znajduje się na początku zapisanych na taśmie danych.
W każdym kroku wybierana jest jedna instrukcja, która pasuje do bieżącej zawartości komórki pod
głowicą i stanu. Następnie dokonywana jest zmiana symbolu, stanu oraz ruch głowicy. Kroki
powtarzana są tak długo, aż maszyna znajdzie się w stanie finalnym. W tej sytuacji maszyna
kończy pracę, a zawartość niepustych komórek taśmy uważamy za wynik. Gdyby maszyna w toku
działania nie doszła do stanu finalnego, wówczas mówimy o „zapętleniu” maszyny i przyjmujemy,
że wynik jest nieokreślony.
Powyższe wyjaśnienia zilustrujemy pełnym opisem zasygnalizowanej powyżej maszyny
Turinga M obliczającej następnik liczby binarnej.
Maszyna M będzie używała alfabetu: {0,1, } , w zbiorze Q znajdują się następujące stany
{ p, z , f } , przy czym stanem początkowym jest p , zaś stanem końcowym stan f . Program
wygląda tak jak powyżej, dla wygody opisu ponumerujemy poszczególne instrukcje:
1. [ p,0]  [ p,0, ] ,
2. [ p,1]  [ p,1, ] ,
3. [ p, ]  [ z, , ] ,
4. [ z ,1]  [ z ,0, ] ,
5. [ z ,0]  [ f ,1, ] ,
6. [ z , ]  [ f ,1, ] .
Prześledźmy działanie maszyny dla danych 101, przyjmijmy przy tym notację, w której
symbol znajdujący się pod głowicą będzie nadpisany kreską. Należy także pamiętać, że dane
początkowe otoczone są z lewej i prawej strony nieskończoną ilością pustych komórek
(zawierających  ).
Rozpoczynamy w stanie p , zaś dane wyglądają następująco: 1 01 . Po wykonaniu instrukcji 2
otrzymamy zawartość taśmy 1 0 1 , następnie instrukcja 1 da nam 10 1 i ponownie zastosowana
instrukcja 2 (uwzględniając pustą komórkę po prawej stronie słowa 101) doprowadzi do sytuacji
101 , przy czym stan cały czas pozostawał równy p . Dopiero w tej chwili użycie instrukcji 3
zmieni stan na z oraz sytuację na taśmie na 10 1 . W tym przypadku należy zastosować instrukcję
4, jej skutkiem będzie pozostawienie tego samego stanu z i następująca zmiana danych 1 0 0 .
Ostatnią instrukcją wykonaną instrukcją będzie instrukcja 5, przeprowadzi ona maszynę w stan
finalny f , wyznaczając jako wynik na taśmie 1 10 , pozycja głowicy przy odczytywaniu wyniku
nie ma żadnego znaczenia. Startując z danymi 101, maszyna wyznaczyła 110 , co jest poprawnym
wynikiem dla obliczenia następnika liczby binarnej.
Dla wygody, aktywność maszyny Turinga można przedstawić jako ciąg par: stan, zawartość
taśmy, taki ciąg nazywamy zapisem obliczenia maszyny Turinga dla zadanych danych. Wówczas
zapis obliczenia maszyny M dla danych 101 wygląda następująco:
( p,1 01)  ( p,101)  ( p,10 1)  ( p,101 )  ( z,10 1)  ( z,100)  ( f ,1 10) .
Zadanie 1.12. Podać zapisy obliczeń powyższej maszyny Turinga dla danych: 1111, 0111,
10101111.
Matematyczne podstawy informatyki
15
Zadanie 1.13. Skonstruować programy maszyn Turinga dokonujących poniżej opisanych operacji.
a. Dla danych będących zapisami liczb binarnych maszyna oblicza poprzednik; przyjmujemy,
iż poprzednikiem 0 jest także 0.
b. Dla danych będących słowami zbudowanymi z symboli a,b maszyna kończy pracę
pozostawiając symbol T na taśmie, jeżeli słowo było palindromem; pozostawiając N w
przeciwnym przypadku.
c. Dla dwóch zapisów liczb binarnych oddzielonych znakiem + maszyna drukuje na taśmie
zapis binarny sumy tych dwóch liczb.
Na zakończenie dodajmy, że podany powyżej opis maszyny Turinga stanowi matematyczną
definicję algorytmu. Algorytmem nazywamy każdy sposób prowadzenia obliczeń, który da się
zapisać za pomocą maszyny Turinga. Co więcej, taka definicja pozwala wyprowadzić wiele
ważnych twierdzeń dotyczących istotnych kwestii:
 jakie problemy dają się algorytmicznie rozwiązać (zagadnienia rozstrzygalności),
 które spośród znanych algorytmów są szczególnie trudne do obliczenia (zagadnienia
złożoności).
Download