Laboratorium 3. Iteracje

Przed zajęciami laboratoryjnymi zapoznaj się z następującymi pozycjami literatury:

1. Wykład z przedmiotu „Podstawy programowania”

2. J. Grębosz „Symfonia C++” tom 1. Oficyna Kallimach, Kraków, 1999

Rozdziały: 2.3, 2.4, 2.5 , 2.7, 2.9, 2.10, 4.1.

Przygotowując się do laboratorium przeanalizuj podane poniżej przykłady, odpowiedz na pytania, rozwiąż testy i napisz odpowiednie programy. Po zajęciach zrób zadania podane w ostatnim punkcie tego opracowania.

Przykłady i pytania

1. Instrukcje iteracyjne - trzy rodzaje pętli: while, do-while, for

Przykład L3_F0_P1 Przeanalizuj program i odpowiedz na pytania

// L3_F0_P1.cpp

// Program wypisuje liczby od 1 do 20 uzywajac petli while

#include<iostream>

using namespace std;

int main()

{

int i = 1; // definicja i inicjalizacja zmiennej i

while( i <= 20 ) // poczatek petli while

{

cout << i << " "; // wypisanie wartosci zmiennej i oraz spacji

i++; // zwiekszenie i. Rownowazne z i = i + 1; albo i +=1;

} // koniec petli while

cout << endl; // przejscie do nowej linii

system("pause");

return 0;

}

Pytania

a) Jak zastąpić blok ITERACJA przez równoważną instrukcję do-while?

b) Jak zastąpić blok ITERACJA przez równoważną instrukcję for( pole1 ; pole2 ; pole3 ). Wypełnij pola.

c) Jak zastąpić blok ITERACJA przez równoważną pętlę nieskończoną while(1) i instrukcję break.

d) Jak zastąpić blok ITERACJA instrukcją for oraz instrukcją continue, tak aby program wypisywał liczby od 20

do 1 z pominięciem liczby 13.

Skompiluj i uruchom programy i sprawdź poprawność odpowiedzi.

Odpowiedzi

a) do {cout << i << " "; i++; } while( i <= 20 );

b) for( ; i<=20; cout << i << " ", i++);

c) while(1){ cout << i << " "; if (i==20) break; i++;}

d) for( i=20; i>= 1; i--) { if (i==13) continue; cout << i << " ";}

S t r o n a | 1 WETI Politechnika Gdańska, POP v.1.5.2

2. Błędy numeryczne w obliczeniach na liczbach zmiennoprzecinkowych

Przykład L3_F0_P2 Przeanalizuj program i odpowiedz na pytania

/*

* L3_F0_P2.cpp

*

* PROGRAM DWUKROTNIE OBLICZA SKONCZONA SUME CIAGU:

* S = 1./1 + 1./2 + 1./3 + 1./4 + ... + 1./n gdzie n=100000000

* Kropki po jedynkach we wzorze wskazuja, ze nie sa to obliczenia na liczbach

* calkowitych ( dla nich wynik bylby staly S=1 ).

* W pierwszej petli for sumowanie rozpoczyna się od najwiekszego skladnika sumy.

* W drugiej petli for sumowanie rozpoczyna się od najmniejszego skladnika sumy.

*/

#include<iostream>

using namespace std;

int main()

{

float fx = 0;

for (long int i=1; i<=100000000; i++)

{

fx = fx + 1./i;

}

cout << fx << endl;

float fy = 0;

for (long int i=100000000; i>=1; i--)

{

fy = fy + 1./i;

}

cout << fy << endl;

system("pause");

return 0;

}

Pytania

a) Sprawdź w Literaturze, jak są reprezentowane liczby zmiennoprzecinkowe. Czy wszystkie liczby rzeczywiste są dokładnie reprezentowane w zapisie binarnym używanym w komputerze?

b) Skompiluj, uruchom program i porównaj obliczone sumy. Czy obie sumy są równe? Jeżeli nie są równe, to dlaczego?

c) Zastąp typ float typem double, ponownie skompiluj i uruchom program. Porównaj wyniki. Który z użytych typów liczb zmiennoprzecinkowych dokładniej reprezentuje liczby rzeczywiste?

Odpowiedzi

a) Nie.

