- 14 maja 2024
- Posted by: Daria
- Category: Baza wiedzy
Zadanie 1 – Cyfrowe dopełnienie
Zacznijmy od przykładów liczb (nazwanych n) i ich cyfrowych dopełnień (nazwanych d). Zgodnie z założeniem najbardziej znacząca cyfra (a więc pierwsza od lewej) ma zawierać się w przedziale od 1 do 8. Jest to pierwszy i główny wymóg. Należy tu pamiętać, że cyfry 0 i 9 mogą pojawiać się w dalszej części liczby.
Prawidłowe liczby n będą więc wyglądać w ten sposób: 1, 8000, 545454 czy 89999.
Cyfrowe dopełnienie stanowi taka liczba d dla której suma kolejno każdej cyfry da 9, najłatwiej zobrazować to na przykładzie:
Liczba n: | 12345 | 100 | 545454 |
Dopełnienie: | 87654 | 899 | 454545 |
Suma: | 99999 | 999 | 999999 |
Rozumiejąc sposób tworzenia liczb n i d, możemy przystąpić do właściwych zadań.
Zadanie 1.1.
Naszym zadaniem jest znalezienie przypadków granicznych dla czterocyfrowej liczby n: maksimum i minimum wartości bezwzględnej różnicy liczby n i dopełnienia d.
Brzmi to dość niepokojąco, jednak rozbijmy to na części pierwsze, a wszystko stanie się jasne.
Wartość bezwzględna (oznaczana w zapisie przez abs, od ang. absolute) sprawia, że wynik zawsze będzie nieujemny, a więc różnica zapisana wewnątrz staje się przemienna.
Dla przykładu: abs(900-100) daje wynik 800, a abs(100-900) -> abs(-800) -> 800. Wyniki są takie same.
Kiedy wartość bezwględna z różnicy liczb n i d będzie najmniejsza? Wtedy, gdy będą one leżeć najbliżej siebie. Wykorzystując wiedzę dotyczącą tworzenia liczb n i d musimy teraz znaleźć te liczby.
Dla n = 5000 nasze d jest równe 4999, bo 5000+4999 = 9999, tak więc różnica jest równa 1.
Możemy spróbować podstawić pod n liczbę 4999, w tym wypadku okaże się, że to d jest równe 5000.
W ten sposób znaleźliśmy dwie liczby, które są prawidłowymi odpowiedziami, ponieważ abs(5000-4999) == abs(4999-5000) i niemożliwe jest uzyskanie mniejszej różnicy.
Zostaje do obliczenia wartość maksymalna. Przy wyznaczaniu maksimum i minimum funkcji zawsze warto sprawdzić, co się stanie, gdy pod liczbę podstawimy wartości graniczne. W naszym wypadku z założeń wynika, że stanowią je liczby 1000 oraz 8999 (pod pierwszą cyfrę nie możemy podstawić 0 ani 9).
Dla n = 1000 wychodzi, że d będzie równe 8999, abs(1000-8999) = 7999.
Wynik będzie taki sam, jeśli pod n podstawimy 8999.
W ten sposób uzyskaliśmy odpowiedzi: 5000 lub 4999 oraz 1000 lub 8999.
Rozwiązanie
a) 5000 (lub 4999)
b) 8999 (lub 1000)
Zadanie 1.2.
Sposobów na rozwiązanie tego typu zadań jest wiele. Omówimy tu dwa rozwiązania, które są stosunkowo proste do zrozumienia i warto o nich wiedzieć, aby dobrze przygotować się na przyszłość.
Pierwszy sposób.
Aby napisać algorytm do tego problemu, należy znać właściwości operatorów dzielenia całkowitego (oznaczonego jako div) oraz reszty z dzielenia (mod).
Dzielenie całkowite polega na odrzuceniu reszty przy dzieleniu. Dla przykładu: 999 div 10 daje wynik 99.
Reszta z dzielenia wykonuje dokładnie to, co ma w nazwie: wyznacza resztę z dzielenia. Dla działania 999 mod 10 otrzymujemy wynik 9 (999/10 to 99 z resztą równą 9).
Wykorzystując te dwa operatory, ustalmy kroki, jakie należy podjąć, aby obliczyć liczbę d.
Najlepszą opcją jest wzięcie kolejno każdej cyfry i odjęcie jej od 9: w ten sposób najpewniej sami liczyliście liczbę d wcześniej. Operujemy jednak na wartościach liczbowych, dlatego kolejność działań musi następować od końca:
- wyznaczamy ostatnią cyfrę wykorzystując resztę z dzielenia przez 10 (n mod 10)
- odejmujemy tę cyfrę od 9 i wynik odejmowania przemnażamy, aby uzyskać wartość dziesiętną, setną, tysięczną itd.
- dodajemy uzyskaną liczbę do zmiennej oznaczającą liczbę d
Czynności te powinniśmy powtórzyć dla każdej cyfry, dlatego po wykonaniu tych działań podzielimy całkowicie liczbę n przez 10. Pozwoli to nam na przejście do kolejnej cyfry zaczynając od końca.
Aby lepiej zobrazować przebieg algorytmu, możemy stworzyć tabelkę dla np. n = 4561:
Lp. | Wartość n | Wartość d | n mod 10 | 9-(n mod 10) | i | n div 10 |
1 | 4561 | 0 | 1 | 8 | 1 | 456 |
2 | 456 | 8 | 6 | 3 | 10 | 45 |
3 | 45 | 38 | 5 | 4 | 100 | 4 |
4 | 4 | 438 | 4 | 5 | 1000 | 0 |
5 | 0 | 5438 | – | – | – | – |
Znamy sposób na uzyskanie liczby d. Przejdźmy więc do pseudokodu:
d ← 0
i ← 1 // zaczynamy od prawej strony, a więc od jedności
dopóki n ≠ 0 wykonuj
reszta ← n mod 10
wynik ← (9 – reszta) * i
d ← d + wynik
n ← n div 10
i ← i * 10 // po pierwszym wykonaniu z jedności przechodzimy na dziesiątki itd.
zwróć d i zakończ
Drugi sposób.
W tym rozwiązaniu wykorzystamy sposób wyznaczania d polegający na odejmowaniu liczby n od liczby składającej się z samych dziewiątek. Dla przykładu:
n = 3579, to d = 9999 – 3579 = 6420
n = 11, to d = 99 – 11 = 88
Musimy tu zadbać o wyznaczenie zmiennej z odpowiednią liczbą dziewiątek. Aby to zrobić, ponownie skorzystamy z dzielenia całkowitego, tak więc znajomość jego działania wydaje się nieunikniona:
dziewiatki ← 0
i ← 1
oryginalne_n ← n // niżej zmieniać będziemy wartość n, więc zapiszmy sobie jej początkową wartość
dopóki n ≠ 0 wykonuj
dziewiatki ← dziewiatki + 9 * i
n ← n div 10
i ← i * 10
d ← dziewiatki – oryginalne_n
zwróć d i zakończ
Przykładowe rozwiązania
I wersja
potega ← 1
d ← 0
dopóki n > 0 wykonuj
cyfra ← n mod 10
n ← n div 10
cyfra ← 9-cyfra
d ← d + (potega * cyfra)
potega ← potega * 10
II wersja
suma ← 0
kopia ← n
dopóki kopia > 0 wykonuj
kopia ← kopia / 10
suma ← suma*10 + 9
d ← suma – n
Zadanie 2 – Analiza algorytmu
W zadaniach z analizy algorytmu sposobem, który wydaje się być najbardziej odporny na pomyłki, jest tworzenie tabelki, dzięki której śledzimy etapy wykonywania algorytmu linijka po linijce.
Każda kolumna oznacza wartość zmiennej bądź sprawdzany warunek, a każdy wiersz jest kolejnym wykonaniem pętli. Pozwala to na bezpieczne prześledzenie działania algorytmu.
Warto jednak najpierw dokładnie przeczytać kroki zapisane w pseudokodzie i znaleźć charakterystyczne operacje.
Przed pętlą w funkcji zachodzą trzy operacje:
- zwiększenie wartości n o 1 (n ← n + 1)
- dodanie zmiennej x do tablicy T (T[n] ← x)
- przypisanie zmiennej s aktualnej wartości n (która wcześniej została powiększona o 1) (s ← n)
Widzimy tu mechanizm dodawania nowej wartości na koniec tablicy. Do zmiennej s zapisywany jest indeks tej nowej wartości.
Kolejną część algorytmu stanowi pętla dopóki. Przed wykonaniem jej sprawdzane są dwa warunki:
- czy wynik z dzielenia całkowitego s przez 2 jest większy lub równy 1
- czy wartość w tablicy T pod indeksem s jest większa od wartości pod indeksem s div 2
Oba warunki muszą zostać spełnione, aby instrukcje wewnątrz pętli zostały wykonane, ponieważ łączy je spójnik oraz.
Wewnątrz pętli zachodzi popularna w informatyce operacja: zamiana dwóch wartości w tabeli z wykorzystaniem zmiennej pomocniczej. Dokonuje się w tym miejscu przestawienie wartości w tabeli T pod indeksem s z wartością pod indeksem s div 2.
Po zamianie dzielimy całkowicie zmienną s przez 2, a więc zmniejszamy jej wartość o połowę i odrzucamy ewentualną resztę z dzielenia (np. dla s = 5, po wykonaniu s ← s div 2 zmienna ta przyjmie wartość 2). W tym momencie możemy zauważyć, że zmienna s przechowuje przez cały czas aktualny indeks zmiennej, którą dodaliśmy do tablicy na początku (zmienna x).
Algorytm ten polega więc na ciągłym przestawianiu w tablicy T wartości zmiennej x, jeśli po środku stoi wartość mniejsza od niej.
Zadanie 2.1.
Zacznijmy od pierwszego, rozwiązanego już przykładu, aby sprawdzić, czy nasz sposób jest prawidłowy.
Dla danych n = 4, T = [26, 3, 5, -4] oraz x = 5 po wykonaniu trzech początkowych operacji, nasze dane wyglądają tak:
n = 5, T = [26, 3, 5, -4, 5], s = 5
Teraz prześledźmy wykonanie pętli:
Lp. | s | s div 2 | (s div 2)>=1? | T przed zamianą | T[s]>T[s div 2] | T po zamianie |
1 | 5 | 2 | PRAWDA | [26, 3, 5, -4, 5] | 5 > 3 PRAWDA | [26, 5, 5, -4, 3], |
2 | 2 | 1 | PRAWDA | [26, 5, 5, -4, 3] | 5 > 26 FAŁSZ | – |
Podczas drugiego sprawdzenia nie zachodzi zamiana, ponieważ jeden z warunków (T[s]>T[s div 2]) zwrócił fałsz. Wynik T = [26, 5, 5, -4, 3] zgadza się z podaną w zadaniu odpowiedzią, więc możemy przejść dalej.
Dla drugiego przykładu:
n = 4, T = [36, 15, 17, 3], x = -5
Po wykonaniu początkowych operacji:
n = 5, T = [36, 15, 17, 3, -5], s = 5
Lp. | s | s div 2 | (s div 2)>=1? | T przed zamianą | T[s]>T[s div 2] | T po zamianie |
1 | 5 | 2 | PRAWDA | [36, 15, 17, 3, -5] | -5 > 15 FAŁSZ | – |
Jak widać, w tym przypadku pętla nie wykona się ani razu, więc odpowiedzią będzie T = [36, 15, 17, 3, -5]
Dla trzeciego przykładu:
n = 7, T = [27, 6, 13, 4, -3, -2, -3], x = 30
Po wykonaniu początkowych operacji:
n = 8, T = [27, 6, 13, 4, -3, -2, -3, 30], s = 8
Lp. | s | s div 2 | (s div 2)>=1? | T przed zamianą | T[s]>T[s div 2] | T po zamianie |
1 | 8 | 4 | PRAWDA | [27, 6, 13, 4, -3, -2, -3, 30] | 30 > 4 PRAWDA | [27, 6, 13, 30, -3, -2, -3, 4] |
2 | 4 | 2 | PRAWDA | [27, 6, 13, 30, -3, -2, -3, 4] | 30 > 6 PRAWDA | [27, 30, 13, 6, -3, -2, -3, 4] |
3 | 2 | 1 | PRAWDA | [27, 30, 13, 6, -3, -2, -3, 4] | 30 > 27 PRAWDA | [30, 27, 13, 6, -3, -2, -3, 4] |
4 | 1 | 0 | FAŁSZ |
Otrzymujemy wynik T = [30, 27, 13, 6, -3, -2, -3, 4]
Z tego przykładu możemy wyciągnąć pewny wniosek. Jeśli wartość zmiennej x przekazywanej do funkcji jest większa od innych elementów w tablicy T, to będzie ona zamieniać się miejscami z innymi liczbami, aż znajdzie się na początku tablicy. Tak właśnie się stało dla x = 30.
Poprawna odpowiedź
n | T[1..n] | x | T[1..n+1] |
---|---|---|---|
4 | 26, 3, 5, -4 | 5 | 26, 5, 5, -4, 3 |
4 | 36, 15, 17, 3 | -5 | 36, 15, 17, 3, -5 |
7 | 27, 6, 13, 4, -3, -2, -3 | 30 | 30, 27, 13, 6, -3, -2, -3, 4 |
Zadanie 2.2.
W tym zadaniu wywołujemy po kolei: d(6), d(-4), d(12)… Jako że początkowo tablica T jest pusta, po pierwszym wywołaniu funkcji: d(6) T będzie równe [6].
Następnie dokładamy kolejny element: -4.
Do rozwiązania tego zadania możemy wykorzystać metodę tabelkową, przedstawię tu natomiast skrócony opis zachodzących operacji.
Początkowo tablica przybiera postać T = [6, -4]. Zmienna s oznacza indeks 2, bo pod nim znajduje się liczba -4. Sprawdzamy więc, czy -4 > 6. Jest to fałsz, dlatego możemy przejść do kolejnego wykonania funkcji.
Dla x = 12 tablica T = [6, -4, 12]. Tym razem porównujemy 12 z 6, ponieważ 3 div 2 = 1. Liczba 12 jest większa od 6, dlatego zamieniamy je miejscami: T = [12, -4, 6]. W tym momencie s = 1 i nie mamy więcej elementów do sprawdzania, ponieważ zmienna x (czyli 12) trafiła już na początek
Następnie wykonujemy funkcję d(27): T = [12, -4, 6, 27]. 27 jest większe od -4, więc zamieniamy je miejscami: T = [12, 27, 6, -4]. 27 jest też większe od 12, dlatego ląduje ono na początku: T = [27, 12, 6, -4]
Dla d(26) T = [27, 12, 6, -4, 26]. 26 jest większe od 12, czyli T = [27, 26, 6, -4, 12]. 26 nie jest natomiast większe od 27. Tabelę zostawiamy tej formie.
Dla x = 8 tablica T = [27, 26, 6, -4, 12, 8]. 8 > 6, czyli T = [27, 26, 8, -4, 12, 6]. Po lewej stronie od 8 pozostały same większe liczby, co oznacza, że właśnie tak będzie wyglądać ostateczna tabela.
Rozwiązanie
27, 26, 8, -4, 12, 6
Zadanie 2.3.
Aby prawidłowo rozwiązać to zadanie, należy dobrze zrozumieć działanie algorytmu. Z treści zadania wynika, że początkowo w tabeli znajdują się liczby od 1 do k-1. Znaczy to, że dla k = 4 T = [1, 2, 3]. Do tej tablicy dokładamy liczbę k, w tym przypadku będzie to 4. Oznacza to, że przesunięcia będziemy dokonywać do czasu, aż liczba k nie znajdzie się na początki tablicy T.
Tablica ta będzie przyjmować wartości kolejno:
- [1, 2, 3, 4]: po tym następuje pierwsze sprawdzenie warunki pętli
- [1, 4, 3, 2]: po zamianie kolejne
- [4, 1, 3, 2]: i trzecie, ostatnie, ma miejsce, gdy liczba k jest już na początku tablicy
Dla k = 8 sprawdzenia będą miały miejsce gdy k jest na 8., 4., 2. i 1. pozycji.
Podążając tym śladem, możemy wyznaczyć odpowiedzi do zadania:
Dla k = 16 sprawdzenia nastąpią przy indeksie 16, 8, 4, 2 oraz 1. Otrzymujemy 5 sprawdzeń.
Dla k = 1025 warunek pętli zostanie sprawdzony na indeksach 1025, 512 (bo 1025 dzielimy całkowicie na pół), 256, 128, 64, 32, 16, 8, 4, 2 i 1, co daje nam odpowiedź 11.
Poprawna odpowiedź
k | Ile razy sprawdzany jest warunek pętli dopóki ? |
---|---|
4 | 3 |
16 | 5 |
1025 | 11 |
Zadanie 3 – Test
Zadanie 3.1.
W tym zadaniu pojawia się rekurencja, czyli wywoływanie funkcji wewnątrz jej samej. Dobrym sposobem na zwizualizowanie rekurencji jest rysowanie drzewa wywołań:
- przed wywołaniem funkcji następuje wypisanie liczby n, co zapiszemy na górze
- następnie wykonuje się kolejna funkcja z argumentem n pomniejszonym o dwa, zapisujemy to po środku
- gdy argument n przestanie być większy od 0, następuje drugie wypisywanie liczby n. Tym razem ma ono jednak miejsce od końca, czyli najmniejszej liczby do największej.
Analizując wynik wywołania funkcji f podążamy za strzałkami: zaczynamy od lewej strony z góry (5, 3, 1), a później od prawej strony z dołu (1, 3, 5).
Dla f(-1) nic nie zostaje wypisane, ponieważ -1 nie spełnia warunku n > 0.
Prawidłową odpowiedź stanowi 5 3 1 1 3 5, a więc pierwszy podpunkt jest fałszywy.
Z drugim podpunktem postępujemy podobnie. Znamy już mechanizm działania tej funkcji: najpierw wypisane zostają liczby od n do 1 z odstępami co 2, a później podążamy w drugą stronę.
Zaczynamy od 6, później 4, 2. 0 nie jest wypisywane, ponieważ nie spełnia warunku n > 0. Następnie te same liczby zostają wypisane od końca.
Odpowiedź 6 4 2 2 4 6 jest prawidłowa, ponieważ spełnia ona wszystkie przedstawione warunki.
Sprawdzamy trzeci podpunkt. W pierwszym przejściu wypisane zostają liczby 7, 5, 3, i 1. Wypisując teraz liczby w drugą stronę dowiadujemy się, że odpowiedź 7 5 3 1 1 3 5 7 jest także prawdziwa.
W ostatnim przypadku możemy od razu dostrzec, że jest on fałszem. Wszystko za sprawą pojawiających się zer, które są niezgodne z warunkiem n > 0.
Rozwiązanie
FPPF
Zadanie 3.2.
Zamiana systemów liczbowych jest typowym zadaniem maturalnym. Każdym pozycyjny system liczbowy ma swoją podstawę. Na co dzień korzystamy z systemu dziesiętnego, którego podstawą jest liczba dziesięć. Oznacza to, że kolejne cyfry oznaczają kolejne potęgi liczby 10, czyli idąc od prawej jest to 10: 1, 10, 100, 1000… Podobnie sprawa wygląda przy innych podstawach, na przykład dla podstawy 2 cyfry oznaczają 1, 2, 4, 8…
Konwersja systemu liczbowego na dziesiętny będzie polegać na przemnożeniu liczby przez odpowiednio spotęgowaną podstawę (zależnie od miejsca). Dla przykładu:
- 1032 w systemie czwórkowym to: 2*40 + 3*41 + 0*42 + 1*43 = 2*1 + 3*4 + 1*64 = 2 + 12 + 64 = 78 w systemie dziesiętnym
- 10001 w systemie dwójkowym to: 1*1 + 1*16 = 17
Posiadając tę wiedzę, możemy przystąpić do rozwiązania zadań:
- (10000000)2 = 1*128 = 128
(A9)16 = 9*1 + 10*16 = 169
Tutaj warto wyjaśnić skąd wziął się znak A w liczbie. W podstawach liczbowych większych od 10 nie istnieje wystarczająca liczba cyfr, aby móc je zapisać, ponieważ jak wspomnieliśmy na co dzień korzystamy z systemu dziesiętnego, w którym występuje 10 cyfr (od 0 do 9). Jak więc zapisać 10 w jednym znaku? Wykorzystano do tego kolejne znaki alfabetu, czyli A, B, C… Każdy znak to kolejna cyfra, czyli A = 10, B = 11, C = 12 itd.
Wracając do zadania, czy liczba 128 jest większa od 169? Ano nie, więc zaznaczamy fałsz. - (1111)4 = 1*1 + 1*4 + 1*16 + 1*64 = 85
(1111111)2 = 1 + 2 + 4 + 8 + 16 + 32 + 64 = 127
Liczba 85 jest większa od 127? Też nie, czyli będzie to fałsz. - (3003)4 = 3*1 + 3*64 = 195
(C2)16 = 2*1 + 12*16 = 194
Tym razem 195 jest większe od 194, przez co zaznaczamy prawdę. - (333)8 = 3*1 + 3*8 + 3*64 = 219
(10100101)2 = 1 + 4 + 32 + 128 = 165
Tutaj także otrzymujemy prawdę.
Rozwiązanie
FFPP
Zadanie 3.3.
Zadania z baz danych wymagają znajomości języka SQL. Każde zapytanie, które wyciąga dane z bazy rozpoczyna się klauzulą SELECT. Następnie wybieramy co chcemy otrzymać (kolumny lub wyrażenia) oraz z jakiej tabeli lub kwerend będziemy wyciągać dane (zapisujemy ją po FROM). W tym przypadku odnosimy się za każdym razem do tabeli Produkty, ponieważ tylko ona jest dostępna.
Po tabeli opcjonalnie występuje instrukcja WHERE, która pozwala wybrać konkretne dane, np. produkty, których cena jest mniejsza od 3. Przejdźmy do zadań:
- W tym zapytaniu wybieramy produkty, których cena jest równa 2 lub 4. Dzieje się tak za sprawą spójnika OR. Patrząc na tabelę widzimy, że taką cenę mają dwa produkty: zeszyt oraz cyrkiel, które powinny stanowić odpowiedź. Zaznaczamy fałsz.
- Zapytanie wykorzystuje funkcję agregującą AVG wyznaczającą średnią z ceny przedmiotów, których liczba sztuk wynosi 125 lub 160. Częstym błędem na maturze było potraktowanie zapisu (125, 160) jako zakresu od 125 do 160, co jest fałszywym założeniem. Prawidłowa liczbę sztuk mają produkty zeszyt i piórnik, dlatego liczymy z nich średnią cenę: (2 + 8) / 2 = 5. Jest więc to prawda.
- W kolejnym zapytaniu ponownie pojawia się funkcja agregująca, tym razem SUM, czyli suma. Sumujemy sztuki produktów, których cena jest równa 1 lub 2. Patrzymy więc na zeszyt, ołówek oraz gumkę. Ich łączna liczba sztuk to 160 + 250 + 250 = 660. Zaznaczamy prawdę.
- Ostatni przykład wykorzystuje funkcję agregującą COUNT, oznaczającą zliczaj. Zliczamy liczbę produktów, których cena zawiera się pomiędzy 2 a 4. Produkty z taką ceną to zeszyt, okładka i cyrkiel, co oznacza, że prawidłowa odpowiedź to 3. Zaznaczamy fałsz.
Rozwiązanie
FPPF
Zadanie 4 – Neon cyfrowy
Zadania z programowania są typowym zadaniem egzaminacyjnym. Warto tu pamiętać o przykładowym pliku: pozwala on sprawdzać, czy nasz kod zwraca poprawne wyniki.Zacznijmy od odczytania danych z dostarczonego pliku. Wykorzystamy do tego instrukcję with open(nazwa_pliku) as nazwa:
[python]instrukcje = []
with open(“Dane_2105/instrukcje.txt”) as plik:
for linia in plik:
instrukcja = linia.strip() # czyszczenie linii z białych znaków
instrukcje.append(instrukcja)[/python]
Tworzymy listę instrukcje, w której po kolei zapiszemy na później instrukcje z pliku. Otwieramy w tym celu plik, po którym iterujemy linia po linii (for linia in plik).
Później należy oczyścić linię z białych znaków (takich jak znak nowej linii czy spacja), aby później praca była bezproblemowa. Ostatecznie dodajemy instrukcję do stworzonej wcześniej listy.
Zadanie 4.1.
[python]dlugosc = 0
for ins in instrukcje:
if ins.startswith(“DOPISZ”):
dlugosc += 1
elif ins.startswith(“USUN”):
dlugosc -= 1
print(“1)”, dlugosc)[/python]
Aby rozwiązać to zadanie, musimy zacząć od utworzenia zmiennej służącej do przechowywania długości napisu. Następnie dla każdej instrukcji DOPISZ dodajemy do tej zmiennej 1, a dla USUN usuwamy. Wykorzystujemy tu metodę startswith, która jest dostępna dla każdego ciągu znaków. Jak sama nazwa sugeruje, sprawdza ona, czy dany ciąg znaków zaczyna się od podanego napisu.
Reszta instrukcji nas nie interesuje, ponieważ nie zmieniają one długości napisu.
Na koniec wypisujemy długość napisu, aby móc ją zapisać do pliku wyniki4.txt.
Rozwiązanie
517
Zadanie 4.2.
W tym zadaniu będziemy potrzebowali kilku zmiennych. Przede wszystkim musimy mieć dwie zmienne, w których zapiszemy rodzaj instrukcji, które występują aktualnie w ciągu oraz ich długość. Oprócz tego potrzebujemy miejsce do zapisania najdłuższego ciągu, który wystąpił do tej pory (wraz z rodzajem instrukcji):
[python]aktualny_rodzaj = None
aktualna_dlugosc = 0
najdluzszy_rodzaj = None
najdluzsza_dlugosc = 0
for ins in instrukcje:
rodzaj = ins.split()[0]
if not aktualny_rodzaj:
aktualny_rodzaj = rodzaj
aktualna_dlugosc = 1
continue
if aktualny_rodzaj == rodzaj:
aktualna_dlugosc += 1
if aktualna_dlugosc > najdluzsza_dlugosc:
# ten ciąg jest w tym momencie najdłuższy
najdluzsza_dlugosc = aktualna_dlugosc
najdluzszy_rodzaj = rodzaj
else:
aktualny_rodzaj = rodzaj
aktualna_dlugosc = 1
print(“2)”, najdluzszy_rodzaj, najdluzsza_dlugosc)[/python]
Kolejny raz korzystamy z pętli for, w której iterujemy po instrukcjach. Tym razem liczy się tylko rodzaj instrukcji (ZMIEN, USUN…), co uzyskujemy instrukcją ins.split()[0]. Wykonuje ona podział tekstu na elementy oddzielone znakiem podanym w nawiasie, domyślnie (i w tym wypadku) będzie to spacja; następnie tworzy z tych elementów listę. Dla przykładu: ‘DOPISZ A’.split() zwróci nam listę [‘DOPISZ’, ‘A’], a więc rodzaj instrukcji zawiera się pod zerowym indeksem.
Następnie sprawdzamy, czy cokolwiek znajduje się pod zmienną aktualny_rodzaj. Taki przypadek ma miejsce tylko podczas pierwszej iteracji pętli, jako że przypisaliśmy aktualnemu rodzajowi wartość None. Po przypisaniu rodzaju i długości używamy instrukcji continue, która przechodzi do kolejnej iteracji pętli (a więc pomija kroki znajdujące się poniżej).
Jeśli zmienna aktualny_rodzaj jest już określona, sprawdzamy czy kolejna instrukcja jest tego samego rodzaju.
Jeśli tak, musimy zwiększyć długość ciągu o jeden i sprawdzić, czy aktualny ciąg jest najdłuższy.
Jeśli nie, oznacza to, że rozpoczyna się nowy ciąg, który przyjmuje początkową długość 1, jako że do tej pory tylko raz wystąpiła instrukcja tego samego typu.
W ten sposób uzyskujemy rodzaj i długość najdłuższego ciągu, którą wypisujemy.
Rozwiązanie
ZMIEN 7
Zadanie 4.3.
W tym zadaniu przydatna jest znajomość Pythonowych słowników (zwanych też tablicą asocjacyjną). Słownik jest strukturą, w której dane zapisane są w formacie klucz:wartość. W tym przypadku kluczem będzie litera, a wartością liczba wystąpień tej litery. Aby odwołać się do wartości, zapisujemy litery[litera]. Wiedza ta jest przydatna, ale nieobowiązkowa. Zadanie można bez większego problemu rozwiązać bez użycia słowników! My jednak przedstawimy rozwiązanie wykorzystujące słowniki.
[python]litery = {}
for ins in instrukcje:
rodzaj, znak = ins.split()
if rodzaj == “DOPISZ”:
if znak in litery:
litery[znak] += 1
else:
litery[znak] = 1
najczesciej_litera = None
najczesciej = 0
for litera in litery:
if litery[litera] > najczesciej:
najczesciej_litera = litera
najczesciej = litery[litera]
# skrócona wersja powyższego dla ciekawych:
# najczesciej = max(litery.items(), key=lambda x: x[1])
print(“3)”, najczesciej_litera, najczesciej)[/python]
Dla każdej instrukcji sprawdzamy, czy dotyczy ona dopisania. Następnie patrzymy, czy dany znak znajduje się w słowniku litery.
Jeśli tak, to dodajemy 1 do liczby wystąpień tej litery.
Jeśli nie, to tworzymy nowy wpis w słowniku z wartością 1.
W ten sposób otrzymujemy ile razy każdy znak został dopisany. Wystarczy teraz znaleźć maksymalną liczbę, co robimy za pomocą pętli for.
Rozwiązanie
Z 37
Zadanie 4.4.
W tym zadaniu wykorzystamy wiedzę z poprzednich poleceń.
Opiszmy każdą instrukcję po kolei:
a) DOPISZ: dopisujemy do napisu podany znak, a więc możemy to zapisać w formie napis += znak (co jest równoznaczne z zapisem napis = napis + znak).
b) ZMIEN: zmieniamy ostatni znak na inny. Aby to zrobić, musimy najpierw pozbyć się ostatniej litery, a następnie dodać podaną. Korzystamy w tym celu z tzw. wycinania (ang. slicing), za pomocą którego wybierzemy znaki znajdujące się od początku aż do przedostatniego znaku w napisie. Zapisujemy to w ten sposób: napis[:-1]. W ten sposób usunęliśmy ostatni znak, teraz zostaje dodać nowy: napis = napis[:-1] + znak.
c) USUN: tutaj także skorzystamy z wykrajania, jako że ponownie musimy pozbyć się ostatniego znaku: napis = napis[:-1].
d) PRZESUN: w tej instrukcji mamy najwięcej do zrobienia. Do zamienienia pierwszego wystąpienia od lewej możemy wykorzystać funkcję napis.replace, w którym jako argumenty podajemy znak do zmienienia, nowy znak oraz opcjonalnie liczbę zmian do wykonania. Tutaj chcemy dokonać tylko jednej zmiany, dlatego w ostatnim argumencie zapisujemy 1. Będzie to wyglądać w ten sposób: napis = napis.replace(znak, nowy_znak, 1).
Musimy teraz w jakiś sposób wyznaczyć ten nowy znak. Zgodnie z podaną w zadaniu definicją, zamieniamy literę na kolejną w alfabecie. W tym celu możemy skorzystać z kodów ASCII, w których każdy znak ma określony kod liczbowy. Duże litery alfabetu zaczynają się od 65 i kończą na 90 (A to 65, a Z to 90), co wykorzystamy w mechanizmie zamiany. Aby w Pythonie uzyskać kod ASCII znaku, przekazujemy znak do funkcji ord, czyli kod_ascii = ord(znak). Następną literę w alfabecie uzyskujemy poprzez dodanie do tego kodu 1 i zamienienie go z powrotem na typ znakowy z użyciem funkcji chr (od character, po polsku znak): nowy_znak = chr(kod_ascii+1).
Wyjątek od tej reguły stanowi znak Z, jako że gdy dodamy do kodu ASCII tej litery 1, otrzymamy wynik 91, który oznacza znak [. Najłatwiejszym sposobem na rozwiązanie tego problemu jest wcześniejsze sprawdzenie, czy znak do zmiany to Z i jeśli tak, to nowym znakiem będzie A:
[python]if znak == “Z”:
nowy_znak = “A”
else:
kod_ascii = ord(znak)
nowy_znak = chr(kod_ascii+l) napis =
napis.replace(znak, nowy_znak, 1)[/python]
Pełne rozwiązanie tego zadania prezentuje się w ten sposób:
[python]napis = “”
for ins in instrukcje:
rodzaj, znak = ins.split()
if rodzaj == “DOPISZ”:
napis += znak
elif rodzaj == “ZMIEN”:
napis = napis[:-1] + znak
elif rodzaj == “USUN”:
napis = napis[:-1]
elif rodzaj == “PRZESUN”:
if znak == “Z”:
nowy_znak = “A”
else:
kod_ascii = ord(znak)
nowy_znak = chr(kod_ascii+1)
napis = napis.replace(znak, nowy_znak, 1)
print(“4)”, napis)[/python]
Poprawne rozwiązanie
POZNIEJMOWIONOZECZLOWIEKTENNADSZEDLODPOLNOCYODBRAMYPOWROZNIC
ZEJSZEDLPIESZOAOBJUCZONEGOKONIAPROWADZILZAUZDEBYLOPOZNEPOPOLUD
NIEIKRAMYPOWROZNIKOWIRYMARZYBYLYJUZZAMKNIETEAULICZKAPUSTABYLOCIE
PLOACZLOWIEKTENMIALNASOBIECZARNYPLASZCZNARZUCONYNARAMIONAZWRA
CALUWAGEZATRZYMALSIEPRZEDGOSPODASTARYNARAKORTPOSTALCHWILEPOSL
UCHALGWARUGLOSOWGOSPODAJAKZWYKLEOTEJPORZEBYLAPELNALUDZINIEZNA
JOMYNIEWSZEDLDOSTAREGONARAKORTUPOCIAGNALKONIADALEJWDOLULICZKIT
AMBYLADRUGAKARCZMAMNIEJSZANAZYWALASIEPODLISEMTUBYLOPUSTOKARCZ
MANIEMIALANAJLEPSZEJSLAWY
[początek opowiadania “Wiedźmin” A.Sapkowskiego]
Zadanie 5 – Wodociągi
Do rozwiązania tego zadania wykorzystamy arkusz kalkulacyjny. Zaczynamy od importu danych, który w Excelu znajduje się pod pozycją Dane -> Z pliku tekstowego. Wybieramy dostarczony plik i bez większych zmian importujemy do arkusza.
Zadanie 5.1.
Rozpoczniemy od przygotowania pięciocyfrowych numerów klientów, które znajdują się na początku kolumny KodKlienta. Aby wydobyć 5 pierwszych znaków, skorzystamy z formuły LEWY, gdzie jako pierwszy argument podajemy pole z kolumny KodKlienta, a jako drugi liczbę znaków, którą chcemy uzyskać: w naszym wypadku 5.
Finalnie instrukcja powinna wyglądać w ten sposób: =LEWY(A2; 5). Klikamy teraz dwukrotnie w prawy dolny róg komórki, aby wykonać to polecenie dla każdego wiersza:
Teraz potrzebujemy liczby osób w gospodarstwie, która także znajduje się w kodzie klienta. Są to dwie cyfry po numerze klienta. Aby je wydobyć, skorzystamy zarówno z funkcji LEWY, jak i PRAWY: najpierw wydobędziemy pierwsze 7 znaków, a z wydobytego tekstu dwa ostatnie: =PRAWY(LEWY(A2;7); 2).
Zsumujmy też zużycie wody z całego roku w osobnej kolumnie, korzystając z formuły SUMA i podając jako zakres wszystkie miesiące: =SUMA(B2:M2).
Tabela po tych operacjach powinna wyglądać w ten sposób:
Przed utworzeniem zestawienia przygotujmy jeszcze wymagane średnie zużycie wody na jedną osobę. Wystarczy podzielić wyznaczoną sumę przez liczbę osób: przy takim układzie kolumn jak na zrzucie, formuła będzie wyglądać tak: =P2/O2. Zgodnie z poleceniem wynik ten należy jeszcze zaokrąglić do dwóch miejsc po przecinku. Służy do tego formuła ZAOKR, w której podajemy liczbę do zaokrąglenia i liczbę miejsc po przecinku. Ostatecznie wygląda to tak: =ZAOKR(P3/O3;2)
Aby wybrać 10 klientów, którzy zużyli najwięcej wody na osobę, musimy posortować dane. W tym celu stwórzmy nową tabelę przestawną, którą można znaleźć w zakładce Wstawianie -> Tabele -> Tabela przestawna:
W panelu tabeli przestawnej wybieramy pola Nr klienta i Zużycie na osobę (w zależności od nazw, jakie wcześniej podaliśmy):
Aby posortować dane, klikamy na jakąkolwiek wartość w kolumnie zawierającą średnie zużycie prawym przyciskiem myszy i wybieramy Sortuj -> Sortuj od największych do najmniejszych.
Wystarczy teraz skopiować 10 pierwszych wpisów i podać je jako odpowiedź do zadania. Możemy w tym celu stworzyć specjalny arkusz o nazwie odpowiedzi:
Nie zapomnijmy jednak też o skopiowaniu wyników do pliku wyniki6.txt.
Rozwiązanie
Nr klienta | Średnie zużycie na osobę |
---|---|
07935 | 41,33 |
05080 | 40,00 |
00645 | 39,75 |
08090 | 39,50 |
05738 | 39,33 |
08349 | 39,20 |
08850 | 39,00 |
02202 | 38,25 |
09468 | 37,75 |
06866 | 36,75 |
Zadanie 5.2.
Jako że potrzebujemy kodów dzielnic, zaczniemy od ich pozyskania. One także zapisane są w kodzie klienta, lecz tym razem na końcu. Skorzystamy z formuły PRAWY: =PRAWY(A2;3).
Do tego zadania wykorzystamy tabelę przestawną – tworzymy ją w taki sam sposób jak wcześniej. Wybieramy Kod dzielnicy jako etykiety wierszy oraz sumę roczną jako wartości. Powinno pojawić się nam ładne zestawienie zużycia wody w każdej dzielnicy:
Możemy przenieść zestawienie do arkusza odpowiedzi.
Rozwiązanie
Dzielnica | Suma |
---|---|
BEM | 54080 |
BIA | 61614 |
BIE | 56368 |
MOK | 55889 |
OCH | 59273 |
PRA | 57241 |
REM | 58971 |
SRO | 58124 |
TAR | 60234 |
URU | 59597 |
URY | 50116 |
WAW | 57674 |
WES | 60372 |
WIL | 55476 |
WLO | 66372 |
WOL | 60523 |
ZOL | 62312 |
Zadanie 5.3.
Zadanie jest podobne do wcześniejszego, z tą różnicą, że tu musimy podać zużycie dla każdego miesiąca. Stwórzmy nową tabelę przestawną, w której powiążemy dzielnice z miesiącami:
Teraz musimy znaleźć maksymalne wartości dla każdej dzielnicy. W kolumnie obok zapisujemy więc formułę MAX, w której zaznaczamy wszystkie miesiące dla każdej dzielnicy:
Teraz przenosimy wyniki (kod dzielnicy wraz z maksymalnym zużyciem) do arkusza odpowiedzi. Jeśli pojawia się problem z adresami komórek przy kopiowaniu, najłatwiej go rozwiązać wklejając dane do np. notatnika, a stamtąd skopiować do arkusza.
Rozwiązanie
Dzielnica | Maksimum |
---|---|
BEM | 8108 |
BIA | 9274 |
BIE | 8475 |
MOK | 8452 |
OCH | 8861 |
PRA | 8575 |
REM | 8873 |
SRO | 8776 |
TAR | 9120 |
URU | 8960 |
URY | 7519 |
WAW | 8699 |
WES | 9050 |
WIL | 8284 |
WLO | 9966 |
WOL | 9117 |
ZOL | 9417 |
Zadanie 5.4.
W tym zadaniu także możemy skorzystać z tabeli przestawnej, aby obliczyć zużycie wody każdego miesiąca w 2019 roku. Wystarczy wybrać każdy miesiąc na liście pól, a suma powinna wyliczyć się sama. Będzie to wyglądać w ten sposób:
Teraz liczymy symulowane zużycie wody dla następnych lat, które zwiększają się co roku o 1%. Musimy pamiętać o zaokrąglaniu liczby w górę, do czego wykorzystujemy formułę ZAOKR.W.GÓRĘ: jako pierwszy argument podajemy liczbę do zaokrąglenia, a jako drugi istotność, czyli czy zaokrąglamy do jedności, dziesiątek czy setek: =ZAOKR.W.GÓRĘ(B4*1,01; 1).
Robimy to dla każdego roku, aż do 2030:
Pamiętajmy o sprawdzeniu poprawności naszych wyliczeń: podane zostały w poleceniu poprawne dane dla stycznia 2020 oraz maja 2025, a więc upewnijmy się, czy wszystko dobrze policzyliśmy.
Szukamy teraz miesiąca i roku, w którym jako pierwszy zostanie przekroczona wartość 160 000. Przy takim zadaniu odpowiedź już po chwili jest widoczna: chodzi o lipiec 2026.
Pamiętajmy jednak, aby zawsze dwa razy sprawdzać dane. Jak może zauważyłeś, z jakiegoś powodu przestawione zostały kolumny Suma z IX i Suma z VIII, przez co mogliśmy popełnić błąd (miesiące nie są zapisane po kolei). Warto to skorygować. W tym wypadku wystarczy odznaczyć i zaznaczyć pola kolejnych miesięcy jeszcze raz na liście pól tabeli przestawnej.
Zostaje nam do stworzenia wykres dla roku 2030. Aby wyglądał schludnie, zapiszmy słownie kolejne miesiące i skopiujmy dane z 2030 roku:
Teraz zaznaczmy te dane i przejdźmy do zakładki Wstawianie -> wykres liniowy:
Teraz należy sformatować wykres. Najczęściej na maturze wymaga się tytułu oraz opisów osi. W tym zadaniu nie zostało to sprecyzowane, jednak lepiej to zrobić.
Aby dodać tytuły osi w Excelu 2019, należy kliknąć na wykres, a później na plusa znajdującego się obok:
Otrzymujemy finalnie taki wykres:
Rozwiązanie
Lipiec 2026 roku
rok | I | II | III | IV | V | VI | VII | VIII | IX | X | XI | XII |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2020 | 54081 | 53807 | 53727 | 86603 | 86484 | 118728 | 151022 | 118672 | 86480 | 86386 | 54052 | 54143 |
2021 | 54622 | 54346 | 54265 | 87470 | 87349 | 119916 | 152533 | 119859 | 87345 | 87250 | 54593 | 54685 |
2022 | 55169 | 54890 | 54808 | 88345 | 88223 | 121116 | 154059 | 121058 | 88219 | 88123 | 55139 | 55232 |
2023 | 55721 | 55439 | 55357 | 89229 | 89106 | 122328 | 155600 | 122269 | 89102 | 89005 | 55691 | 55785 |
2024 | 56279 | 55994 | 55911 | 90122 | 89998 | 123552 | 157156 | 123492 | 89994 | 89896 | 56248 | 56343 |
2025 | 56842 | 56554 | 56471 | 91024 | 90898 | 124788 | 158728 | 124727 | 90894 | 90795 | 56811 | 56907 |
2026 | 57411 | 57120 | 57036 | 91935 | 91807 | 126036 | 160316 | 125975 | 91803 | 91703 | 57380 | 57477 |
2027 | 57986 | 57692 | 57607 | 92855 | 92726 | 127297 | 161920 | 127235 | 92722 | 92621 | 57954 | 58052 |
2028 | 58566 | 58269 | 58184 | 93784 | 93654 | 128570 | 163540 | 128508 | 93650 | 93548 | 58534 | 58633 |
2029 | 59152 | 58852 | 58766 | 94722 | 94591 | 129856 | 165176 | 129794 | 94587 | 94484 | 59120 | 59220 |
2030 | 59744 | 59441 | 59354 | 95670 | 95537 | 131155 | 166828 | 131092 | 95533 | 95429 | 59712 | 59813 |
Zadanie 5.5.
W tym zadaniu wykorzystamy dane przygotowane w poprzednim poleceniu. Skopiujmy dane z lat 2019-2030 do nowego arkusza. W 2019 i 2020 roku maksymalny przepływ wody wyniósł 160 000 m3, a od 2021 ma zwiększać się każdego roku o 1000 m3. Zapiszmy to w nowej kolumnie:
Jak widzimy, nigdzie do tej pory nie przekroczyliśmy maksymalnego przepływu. Możemy stworzyć nową kolumnę, w której będziemy sprawdzać, czy w danym roku przekroczony został limit. Będzie ona wyglądać w ten sposób: =MAX(B2:M2)>N2, gdzie pod zakresem B2:M2 są wszystkie miesiące, a komórką N2 jest maksymalny przepływ:
Jeśli wrócimy do polecenia okazuje się, że w tym zadaniu nie ma ograniczenia co do lat, tj. rok 2030 nie jest ostatnim, jaki powinniśmy wziąć pod uwagę. Przedłużmy symulację do kolejnych lat:
Okazuje się, że wody pierwszy raz zabraknie ponownie w lipcu, w roku 2035. Zapisujemy to jako odpowiedź.
Rozwiązanie
lipiec 2035
Zadanie 6 – Bitwa
Rozpoczynamy od importu danych. Przechodzimy do zakładki Dane zewnętrzne -> Nowe źródło danych -> Z pliku -> Plik tekstowy:
Wybierzmy na początek dołączony plik gracze.txt. W poleceniu zapisane jest, że pola są oddzielone znakiem tabulacji, wybieramy więc format ograniczony:
Po przejściu dalej wybieramy tabulator jako ogranicznik oraz zaznaczamy pole „Pierwszy wiersz zawiera nazwy pól”:
W kolejnym oknie sprawdzamy, czy typy danych są prawidłowe dla każdej kolumny. Zazwyczaj Access dobiera właściwe typy, jednak warto się upewnić. Ważną kwestią jest także format dat: aby ustawić odpowiedni, klikamy na Przycisk „Zaawansowane” w lewym dolnym rogu:
Jak widzimy, domyślnie Access wybrał kolejność dat w formacie dzień, miesiąc, rok, co jest niezgodne z zawartością pliku. Jeśli tego nie edytujemy, otrzymamy później błędy importu. Musimy wybrać rok, miesiąc, dzień, czyli RMD, oraz zapisać ogranicznik daty jako myślnik:
Akceptujemy zmiany i przechodzimy dalej.Pozostaje nam do wybrania klucz podstawowy: jest on dostarczony w kolumnie id_gracza, więc wybierzmy go:
Przechodzimy dalej i wybieramy nazwę dla tabeli, w naszym przypadku proponowana przez Accessa „Gracze” jest w porządku.Kroki te powtarzamy dla dwóch kolejnych plików.
W pliku jednostki kluczem podstawowym będzie id_jednostki.
W pliku klasy kluczem podstawowym będzie nazwa jednostki, jako że jest to unikalna wartość dla każdego wpisu.Po pomyślnym zaimportowaniu wszystkich plików możemy przejść do zadań.
Zadanie 6.1.
Do tworzenia zapytań posłużymy się językiem SQL, który postaramy się jak najlepiej wytłumaczyć. Aby utworzyć kwerendę przechodzimy do zakładki „Tworzenie”, gdzie wybieramy „Projekt kwerendy”. Ukazuje się okno z dostępnymi tabelami. W tym zadaniu potrzebna będzie tabela Gracze, którą dodajemy. Tak powinno wyglądać teraz okno:
Klikamy prawym przyciskiem na Kwerenda1 i przechodzimy do widoku SQL:
Tutaj będziemy zapisywać kod potrzebny do wykonania zadań.Każdą kwerendę wybierającą dane zaczynamy od instrukcji SELECT. Następnie podajemy kolumny (jeśli jest więcej niż jedna – oddzielone przecinkiem), z których chcemy uzyskać dane, jak np. kraj czy data_dolaczenia. W tym miejscu można też podać funkcje agregujące (o których za chwilę) lub znak *, który wybiera wszystkie dostępne kolumny.Po podaniu tych informacji, zapisujemy źródło danych, które w tym przypadku jest tabelą Gracze. Taki zapis powinien znajdować się już na miejscu, ponieważ wcześniej wybraliśmy tabelę z listy. Chodzi o instrukcję FROM Gracze.Na końcu każdego zapytania znajduje się średnik, jednak nawet gdy o nim zapomnimy, kwerenda wykona się poprawnie.Przykładowa kwerenda będzie wyglądać w ten sposób:
[sql]SELECT kraj, data_dolaczenia
FROM Gracze;[/sql]
W zadaniu jest mowa o graczach, którzy dołączyli w 2018 roku. W języku SQL istnieją funkcje podobne do tych z Excela. Ich listę dla Accessa możemy znaleźć na tej stronie. Aby uzyskać rok, skorzystamy z funkcji YEAR. Aby przetestować działanie funkcji, zapiszmy w kwerendzie zapytanie:
[sql]SELECT kraj, YEAR(data_dolaczenia)
FROM Gracze;[/sql]
Po przejściu do widoku arkusza danych, powinniśmy otrzymać coś takiego:
Jak widać, w drugiej kolumnie został wypisany sam rok. Wykorzystamy to później do wybrania osób z 2018 roku.
Nazwa kolumny nie brzmi zachęcająco. Aby to zmienić, po wyrażeniu YEAR(data_dolaczenia) możemy zapisać AS rok. W ten sposób druga kolumna otrzyma wybraną przez nas nazwę.Wracając do zadania: należy wybrać osoby z roku 2018. Do wybrania konkretnych wartości służy klauzula WHERE wraz z warunkiem, zapisana bezpośrednio po FROMtabela. Jeśli interesuje nas tylko rok 2018, zapisujemy to w ten sposób:
[sql]SELECT kraj, YEAR(data_dolaczenia) AS rok
FROM Gracze
WHERE YEAR(data_dolaczenia) = 2018;[/sql]
Warto zwrócić uwagę na różnice w zapisie między językiem SQL, a klasycznymi językami programowania. Aby sprawdzić, czy wartości są sobie równe, wykorzystujemy pojedynczy znak równości =. Natomiast przy sprawdzaniu, czy wartości są różne, zamiast typowego !=, w Accessie zapiszemy <>.Teraz należy zliczyć wpisy dla każdego kraju, jako że jeden wpis odpowiada jednemu graczowi. Na szczęście nie musimy robić tego ręcznie, aby uzyskać odpowiedź na zadanie. Służy do tego klauzula GROUP BY, dzięki której możemy agregować (pogrupować) dane według np. wartości w danej kolumnie. W naszym przypadku chcemy stworzyć osobny wpis dla każdego kraju, a więc będziemy grupować według kolumny kraj:
[sql]SELECT kraj, YEAR(data_dolaczenia) AS rok
FROM Gracze
WHERE YEAR(data_dolaczenia) = 2018
GROUP BY kraj;[/sql]
Próba wykonania tej kwerendy zwróci jednak błąd. Gdy grupujemy dane, musimy skorzystać z tzw. funkcji agregujących, ponieważ zbieramy wszystkie wpisy dla danego kraju w jeden wspólny. Główne funkcje agregujące to:
- COUNT: zlicza liczbę elementów w grupie
- SUM: zlicza sumę elementów w grupie
- AVG: zlicza średnią elementów w grupie
- MAX: zwraca element z maksymalną wartością
- MIN: zwraca element z minimalną wartością
Aby zliczyć graczy z każdego kraju osobno, skorzystamy z funkcji COUNT. Zapisujemy je po klauzuli SELECT w taki sposób:
[sql]SELECT kraj, COUNT(data_dolaczenia) AS liczba_graczy
FROM Gracze
WHERE YEAR(data_dolaczenia) = 2018
GROUP BY kraj;[/sql]
Otrzymaliśmy w ten sposób listę krajów wraz z liczbą graczy z tego kraju. Wystarczy teraz wynik posortować malejąco według liczby graczy i wybrać pięć pierwszych wpisów: służy do tego instrukcja ORDER BY, po której podajemy nazwę kolumny, wartość lub numer kolumny. W naszym przypadku chcemy posortować kraje według liczby graczy, dlatego możemy zapisać ORDER BY COUNT(data_dolaczenia).
Zapisujemy to na końcu kodu:
[sql]SELECT kraj, COUNT(data_dolaczenia) AS liczba_graczy
FROM Gracze
WHERE YEAR(data_dolaczenia) = 2018
GROUP BY kraj
ORDER BY COUNT(data_dolaczenia);[/sql]
Gdy przejdziemy do arkusza danych okazuje się, że domyślnie wpisy posortowane są malejąco. Aby to zmienić, po ORDER BY instrukcja zapisujemy DESC (od ang. descending, malejąco): ORDER BY COUNT(data_dolaczenia) DESC; Otrzymujemy wpisy krajów z największą liczbą graczy. Możemy skopiować już pięć pierwszych do pliku wyniki6.txt. Jeśli chcemy być bardzo dokładni, istnieje instrukcja, która pozwala ograniczyć liczbę wypisanych wierszy. W Accessie jest to TOP, po której zapisujemy ile wpisów chcemy uzyskać. Zapisujemy ją bezpośrednio po SELECT:
[sql]SELECT TOP 5 kraj, COUNT(data_dolaczenia) AS liczba_graczy
FROM Gracze
WHERE YEAR(data_dolaczenia) = 2018
GROUP BY kraj
ORDER BY COUNT(data_dolaczenia) DESC;[/sql]
Taką odpowiedź powinniśmy otrzymać:
Rozwiązanie poprawne
Polska | 11 |
Stany Zjednoczone | 8 |
Francja | 7 |
Rosja | 6 |
Niemcy | 6 |
Rozwiązanie nieuwzględniające roku dołączenia
Polska | 26 |
Niemcy | 23 |
Stany Zjednoczone | 18 |
Rosja | 17 |
Francja | 15 |
Zadanie 6.2.
Treść tego zadania jest niefortunnie sformułowana. Interpretacja, jakiej oczekuje od nas zadanie, sprowadza się do obliczenia sumy strzału dla wszystkich jednostek zawierających “elf” w nazwie, z podziałem na te jednostki.Aby to zrobić, będziemy musieli skorzystać z funkcji SUM. Klauzula FROM będzie wybierała informacje z połączenia tabel Jednostki oraz Klasy za pomocą instrukcji INNER JOIN. Wynik należy pogrupować według nazw, aby uzyskać zestawienie z podziałem na poszczególne klasy.Ostatnim etapem będzie uwzględnienie warunku dotyczącego samej nazwy. Aby dopasowywać tekst z wykorzystaniem tzw. symboli wieloznacznych, możemy użyć klauzuli LIKE. Do dyspozycji mamy znak *, oznaczający dowolną liczbę dowolnych znaków, oraz znak ?, który oznacza dokładnie jeden dowolny znak. Ponieważ “elf” można znajdowac się w dowolnym miejscu nazwy, klauzula przyjmie postać LIKE “*elf*”. Potrzebne będzie użycie HAVING zamiast WHERE, ponieważ używamy funkcji agregujących.
[sql]SELECT Jednostki.nazwa, Sum(Klasy.strzal) AS Suma
FROM Jednostki
INNER JOIN Klasy ON Jednostki.nazwa = Klasy.nazwa
GROUP BY Jednostki.nazwa
HAVING Jednostki.nazwa Like “*elf*”;[/sql]
Rozwiązanie
ciemny elf 555
elfi czarodziej 435
lesny elf 1815
wysoki elf–gwardzista 870
Zadanie 6.3.
W tym zadaniu wykorzystamy dwie kwerendy. W pierwszej z nich znajdziemy numery graczy, którzy posiadają artylerzystów, a dopiero w drugiej wyszukamy tych, którzy takich jednostek nie posiadają.Stwórzmy kwerendę, która korzystać będzie z tabeli jednostki. Aby wybrać graczy, którzy mają na stanie artylerzystów, wykorzystamy klauzulę WHERE:
[sql]SELECT id_gracza
FROM Jednostki
WHERE nazwa = “artylerzysta”;[/sql]
W ten sposób uzyskaliśmy identyfikatory wszystkich graczy z jednostkami artylerzysty.Aby wydobyć graczy bez jednostek artylerzysty, wykorzystamy powyższa kwerendę. Najpierw musimy wydobyć identyfikator każdego z graczy, a więc skorzystamy z tabeli Gracze:
[sql]SELECT id_gracza
FROM Gracze;[/sql]
Teraz jako warunek wybierzemy brak identyfikatora gracza w kwerendzie, którą zapisaliśmy powyżej:
[sql]SELECT id_gracza
FROM Gracze
WHERE id_gracza NOT IN (SELECT id_gracza FROM Jednostki WHERE nazwa = “artylerzysta”);[/sql]
W taki sposób połączyliśmy dwie kwerendy i otrzymaliśmy odpowiedź:
Rozwiązanie
22
24
29
35
36
37
38
47
54
61
64
72
110
114
115
122
123
138
141
167
Zadanie 6.4.
Skupmy się na wyborze jednostek, które mogą dojść do Kamiennej Bramy w jednej turze. Najważniejszy dla nas jest podany warunek: |x – x1| + |y – y1| ≤ szybkosc.
Kamienna Brama znajduje się w lokacji o koordynatach (100, 100), a jednostka w (lok_x, lok_y). Warunek będzie wyglądać w ten sposób: |100 – lok_x| + |100 – lok_y| ≤ szybkosc. Teraz stworzenie odpowiedniej kwerendy jest już formalnością. Pamiętajmy jedynie, że wartość bezwzględna w Accessie jest oznaczana pod postacią funkcji abs.Przy tworzeniu kwerendy potrzebować będziemy lokalizację, klasę i szybkość jednostek. Tworząc kwerendę wybierzemy więc tabele Jednostki oraz Klasy. Dzięki temu w kodzie zapisane zostaje złączenie dwóch tabel w formie INNER JOIN.Kwerenda, która wybierze nam wartości dotyczące jednostek będzie wyglądać tak:
[sql]SELECT id_jednostki, lok_x, lok_y, szybkosc
FROM Jednostki
INNER JOIN Klasy ON Jednostki.nazwa = Klasy.nazwa
WHERE abs(100-lok_x) + abs(100-lok_y) <= szybkosc;[/sql]Możemy sprawdzić poprawność danych, przechodząc do arkusza:[caption id="attachment_5792" align="aligncenter" width="667"] Sprawdzamy poprawność danych przechodząc do arkusza.[/caption]
Jak widzimy, warunek wybiera odpowiednie dane. Wróćmy do treści zadania.Musimy teraz pogrupować jednostki na klasy, a następnie zliczyć jednostki, które są w stanie dojść do lokalizacji (100, 100). Zróbmy to:
[sql]SELECT Jednostki.nazwa, COUNT(*) As Liczba_jednostek
FROM Jednostki
INNER JOIN Klasy ON Jednostki.nazwa = Klasy.nazwa
WHERE abs(100-lok_x) + abs(100-lok_y) <= szybkosc
GROUP BY Jednostki.nazwa;[/sql]Otrzymujemy odpowiedź:[caption id="attachment_5793" align="aligncenter" width="429"] Ostateczna odpowiedź.[/caption]
Rozwiązanie
Nazwa | Liczba |
architekt | 1 |
artylerzysta | 4 |
balista | 2 |
ciemny elf | 1 |
drwal | 3 |
elfi czarodziej | 1 |
goniec | 2 |
ifryt | 1 |
kaplan | 2 |
kawalerzysta | 7 |
konny lucznik | 4 |
kusznik | 1 |
lekki jezdziec | 19 |
lucznik | 1 |
mag powietrza | 3 |
mag wody | 2 |
paladyn | 1 |
piechur | 7 |
pikinier | 5 |
robotnik | 5 |
topornik | 5 |
wysoki elf-gwardzista | 1 |
zwiadowca | 4 |
Zadanie 6.5.
W pierwszym podpunkcie wystarczyłoby pogrupować jednostki według lokalizacji, a następnie je zliczyć i wybrać takie, których zliczenie zwróciło 2 lub więcej (znajduje się w tym miejscu więcej niż jedna jednostka). Co jednak, jeśli wszystkie jednostki na polu będą należeć do tego samego gracza? Sprawdźmy, czy takie sytuacje mają w ogóle miejsce:
[sql]SELECT lok_x, lok_y, id_gracza, COUNT(*) AS liczba_jednostek
FROM Jednostki
GROUP BY lok_x, lok_y, id_gracza;[/sql]
Przechodząc do arkusza danych, kliknijmy na trójkąt obok kolumny liczba_jednostek. Powinniśmy ujrzeć coś takiego:
Odznaczmy wartość 1. Otrzymaliśmy listę pozycji, w których dwie jednostki należą do tego samego gracza. Na naszej liście bitew musimy wziąć to pod uwagę, stwórzmy w tym celu tabelę wykluczającą więcej niż jedną jednostkę tego samego gracza. Podobnie jak w podpunkcie 6.2, należy tutaj zastosować klauzulę HAVING zamiast COUNT.
[sql]SELECT lok_x, lok_y, id_gracza, COUNT(*) AS liczba_jednostek
FROM Jednostki
GROUP BY lok_x, lok_y, id_gracza
HAVING COUNT(*) = 1;[/sql]
Otrzymujemy tu tabelę, z której teraz powinniśmy zliczyć mające miejsce bitwy. Zróbmy to w nowej kwerendzie, przy której dane wybierzemy z wcześniej stworzonej:
[sql]SELECT lok_x, lok_y, COUNT(*) As liczba_jednostek
FROM [Zadanie-5-jednostki]
GROUP BY lok_x, lok_y
HAVING COUNT(*) > 1;[/sql]
Już widzimy, ile bitew trwa na mapie. 1061 – widać to w dolnej części Accessa. Zapisujemy to jako odpowiedź i przechodzimy do kolejnego podpunktu.Tutaj sprawa się komplikuje. Bitwy, które nas interesują, są toczone przez Polaków. Oznacza to, że do kwerendy u mnie nazwanej Zadanie-5-jednostki, należy dołączyć tabelę Gracze i sprawdzić kraj uczestnika. Trzeba jednak też sprawdzić, czy występują takie bitwy, w których Polacy walczą ze sobą tj. w bitwie uczestniczy więcej niż jedna osoby z Polski. Takie przypadki musimy liczyć jako jeden, ponieważ interesuje nas liczba bitw, w których uczestniczą Polacy, a nie liczba osób z Polski uczestnicząca w bitwach.
Aby uzyskać liczbę bitew, w których uczestniczy więcej niż jedna osoba z Polski, stwórzmy nową kwerendę:
[sql]SELECT lok_x, lok_y, kraj, COUNT(*) AS liczba_jednostek
FROM [Zadanie-5-jednostki]
INNER JOIN Gracze ON [Zadanie-5-jednostki].id_gracza = Gracze.id_gracza
WHERE kraj LIKE “Polska”
GROUP BY lok_x, lok_y, kraj
HAVING COUNT(*) > 1;[/sql]
Powinniśmy otrzymać 19 wpisów:
Później odliczymy od wyniku 19, aby wykluczyć właśnie te bratobójcze bitwy.Teraz zajmiemy się wyznaczeniem liczby bitew toczonych przez Polaków.W tym celu wykorzystamy dane odnośnie toczących się bitew z podpunktu a, tabele Jednostki oraz Gracze. Połączmy je graficznie tak jak na zrzucie:
Uzyskaliśmy w ten sposób informacje odnośnie każdej bitwy, tj. kto ją toczy i z jakiego kraju pochodzi.Przejdźmy do kodu i zapiszmy zliczanie rekordów za pomocą COUNT(*) oraz warunek WHERE kraj LIKE “Polska”:
[sql]SELECT COUNT(*) as bitwy_polakow
FROM (Jednostki INNER JOIN Gracze ON Jednostki.id_gracza = Gracze.id_gracza) INNER JOIN [Zadanie-5-a] ON (Jednostki.lok_y = [Zadanie-5-a].lok_y) AND (Jednostki.lok_x = [Zadanie-5-a].lok_x)
WHERE kraj LIKE “Polska”;[/sql]
Wyciągnęliśmy tu liczbę Polaków, którzy toczą bitwę. Wynosi ona 264. Są tutaj jednak też uwzględnione wspomniane wcześniej „bratobójcze” walki między Polakami, które powinniśmy liczyć pojedynczo. Dlatego w wyniku zapisujemy 245, ponieważ odejmujemy wyznaczone 19 bitw, w których osoby z Polski toczą walkę między sobą (muszą być one liczone pojedynczo).
Rozwiązanie
1061, w tym 245 z udziałem Polaków
Wpisy, które mogą Cię zainteresować: