• Nie Znaleziono Wyników

Podstawy algorytmiki i programowania - wykład 2 Tablice dwuwymiarowe cd Funkcje rekurencyjne

N/A
N/A
Protected

Academic year: 2022

Share "Podstawy algorytmiki i programowania - wykład 2 Tablice dwuwymiarowe cd Funkcje rekurencyjne"

Copied!
26
0
0

Pełen tekst

(1)

Tablice dwuwymiarowe cd Funkcje rekurencyjne

Treści prezentowane w wykładzie zostały oparte o:

S. Prata, Język C++. Szkoła programowania. Wydanie VI, Helion, 2012

www.cplusplus.com

Jerzy Grębosz, Opus magnum C++11, Helion, 2017

B. Stroustrup, Język C++. Kompendium wiedzy. Wydanie IV, Helion, 2014

S. B. Lippman, J. Lajoie, Podstawy języka C++, WNT, Warszawa

Podstawy algorytmiki i programowania -

wykład 2

(2)

Tablica jednowymiarowa to jakby rząd liczb napisanych jedna za drugą:

Ta tablica ma rozmiar 9.

Tablica dwuwymiarowa to jakby arkusz liczb, tabela, macierz:

Ta dwuwymiarowa tablica liczb ma dwa rozmiary: 3 wiersze(rzędy) i 2 kolumny elementów.

W C++ tablicę dwuwymiarową mogącą przechować te liczby definiuje się tak:

Tablice wielowymiarowe

1 20 3 100 32 5 4 0 19

1 0

2 14

4 5

(3)

W tablicy t do elementu o współrzędnych wiersz: i kolumna: j

odnosimy się wyrażeniem t[i][j]

Zatem elementy naszej tablicy to:

t[0][0]=1 t[0][1]=0 t[1][0]=2 t[1][1]=14 t[2][0]=4 t[2][1]=5