b) Nie. Z powodu kumulujących się błędów numerycznych.

c) Typ double.

S t r o n a | 2 WETI Politechnika Gdańska, POP v.1.5.2

3. Pętle zagnieżdżone (pętle wewnątrz innych pętli)

Przykład L3_F0_P3 Program rysuje brzeg prostokąta znakami ’x’. Długości boków użytkownik podaje na standardowym wejściu jako liczby całkowite z przedziału [2, 60].

Przeanalizuj program i odpowiedz na pytania

// L3_F0_P3.cpp

/* Program rysuje brzeg prostokąta znakami ’x’.

xxxxxxxxxxxxxxxxxxxxx

x x

x x b

x x

xxxxxxxxxxxxxxxxxxxxx

a

*/

#include<iostream>

using namespace std;

int main()

{

int a; // dlugosc boku a

int b; // dlugosc boku b

// wczytaj boki: a oraz b

cout << "Podaj dlugosc boku a [2,60]: ";

cin >> a;

cout << "Podaj dlugosc boku b [2,60]: ";

cin >> b;

// zakładamy ze podane dane sa poprawne

for(int i = 1; i <= a; i++)

cout << 'x'; // rysowanie gornej linii

cout << endl;

for(int j = 2; j <= b-1; j++) // petla kolejnych wierszy - rysowanie linii bocznych

{

cout << 'x'; // znak ’x’ na lewym brzegu

for(int i = 2; i < a; i++) // wypisanie a-2 spacji we wnętrzu j-tego wiersza

cout << ' ';

cout << 'x' << endl; // znak ’x’ na prawym brzegu

}

for(int i = 1; i <= a; i++)

cout << 'x' ; // rysowanie dolnej linii

cout << endl;

system("pause");

return 0;

}

Pytania

a) Skompiluj i uruchom program dla ( a = 60, b = 20) oraz ( a = 2, b = 2). Jaka jest kolejność wypisywania znaków na ekranie?

b) W programie użyto 4 pętli for, przy czym pętla druga jest zagnieżdżona w pętli trzeciej. Czy ilość zagnieżdżeń jest ograniczona? Czy można zagnieżdżać w sobie pętle różnego typu?

c) Napisz program L3_F0_P3.cpp tak, aby użyć tylko dwóch instrukcji iteracji (można dodatkowo użyć instrukcji warunkowych). Ponownie skompiluj i uruchom program.

Odpowiedzi

a) Wierszami, z góry na dół. W wierszu, od lewej do prawej.

b) Nie. Tak.

S t r o n a | 3 WETI Politechnika Gdańska, POP v.1.5.2

c)

//-------------------------------------------------------------rysowanie--------

for(int y = 1; y <= b; y++) // petla wierszy

{

for(int x = 1; x <= a; x++) // petla kolumn

{

if( (y == 1) || (y == b) ) // pierwszy i ostatni wiersz rysujemy

cout << 'x'; // jako linie ciagla ( znaki ‘x’)

else

if( (x == 1) || (x == a) ) // pierwsza i ostatnia kolumne rowniez

cout << 'x'; // rysujemy jako linia ciagla ( znaki ‘x’)

else

cout << ' '; // pozostaly obszar wypelniamy spacjami

} // koniec petli kolumn

cout << endl; // po kazdym wierszu przejscie do nowej linii

}

//--------------------------------------------------------------------------------

Testy

1. Co wypiszą na ekranie następujące iteracje?

a) int i=5;

while (i >= 0) {

cout << i+1 << ' ';

i--;

}

cout << endl;

b) int j=8;

do {

cout << '-';

j--;

if (j<5) continue;

cout << '+';

} while (j);

cout << endl;

c) int x=1, max=1;

for (int k=0; k<3 ; k+=1){

while (x <= max) cout << x++;

cout << endl;

max++;

x=1;

}

2. Czy poniższe pętle są równoważne, w sensie wypisywanych wyników, z iteracją z Testu 1 a)?

a) for (int i=7, k=2 ; ; k++){

cout << i-k+1 << ' ';

if (k > i-1) break;

}

b) int i=5;

do {

cout << i+1 << ' '; i-=1;

} while (i);

c) int k=2;