Pętla for, która wypisuje na ekranie wartości elementów tablicy for(int i = 0 ; i < 3 ; i++) {

for(int j = 0 ; j < 2 ; j++) { cout << t[i][j] << " ";

}

cout<<endl;//po każdym wierszu

// przejście do nowej linii }

Tablice wielowymiarowe

(4)

Przekazywanie tablic wielowymiarowych do funkcji

Elementy tablicy wielowymiarowej umieszczone są kolejno w pamięci komputera tak, że najszybciej zmienia się najbardziej skrajny prawy indeks.

Oznacza to że tablica przechowywana jest „rzędami”.

Dlatego powyższą tablicę dwuwymiarową możemy inicjalizować następująco:

int t[3][2]={1,0,2,14,4,5};

Lepiej jest inicjalizacjować tablicę ujmując poszczególne wiersze w nawiasy klamrowe (przy czym ewentualne brakujące elementy są inicjalizowane zerami)

int tab[4][2] = {{10, 20},{30},{60,70},{80 }};

W funkcjach mających jako parametry tablice

(5)

Przykład 1 – wyświetlenie macierzy /* Napisz funkcję wyświetla na ekranie

macierz a o w wierszach i k kolumnach (k<=10).

Przetestuj funkcję.*/

void drukMacierz(int n, int m, double a[][10]) {

//wyświetlanie el.macierzy

//z dokladn.do 2-ch miejsc po przecinku cout<<fixed<<setprecision(2);

for(int i=0; i < n; i++)

{//wyświetlamy el. i-tego wiersza for(int j=0; j < m; j++)

cout << setw(7) << a[i][j];

cout << endl;//przejście do nowego wiersza }

(6)

Przykład 2- liczba elementów ujemnych

/*Funkcja oblicza liczbę ujemnych elementów w tablicy t będącej jej parametrem. Macierz t ma w wierszy i k kolumn (k<=10)- parametry funkcji. */

int ileElUjMacierz(double t[][10], int w, int k) { int ile = 0;//licznik elem. ujemnych

for(int i=0; i<w; i++)//po wierszach

{ //przechodzimy wzdłuż i-tego wiersza for(int j=0; j<k; j++)

if( t[i][j]<0) //jeśli elem. jest <0 ile++;//zwiększamy licznik

}

return ile;

(7)

Przykład 2 – main() int main()

{

double m[3][10]= {{1,-2,-3,4},{0,2,-3,5}, {-2,4,-6,8}};

int ileuj = ileElUjMacierz(m,3,4);

cout<<"W macierzy jest: "<<ileuj

<<" elementow ujemnych "<<endl;

}

Przykład 3. Napisz funkcję

void sumyKolumnami(int t[][10],int w, int k, int sk[]);

która w kolejnych składowych wektora sk zapisze sumy odpowiednich kolumn macierzy t. Macierz t ma w

wierszy i k kolumn (k<=10). Przetestuj funkcję.

(8)

Przykład 3. Suma elementów w kolumnach void sumyKolumnami1(int t[][10],int w, int k,

int sk[]) {

for(int j=0; j<k; j++)//przechodzimy po kolumnach {

//dla j-tej kolumny:

sk[j]=0;//zerujemy j-ty elem wektora sk for(int i=0; i<w; i++)

//przechodzimy po wierszach j-tej kolumny {

sk[j]+=t[i][j]; //zwiększamy nasza sumę //o kolejne wartości z j-tej kolumny }

}

(9)

Przykład 3. Suma elementów w kolumnach sposób 2

/*wersja 2 przechodzimy po tablicy dwuwym. zgodnie z ułożeniem elementów w pamięci, czyli wierszami */

void sumyKolumnami2(int t[][10],int w, int k, int sk[]){

//zerujemy tablice sk przed pętla for(int j=0; j<k; j++)

sk[j]=0;

for (int i=0;i<w;i++)//przechodzimy po wierszach for(int j=0;j<k;j++)//dla i-tego wiersza

//po jego kolumnach

sk[j]+=t[i][j];//zwiększamy dla każdej // kolumny jej sumę elem /*po przejsciu 0-go wiersza w tablicy sk mamy:

sk[0]=t[0][0],sk[1]=t[0][1],...,sk[k-1]=t[0][k-1]

po przejsciu 1-go wiersza zwiększamy każdą sumę o jeden element z 1- go wiersza, itd. */

(10)

Przykład 3. Suma elementów w kolumnach – f-cja pomocnicza //funkcja wyswietlajaca na ekranie tablica

void drukTab(int t[], int n){

for(int i=0; i<n ;i++)

cout<<t[i]<<((i != (n-1)) ? ", " : "\n");

}

(11)

Przykład 3 - funkcja main() int main () {

int m1[3][10]={{1,2,3,4},{0,2,3,5}, {2,4,6,8}};

int tabsk1[4];

sumyKolumnami1(m1,3,4,tabsk1);

cout<<"sumy kolumn - v1: "<<endl;

drukTab(tabsk1, 4);

int tabsk2[4];

sumyKolumnami2(m1,3,4,tabsk2);

cout<<"sumy kolumn - v2: "<<endl;

drukTab(tabsk2, 4);

}Run:

sumy kolumn - v1:

3, 8, 12, 17

sumy kolumn - v2:

(12)

Przykład 4 – minima wierszy w macierzy

/* Napisz funkcję

void MinimaWierszami(int t[][10],int w, int k, int m[]);

która w kolejnych składowych wektora m zapisze minimalne wartości z odpowiednich wierszy macierzy t. Macierz t ma w wierszy i k kolumn (k<=10). Przetestuj funkcję.*/

void MinimaWierszami(int t[][10],int w, int k, int m[]) { for (int i=0;i<w;i++){//przechodzimy po wierszach

//jestesmy w i-tym wierszu, dla i-tego wiersza:

m[i]=t[i][0]; //pod minimum i-tego wiersza

//podstawiamy 0-wy elem z tego wiersza czyli t[i][0]

for(int j=1;j<k;j++){//przechodzimy wzdłuż wiersza // czyli po kolumnach

if(t[i][j]<m[i]){ //jeśli kolejny elem w wierszu // jest mniejszy

m[i]=t[i][j]; //to mamy nowe min }

}

(13)

Przykład 4 - funkcja main()

int main () {

int mac[3][10] = {{1, 2, 0, 5}, {6, 2, 1, 8}, {4, 5, 3, 9}};

int tab_min[3];

minimaWierszami(mac, 3, 4, tab_min);

//wyswietlamy minima w poszczegolnych wierszach drukTab(tab_min, 3);

}

Run:0, 1, 3

(14)

Przykład 5 - Iloczyn macierzy

Przykład 5.

Funkcja, która oblicza, o ile to możliwe iloczyn dwóch macierzy A i B. Jeżeli nie można obliczyć iloczynu A*B (liczba kolumn mac. A != liczba wierszy mac. B) funkcja zwraca false, a w przeciwny razie true i oblicza iloczyn.

Parametrami funkcji są macierze A i B, ich wymiary oraz

macierz wynikowa i jej wymiary.

(15)

Przykład 5 - iloczyn macierzy

#include <iostream>

#include<iomanip>

using namespace std;

bool iloczynMac(double a[][10], int wa,int ka, double b[][10],int wb, int kb,

double c[][10], int &wc,int &kc) {

//liczba kolumn A musi być równa //liczbie wierszy macierzy B

if(ka != wb)

return false;

//obliczamy wymiary macierzy C=A*B wc = wa;

kc = kb;

(16)

Przykład 5 - iloczyn macierzy for(int i = 0; i < wc; ++i)

{ for(int j = 0; j < kc; ++j) {

//obliczmy element c[i][j]

//i-ty wiersz mac.A * j-ta kolumna mac.B c[i][j] = 0.0;

for(int k=0; k<ka; ++k) {

c[i][j] += a[i][k] * b[k][j];

} }

}

return true;

}

(17)

Przykład 5 - iloczyn macierzy int main() {

//deklaracja mac a

double a[10][10] = {{1}, {2}, {3}};

int wa = 3, ka = 1;

//deklaracja mac b

double b[10][10] = {{1, 1, 1}};

int wb = 1, kb = 3;

//deklaracja mac c double c[10][10];

int wc, kc;

(18)

Przykład 5 - iloczyn macierzy if(iloczynMac(a,wa,ka,b,wb,kb,c,wc,kc)) { cout<<"Iloczyn macierzy A*B "<<endl;

drukMacierz(wa,wb,c);

}

else

cout<<"Mnozenie mac. niemożliwe"<<endl;

return 0;

}

Run:

Iloczyn macierzy A*B 1.00 1.00 1.00

2.00 2.00 2.00 3.00 3.00 3.00

(19)

Funkcje rekurencyjne

Funkcje mogą wywoływać same siebie - takie funkcje nazywamy funkcjami rekurencyjnymi.

void f(int x){

f(x);

}

Przy definiowaniu takich funkcji trzeba zadbać, aby ciąg wywołań skończył się (aby nie doprowadzić do

nieskończonej pętli wywołań jak w powyższym przykładzie).

Zatem przed wywołaniem samej siebie funkcja zwykle

sprawdza pewien warunek i jeśli nie jest on spełniony, nie dokonuje już samo-wywołania.

Warunek ten nazywamy warunkiem zatrzymującym

rekurencję (lub warunkiem stopu).

(20)

Funkcje rekurencyjne void fun(int x){

if(x > 0) fun(x-1);

}

Funkcja ma warunek if(x > 0), dzięki któremu jej wywoływanie ma szanse się kiedyś zakończyć. Jeżeli wywołamy tę funkcję tak:

fun(3),

to wywoła ona siebie samą z argumentem 3 – 1 = 2.

Zacznie się więc ponowne wykonywanie funkcji: fun(2).

W niej nastąpi kolejne wywołanie, tym razem z argumentem 2 – 1 = 1, czyli

fun(1).

Teraz nastąpi kolejne wywołanie, tym razem z argumentem zero (bo:

1 – 1 = 0) fun(0).

To wywołanie funkcji sprawdzi warunek if(x>0) – i okaże się on niespełniony, więc dalsze wywołanie już nie nastąpi. Funkcja wykona

(21)

Obiekty definiowane w funkcji rekurencyjnej

Wiemy, że w zwykłych sytuacjach, jeżeli jedna funkcja A wywoła funkcję B, to lokalne zmienne funkcji A (przechowywane na stosie) nie giną.

Jeżeli ta funkcja B zdefiniuje jakieś swoje lokalne zmienne

(obiekty), także pojawiają się one na stosie. Po zakończeniu pracy funkcji B jej lokalne zmienne są usuwane ze stosu, a funkcja A

pracuje na tych swoich, które na nią czekały.

W przypadku wywołań rekurencyjnych jest podobnie. Gdy funkcja A wywołuje siebie samą (czyli drugi raz funkcję A), na stosie są już zmienne będące własnością pierwszego wywołania.

Drugie wywołanie spowoduje, że na stosie pojawią się nowe zmienne dla tego drugiego wywołania. Oczywiście będą to zupełnie odrębne zmienne.

(22)

Funkcja n!- wersja iteracyjna

unsigned long long silniaIt(unsigned short n){

//silnia: n!=1*2*...*n, 0!=1!=1 unsigned long long sil=1;

for(int i=1; i<=n; i++) sil *= i;

return sil;

}

int main() {

cout << "5!=" << silniaIt(5)<<endl;

unsigned short n;

cout<<"podaj liczbe";

cin>>n;

cout<<n<<"!="<<silniaIt(n)<<endl;

(23)

Funkcje rekurencyjne - przykład n!

Przykład 1. Funkcja obliczająca n!, dla n>=0 //silnia: n!=(n-1)!*n, 0!=1!=1

unsigned long long silnia(unsigned short n)

{ if(n<=1)// warunek stopu:n==0 lub n==1

return 1;// 0! =1 1!=1 return n*silnia(n-1);

}

Dopóki zmienna n nie stanie się <=1, funkcja wywołuje się rekurencyjnie. Z analizy teoretycznej wynika, że po skończonej liczbie kroków wartość n musi stać się równa 0 lub 1. Tak więc warunek zakończenia (tzw. warunek stopu) jest zapewniony i funkcja działa prawidłowo.

(24)

Funkcje rekurencyjne - przykład n!

int main() {

cout << "5!=" << silnia(5)<<endl;

unsigned short n;

cout<<"podaj liczbe";

cin>>n;

cout<<n<<"!="<<silnia(n)<<endl;

}

(25)

Funkcje rekurencyjne - przykład n!

silnia(5)=5*silnia(4) =5*24 =120

wywołanie powrót

4*silnia(3) =4*6=24 wywołanie powrót

3*silnia(2) =3*2=6 wywołanie powrót

2*silnia(1) =2*1

wywołanie powrót

1

(26)

Funkcje rekurencyjne - przykład n!

Cytaty

Powiązane dokumenty

[r]

Jeżeli wartość w komórce A2 będzie słowem Warszawa, to w komórce, w której ma być wprowadzona funkcja JEŻELI pojawi się słowo stolica, jeśli będzie to inne miasto,

Na początku podajmy komunikat, do czego jest nasz program (kategoria wygląd).. W kolejnym kroku z kategorii czujniki wybierzemy

Na wejściówkę trzeba umieć napisać wzór funkcji mają dany kąt przecięcia z osią OX oraz jeden punkt, obliczyć kąt przecięcia danej prostej z osią oraz rozwiązać zadanie

W dowolnym n-wyrazowym postępie arytmetycznym o sumie wyrazów równej n, k-ty wyraz jest równy 1.. Dla podanego n wskazać takie k, aby powyższe zdanie

Ile jest tych

Dowód nierówności Jensena.

Prosta l jest równoległa do prostej AC i dzieli trójkąt ABC na dwie figury o równych polach.. Znajdź równanie