for (k--, k--; k<6; cout << 6-k << ' ', k+=1);

3. Które pętle kompilator uzna za nieprawidłowe ze względu na błędy syntaktyczne?

a) for(;0;)for(;1;)for(;2;);

b) for(int a=11, b=a+4; ; b-=1, cout<<b);

c) for(int a=2,,a++) if(a==2) break;

d) while(1)(cout << "hello\n"; break;)

e) do {cout << "witaj\n"; break;} while{1};

S t r o n a | 4 WETI Politechnika Gdańska, POP v.1.5.2

4. Wskaż pętle, które nigdy się nie kończą:

Pętla 1:

int k = 12; while (k){ cout << "Jupi!\n"; }

Pętla 2:

for (int i = 0; i < 100; i = i-2)

cout << ":) ";

Pętla 3:

int progress = 1;

cout << "Loading, please wait";

do {

cout << ".";

if (progress == 100) break;

progress = progress + 2;

} while (1);

Pętla 4:

int j = 15;

do {

if (j%2 == 0) j = j-2;

else j = j-1;

} while (j);

Pętla 5:

int j = 15;

do {

if (j%2 == 0) j = j-2;

else j = j-1;

} while (j = 8);

5. Podaj liczby, jakie należy wczytać w poniższych pętlach, aby pętle zakończyły się:

a) int c, d;

do {

cin >> c >> d;

d = d+1;

} while ( !(c==d-3 and d==5));

b) int m, n;

do {

cin >> m >> n;

} while ( n!=m-2 or m!=5);

Odpowiedzi

Test 1:

a)

6 5 4 3 2 1

b)

-+-+-+-----

c)

1

12

123

Test 2: a) tak, b) nie, c) tak.

Test 3: a), b) – prawidłowe; c), d), e) – nieprawidłowe.

Test 4: Pętle 1, 2, 3 i 5 nie kończą się. Pętla 4 kończy się dla każdej początkowej dodatniej liczby j .

Test 5: a) c = 2, d = 4, b) m = 5, n = 3.

S t r o n a | 5 WETI Politechnika Gdańska, POP v.1.5.2

Zadania przygotowujące do laboratorium

Napisz, uruchom i przetestuj następujące programy

Zadanie L3_F0_Z1

Napisz program wyznaczający sumę n iloczynów liczb naturalnych o postaci:

= 1 ∙ 2 + 2 ∙ 3 + ⋯ + ( + 1)

gdzie n jest podawane z klawiatury przez użytkownika.

Program nie wykonuje obliczeń i podaje komunikat "Zła wartość n", gdy n nie należy do przedziału [2, 100].

Przykład:

dla n = 2 wynik jest w postaci:

= 1 ∙ 2 + 2 ∙ 3 = 6

dla n = 10 wynik jest w postaci:

= 1 ∙ 2 + 2 ∙ 3 + ⋯ + 10 ∙ 11 = 440

Polecenie: W podanym niżej szkielecie programu L3_F1_S1.cpp uzupełnij linie oznaczone przez //$$$. Nie zmieniaj pozostałych części szkieletu.

// L3_F0_S1.cpp Suma iloczynow – Iteracja ograniczona

#include<iostream>

using namespace std;

int main()

{

int i; // licznik petli

int n; // dana wejsciowa n

int suma ; // suma iloczynow

//---------------------------------------------------------------------------------

cout << "Program wyznacza sume 1*2+2*3+...+n*(n+1)" << endl;

cout << endl << "Podaj wartosc n: "; //$$$ wczytanie n

if ( ) { //$$$ czy n jest poprawne?

suma = ; //$$$

i = ; //$$$

while( ) { //$$$ petla - obliczanie sumy

suma = //$$$

i++; // to samo co i=i+1; albo i += 1;

}

cout << endl << " 1*2+...+" << << '*' << << " = " << << endl; //$$$

}

else

cout << endl << << endl; //$$$

//---------------------------------------------------------------------------------

cout << endl;

system ("pause");

return 0;

}

S t r o n a | 6 WETI Politechnika Gdańska, POP v.1.5.2

Rozwiązanie

// L3_F0_Z1.cpp Suma iloczynow – Iteracja ograniczona

#include<iostream>

using namespace std;

int main()

{

int i; // indeks petli

int n; // dana wejsciowa n

int suma ; // suma iloczynow

//-----------------------------------------------------------------------------------

cout << "Program wyznacza sume 1*2+2*3+...+n*(n+1)" << endl;

cout << endl << "Podaj wartosc n: "; cin >> n; //$$$

if ( n >= 2 && n <= 100 ) { //$$$

suma = 0 ; //$$$

i = 1 ; //$$$

while( i <= n ) { //$$$

suma = suma + i*(i+1);

i++;

}

cout << endl << " 1*2+...+" << n << '*' << n+1 << " = " << suma << endl; //$$$

}

else cout << endl << "Zla wartosc n" << endl; //$$$

//------------------------------------------------------------------------------------

cout << endl;

system ("pause");

return 0;

}

Zadanie L3_F0_Z2

Napisz program, który na przemian dodaje i odejmuje kolejno wczytane liczby całkowite, aż do wczytania liczby zero. Po podaniu każdej kolejnej liczby program wyświetla aktualną sumę.

// L3_F0_Z2.cpp Program dodaje i odejmuje (na przemian) liczby - Iteracja warunkowa

#include<iostream>

using namespace std;

int main( )

{

int liczba;

int suma = 0;

int znak = 1;

do {

if (znak > 0) cout << "Dodaj ";

else cout << "Odejmij ";

cout << "liczbe calkowita: ";

cin >> liczba;

suma = suma + znak*liczba;

cout << "Suma: "<< suma << endl << endl;

znak = -znak;

} while( liczba != 0 );

system("pause");

return 0;

}

S t r o n a | 7 WETI Politechnika Gdańska, POP v.1.5.2

Zadanie L3_F0_Z3

Napisz program rysujący trójkąt prostokątny równoramienny za pomocą znaku ’o’. Długość przyprostokątnych zadaje użytkownik jako liczbę całkowitą w zakresie od 1 do 30. Program sprawdza poprawność wczytanej liczby.

Przykład: Wynik działania programu dla n = 7

ooooooo

oooooo

ooooo

oooo

ooo

oo

o

// L3_F0_Z3.cpp Rysowanie Trojkata - Petle zagniezdzone

#include<iostream>

using namespace std;

int main()

{

char znak = 'o';

int n; // licznik linii = dlugosc przyprostokatnej

cout << "Podaj dlugosc przyprostokatnej [1, 30]: ";

cin >> n;

if( (n < 1) || (n > 30) )

{

cout << "Blad: dlugosc przyprostokatnej spoza zakresu" << endl;

system("pause");

exit(0);

}

// ----------------------------------------------------------rysowanie-------

while (n) { // petla liczaca linie, petla trwa dopoki n rozne od zera

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

cout << znak; // petla rysujaca jedna linie

cout << endl; // przejdz do nastepnej linii

n--; // zmniejsz licznik n = n-1;

}

system("pause");

return 0;

}

Zadanie L3_F0_Z4

Niech

= 1

oraz

1

=

+

Dla danego n (liczba naturalna, 0 < n < 11) obliczyć n pierwszych wyrazów ciągu liczb rzeczywistych

, … , oraz

wyświetlić je w kolejnych liniach na ekranie z dokładnością 4 miejsc po przecinku.

Przykład:

Wejście:

Wyjście:

3

2.0000

4.5000

13.8333

S t r o n a | 8 WETI Politechnika Gdańska, POP v.1.5.2

//L3_F0_Z4.cpp - Wypisywanie wyrazow ciagu wg wzoru

#include<iostream>

#include<iomanip>

using namespace std;

int main ()

{

int n, i=1;

double a=1;

cout << "Podaj n : " ;

cin >> n;

if( n<=0 || n>=11 )

{

cout << "zle dane\n\n";

system("PAUSE");

exit(0);

}

cout << fixed << setprecision(4);

while (n--)

{ // wartosc a po prawej stronie wyrazenia jest elem. ciagu z poprzedniej iteracji, a = a*i + 1./i; // natomiast po lewej stronie jest elementem ciagu wyznaczanym w biezacej iteracji i = i + 1;

cout << setw(15) << a << endl;

}

system("PAUSE");

return 0;

}

Zadania do samodzielnego rozwiązania po laboratorium

Zadanie L3_F3_Z1

Oblicz przybliżoną wartość funkcji sinus dla kąta xr podanego w radianach jako liczba rzeczywista. Przybliżenie wartości funkcji wyznacz na podstawie jej rozwinięcia w szereg potęgowy:

( ) = 1 − 3! + 5! −...=

= # + #$ + # + …

Iteracje są kontynuowane dopóki wyraz wi szeregu jest większy od zadanej dokładności eps. Skorzystaj z faktu, że

'(

= 1

# = &

2

*

−1 ∙ #

−1 (2 − 2)(2 − 1) '(

> 1

co oznacza, że kolejny wyraz szeregu można obliczyć na podstawie wartości wyrazu poprzedniego.

Wejście: xr oraz eps (obie liczby rzeczywiste np. xr = 30.0, eps = 1e-8).

Wyjście: wartości funkcji sinus wyznaczone odpowiednio na podstawie szeregu oraz przy pomocy funkcji sin z biblioteki <cmath>. Obie liczby wypisz w formacie wykładniczym (10 miejsc po przecinku). Podaj liczbę wyrazów szeregu większych od eps.

Testując program porównaj wyniki dla różnych wartości eps.

S t r o n a | 9 WETI Politechnika Gdańska, POP v.1.5.2

Zadanie L3_F3_Z2

Napisz program tablicujący funkcję sinus w zakresie od 0 do 360 stopni z krokiem 5 stopni. Obok wartości kąta w stopniach oraz wartości funkcji wypisywanych w równych kolumnach, z lewej strony ekranu należy dodatkowo rysować szkic wykresu funkcji wykorzystując znak ’*’ (patrz rysunek). Przesunięcie gwiazdki o odpowiednią ilość znaków wylicz na podstawie wartości funkcji. Przyjmij, że przedział [-1,1], do którego należą wartości funkcji, odpowiada 60 znakom na ekranie.

Przykład:

x sin(x) -1 0 1

-----------------------------------------------------------------------------

0.000 0.000 *

5.000 0.087 *

10.000 0.174 *

15.000 0.259 *

20.000 0.342 *

25.000 0.423 *

30.000 0.500 *

. . . .

. . .

Zadanie L3_F3_Z3

Ciąg Collatz’a jest ciągiem danym następującym wzorem

1

+

*

, = - 2 +

.'/ + 01 2 3 45/ 2

3+ + 1 .'/ + 01 2 13 45/ 2

Zauważ, że kolejne wyrazy ciągu Collatz’a mogą przyjmować różne wartości w zależności od wartości pierwszego wyrazu c 0.

Przykład:

c 0 = 20 C20 = (20, 10, 5, 16, 8, 4, 2, 1)

c 0 = 19 C19 = (19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, . . . , 1)

c 0 = 18 C18 = (18, 9, 28, 14, 7, 22, . . . , 1)

Napisz program, który wypisuje elementy ciągu Collatz’a zaczynającego się od podanej na wejściu programu liczby całkowitej c 0 będącej pierwszym wyrazem ciągu. Program powinien zakończyć wypisywanie, gdy wartość kolejnego wyrazu będzie miała wartość równą 1.

Zadanie L3_F3_Z4

Hipoteza Collatz’a mówi, że niezależnie od jakiej liczby c 0 zaczniemy, zawsze dojdziemy do wyrazu o wartości równej 1. Napisz program, który dla każdego 1 < c 0 < 104 sprawdza, czy hipoteza Collatz’a jest prawdziwa.

Hipotezę udowodniono dotychczas dla liczb mniejszych niż 20*258. Program powinien wyznaczyć c 0, z podanego powyżej zakresu, dla którego wygenerowany ciąg ma najwięcej elementów oraz podać liczbę tych elementów.

Zastanów się czy jest gwarancja, że program, który napisałeś w zadaniu L3_F3_Z3 jest poprawny? Czy zatrzyma się on dla każdych danych wejściowych? Jeżeli nie, to jak można zabezpieczyć się przed jego ewentualnym

„zapętleniem”, czyli nieskończonym działaniem?

S t r o n a | 10 WETI Politechnika Gdańska, POP v.1.5.2