[WikiDyd] [TitleIndex] [WordIndex

Materiały zostały opracowane w ramach realizacji Programu Rozwojowego Politechniki Warszawskiej.

http://prpw.iem.pw.edu.pl/images/KAPITAL_LUDZKI.gif http://prpw.iem.pw.edu.pl/images/EU+EFS_P-kolor.gif

http://www.pr.pw.edu.pl/ jest projektem współfinansowanym przez Unię Europejską w ramach Europejskiego Funduszu społecznego (działanie 4.1.1 Programu Operacyjnego Kapitał Ludzki) i ma na celu poprawę jakości kształcenia oraz dostosowanie oferty dydaktycznej Politechniki Warszawskiej do potrzeb rynku pracy. Będzie on realizowany przez Uczelnię w latach 2008-2015.


Laboratorium metodyki programowania

Ćwiczenie 5: Uruchamianie programów

Scenariusz

  1. Student loguje się do systemu LOP (Laboratorium Otwartego Programowania).
  2. Student przechodzi do katalogu roboczego dla zajęć LMP.
  3. Student pobiera z repozytorium kod programów.
  4. Po wyjaśnieniach prowadzącego student przystępuje do kompilacji i uruchomienia programów. Porównuje wynik działania z kodem źródłowym.
  5. Student modyfikuje programy według wskazówek prowadzącego (patrz opis szczegółowy). Powtarza próbę kompilacji i uruchomienia.
  6. Ćwiczenie zaliczeniowe: student rozszerza funkcjonalność programu według zamówienia prowadzącego.

Opis szczegółowy

W trakcie zajęć nauczymy się podstaw sprawdzania, czy nasz program działa poprawnie i postępowania w sytuacji, kiedy z początku jest inaczej.

Rozpoczęcie pracy

Zajęcia będą prowadzone na komputerze stud.iem.pw.edu.pl. W celu rozpoczęcia pracy należy zalogować się na tym komputerze za pomocą usługi ssh. Można w tym celu wykorzystać albo dostępny w każdym praktycznie systemie Unix/Linux program ssh albo klienta SSH dla systemu Windows - PuTTY. Na maszynach studenckich w IETiSIP PW można podnieść różne dystrybucje Uniksa/Linuksa lub system Windows, ale w każdej z nich jest zainstalowane oprogramowanie ssh.

Do logowania na maszynie stud.iem.pw.edu.pl należy użyć takiego samego loginu i hasła jakie wykorzystywane sa do dostępu do usług wydziałowych (poczta, e-dziekanat, itd.).

Po zalogowaniu się należy przejść do utworzonego na pierwszych zajęciach katalogu lmp:

 cd lmp

Kod do zajęć

lmp5.tgz

Praca nad kodem

Program

Celem zajęć będzie uruchomienie programów porównujących szybkość działania dwóch algorytmów sortowania wektorów.

Pierwszy algorytm to tak zwane ,,sortowanie przez wstawianie".

Sortowanie przez wstawianie - Insertion Sort (insort)

Insort to algorytm iteracyjny. Przegląda wektor poczynając od drugiego elementu i w każdej iteracji dokłada rozważany element do już posortowanego wektora.

Rozważmy dla przykładu wektor składający się z siedmiu elementów:

v= [ 3.0, 2.1, 1.4, 4.6, 7.7, 5.8, 6.1 ]

Pierwsza iteracja

i= 1; // v[i] == 2.1

Rozpoczynamy od drugiego elementu (2.1). Przed nim znajduje się ,,już posortowany" jednoelementowy wektor [3.0]. Naszym zadaniem jest przejrzenie tego wektora, poczynając od ostatniego elementu, a kończąc na takim, który jest mniejszy od elementu rozpatrywanego (czyli 2.1). Ponieważ posortowany wektor się wydłuży, będziemy potrzebowali miejsce na nowy element - aby je przygotować, kopiujemy rozpatrywany element do zmiennej tymczasowej. Przeglądając elementy przesuwamy te, które okażą się większe od zmiennej tymczasowej o jedno miejsce w prawo. W ten sposób, gdy znajdziemy element nie większy od sprawdzanego, będziemy mogli wstawić za niego zmienną tymczasową. Nasza pierwsza iteracja wygląda więc tak:

v= [ 3.0, 2.1, 1.4, 4.6, 7.7, 5.8, 6.1 ]
tmp= 2.1
v= [ 3.0, 3.0, 1.4, 4.6, 7.7, 5.8, 6.1 ]
v= [ 2.1, 3.0, 1.4, 4.6, 7.7, 5.8, 6.1 ]

Druga iteracja

i= 2; // v[i] == 1.4

Trzeci element to 1.4. Przed nim znajduje się już posortowany dwuelementowy wektor [2.1, 3.0]. Postępujemy podobnie, jak poprzednio:

v= [ 2.1, 3.0, 1.4, 4.6, 7.7, 5.8, 6.1 ]
tmp= 1.4
v= [ 2.1, 3.0, 3.0, 4.6, 7.7, 5.8, 6.1 ]
v= [ 2.1, 2.1, 3.0, 4.6, 7.7, 5.8, 6.1 ]
v= [ 1.4, 2.1, 3.0, 4.6, 7.7, 5.8, 6.1 ]

Trzecia iteracja

i= 3; // v[i] == 4.6

v= [ 1.4, 2.1, 3.0, 4.6, 7.7, 5.8, 6.1 ]
tmp= 4.6
v= [ 1.4, 2.1, 3.0, 4.6, 7.7, 5.8, 6.1 ]

A oto funkcja w C, która realizuje ten algorytm (UWAGA: funkcja zawiera błędy składniowe i merytoryczne - zadaniem Państwa jest ich znalezienie i poprawienie).

   1 #include "insort.h"
   2 
   3 void
   4 insort (double v[], int n)
   5 {
   6   int i, j;
   7   for (i = 1; i < n; i++) {
   8     double tmp = v[j];
   9     for (j = i-1; v[j] > tmp, j--)
  10       v[j+1] = v[j];
  11     v[j+1] = tnp;
  12   }
  13 }

Kolejny plik to program testujący funkcję:

   1 #include "insort.h"
   2 
   3 #include <stdio.h>  // wiadomo po co
   4 #include <stdlib.h> // funkcje atoi, malloc i rand
   5 #include <time.h>   // funkcja time
   6 
   7 void printv( double v[], int n ) {  // wypisuje wektor na stdout
   8   int i;
   9   int np=  n > MAX_ELEM_2_PRINT ? MAX_ELEM_2_PRINT : n;  // UWAGA: przy kompilacji trzeba zdefiniować MAX_ELEM_2_PRINT
  10                                                          //   np.: -DMAX_ELEM_2_PRINT=10
  11   printf( "[" );
  12   for( i= 0; i < np; i++ )
  13     printf( " %g", v[i] );
  14   printf( "%s ]\n", np < n ? " ... " : "" );
  15 }
  16 
  17 int
  18 main( int argc, char **argv ) {
  19   int n= argc > 1 ? atoi( argv[1] ) : 1000;
  20   int i;
  21 
  22   double *v= malloc( n * sizeof *v );
  23 
  24   srand( argc > 2 ? atoi(argv[2]) : time(NULL) );
  25 
  26   for( i= 0; i < n; i++ )
  27     v[i] = rand() *10 / (double)RAND_MAX * n;
  28 
  29   printf( "Wygenerowany wektor: " );
  30   printv( v, n );
  31 
  32   insort( v, n );
  33 
  34   printf( "Posortowany wektor: " );
  35   printv( v, n );
  36 
  37   for( i= 1; i <n; i++ )
  38     if( v[i-1] > v[i] )
  39       fprintf( stderr, "Zly algorytm sortowania: v[%d]==%g > v[%d]==%g\n", i-1, v[i-1], i, v[i] );
  40 
  41   return 0;
  42 }

Pierwsza próba kompilacji powinna się zakończyć mniej-więcej takim efektem:

oer:~/tmp/lmp5/gr1> cc insort.c
insort.c: In function ‘insort’:
insort.c:9: error: expected ‘;’ before ‘)’ token
insort.c:11: error: ‘tnp’ undeclared (first use in this function)
insort.c:11: error: (Each undeclared identifier is reported only once
insort.c:11: error: for each function it appears in.)
oer:~/tmp/lmp5/gr1>

Po usunięciu błędów składniowych:

oer:~/tmp/lmp5/gr1> cc insort.c -DMAX_ELEM_2_PRINT=16 test.c
oer:~/tmp/lmp5/gr1> ./a.out 10
Wygenerowany wektor: [ 9.75783 -8.42358 -6.12913 2.59683 -0.755495 -7.61919 4.50464 -8.28581 9.92636 -8.88772 ]
[1]    15719 segmentation fault  ./a.out 10
oer:~/tmp/lmp5/gr1> ./a.out 20
Wygenerowany wektor: [ -7.02152 -10.7925 -3.32976 -10.0877 19.163 -2.85997 11.8226 7.18634 0.406509 9.79747 -3.50272 7.07695 1.63055 -19.5686 -2.62934 -16.7263 ...  ]
[1]    15720 segmentation fault  ./a.out 20
oer:~/tmp/lmp5/gr1>

Kompilujemy z opcją powodującą wygenerowanie kodu z informacjami wykorzystywanymi przez debugger:

oer:~/tmp/lmp5/gr1> cc -ggdb insort.c -DMAX_ELEM_2_PRINT=16 test.c

Uruchamiamy debugger i prosimy go o informację o dostępnych komendach:

oer:~/tmp/lmp5/gr1> gdb a.out
GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/jstar/tmp/lmp5/gr1/a.out...done.
(gdb) help
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
(gdb)

Ustawiamy zatrzymywanie programu po każdej instrukcji funkcji main i uruchamiamy go (zwróćmy uwagę na to, że argumenty linii poleceń podajemy po komendzie run):

(gdb) break main
Breakpoint 1 at 0x4008c8: file test.c, line 19.
(gdb) run 10
Starting program: /home/jstar/tmp/lmp5/gr1/a.out 10
Breakpoint 1, main (argc=2, argv=0x7fffffffdca8) at test.c:19
19              int n= argc > 1 ? atoi( argv[1] ) : 1000;
(gdb)

Przechodzimy do następnej linii i sprawdzamy, czy prawidłowo odczytana została wartość argumentu z linii poleceń:

(gdb) next
22              double *v= malloc( n * sizeof *v );
(gdb) p n
$3 = 10
(gdb)

Ponieważ zbliżamy się do pętli, która będzie wykonywana 10 razy i nie mamy ochoty pisać 20 razy ,,next", a więc sprawdzamy, czy komenda ta może byc użyta do wykonania więcej, niż jednej instrukcji programu:

(gdb) help next
Step program, proceeding through subroutine calls.
Like the "step" command as long as subroutine calls do not happen;
when they do, the call is treated as one instruction.
Argument N means do this N times (or till program stops for another reason).
(gdb)

Wykorzystujemy zdobytą wiedzę do przesunięcia się na koniec pętli (n to skrótowa wersja komendy next):

(gdb) n 21
30              printv( v, n );
(gdb) p i
$5 = 10
(gdb)

Kolejne polecenie n przeniesie nas za funkcję printv (gdybyśmy chcieli ,,wejść do kodu" tej funkcji, należałoby użyć polecenia step - lub s):

(gdb) n
Wygenerowany wektor: [ -1.1978 4.37923 -0.245998 -0.374274 -6.2296 -3.21673 7.53597 -7.75684 -0.161619 -2.96828 ]
32              insort( v, n );
(gdb)

Kolejnym poleceniem próbujemy ,,przeskoczyć" funkcję insort i...

(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x00000000004007a8 in insort (v=0x602010, n=10) at insort.c:8
8           double tmp = v[j];
(gdb)

Widzimy, że program wykonał niedoswoloną operację (Segmentation fault czyli ,,mazanie po pamięci") w ósmej linii pliku insort.c. Aby zdiagnozować sytuację dokładniej, uruchomimy program jeszcze raz od początku. Usuwamy stare ,,breakpointy":

(gdb) delete break
Delete all breakpoints? (y or n) y
(gdb)

i uruchamiamy ponownie:

(gdb) run 10
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/jstar/tmp/lmp5/gr1/a.out 10
Wygenerowany wektor: [ -0.969184 -3.55095 -6.55481 -5.84094 -3.20608 2.03404 -8.70428 -1.95726 5.92311 -2.13613 ]

Program received signal SIGSEGV, Segmentation fault.
0x00000000004007a8 in insort (v=0x602010, n=10) at insort.c:8
8           double tmp = v[j];
(gdb)

Widzimy, że tym razem niekontrolowany program od razu doszedł do (zapewne błędnej) linii 8 w pliku insort.c i ponownie próbował uzyskać dostęp do zabronionego adresu. Błąd jest, jak widać, powtarzalny (to bardzo ułatwia jego lokalizację). Uruchamiamy program po raz trzeci, ale wcześniej zmusimy debugger do zatrzymywania się przed wykonywaniem instrukcji w funkcji insort:

(gdb) run 10
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/jstar/tmp/lmp5/gr1/a.out 10
Wygenerowany wektor: [ 1.79327 6.07372 8.12923 8.54245 8.10269 1.1992 -1.50055 -4.0823 9.37633 2.35013 ]

Breakpoint 2, insort (v=0x602010, n=10) at insort.c:7
7         for (i = 1; i < n; i++) {
(gdb)

Program został teraz zatrzymany przed wejściem do głównej pętli funkcji sortującej. Wchodzimy do tej pętli i sprawdzamy wartość indeksu tablicy używanego tam:

(gdb) n
8           double tmp = v[j];
(gdb) p j
$6 = 32767
(gdb)

Wartość 32767 jest, z naszego punktu wodzenia, przypadkowa - nasz wektor składa się z 10 elementów, a więc dopuszczalne wartości indeksów leżą w przedziale [0...9]. Po chwili namysłu (albo i wcześniej) dochodzimy do wniosku, że zmienna tmp powinna być przecież inicjowana wartością v[i] a nie v[j] - to jest właśnie błąd, który znaleźliśmy. Możemy opuścić debuger:

(gdb) quit
A debugging session is active.

        Inferior 3 [process 15997] will be killed.

Quit anyway? (y or n) y
oer:~/tmp/lmp5/gr1>

Poprawiamy program, kompilujemy, uruchamiamy ponownie i...

oer:~/tmp/lmp5/gr1> cc -ggdb insort.c -DMAX_ELEM_2_PRINT=16 test.c
oer:~/tmp/lmp5/gr1> ./a.out 10
Wygenerowany wektor: [ 9.78929 -2.71147 3.41612 -0.353085 -5.64247 2.70303 -8.44685 4.41659 -4.45201 -2.80339 ]
[1]    16012 segmentation fault  ./a.out 10

...dalej źle, ale czy w tym samym miejscu? Używamy debuggera, aby to sprawdzić:

oer:~/tmp/lmp5/gr1> gdb a.out
GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/jstar/tmp/lmp5/gr1/a.out...done.
(gdb) run
Starting program: /home/jstar/tmp/lmp5/gr1/a.out
Wygenerowany wektor: [ 238.989 -87.1895 -413.973 781.375 506.871 151.881 655.411 -996.003 -941.633 350.069 476.9 -119.813 710.08 -856.707 463.277 -344.718 ...  ]

Program received signal SIGSEGV, Segmentation fault.
0x00000000004007dc in insort (v=0x602010, n=1000) at insort.c:10
10            v[j+1] = v[j];
(gdb)

Okazuje się, że jest to inny błąd, który możemy lepiej zrozumieć używając opisanego powyżej sposobu postępowania (zatrzymanie przed linią 10 pliku insort.c i sprawdzenie wartości indeksów). Pozostawiamy to jednak Państwu do samodzielnego wykonania.

Przejdźmy teraz do prezentacji kolejnego algorytmu.

Sortowanie szybkie - Quick Sort (qsort)

Sortowanie szybkie to algorytm, który najłatwiej jest zrozumieć analizując jego wersję rekurencyjną. Jej zasadniczy kod jest bardzo prosty (a diabeł tkwi, niestety, w szczegółach). Rozważmy ten sam wektor przykładowy:

v= [ 3.0, 2.1, 1.4, 4.6, 7.7, 5.8, 6.1 ]

Każdy rekurencyjny krok algorytmu qsort składa się z trzech faz:

  1. Podział wektora na elementy mniejsze i większe od wybranego - dla prostoty użyjmy pierwszego elementu jako ,,wybranego" - co w powyższym przykładzie prowadzi do sytuacji:
    • v = [ 2.1, 1.4, 3.0, 7.7, 4.6, 5.8, 6.1 ]
  2. Zastosowanie algorytmu qsort dla pierwszego podwektora:
    • qsort( [2.1, 1.4] )
  3. Zatosowanie algorytmu qsort dla drugiepo podwektora:
    • qsort( [7.7, 4.6, 5.8, 6.1 ] )

co możemy zapisać w postaci następującej funkcji (qsort_rec):

   1 # include "q_sort.h"
   2 
   3 int divide( double v[], int f, int l ); // zajmiemy się tym za chwilę
   4 
   5 void qsort_rec( double v[], int first, int last ) {
   6                 // wygodniej jest zapisać tę funkcję rekurencyjną
   7                 // posługując się indeksem początku i końca wektora
   8    if( first < last ) {
   9       int m= divide( v, first, last );
  10       qsort_rec( v, first, m-1 );
  11       qsort_rec( v, m+1, last );
  12    }
  13 }
  14 
  15 void q_sort( double v[], int n ) {  // to jest tylko opakowanie, aby wywołanie
  16                                    // wyglądało tak samo, jak insort
  17    qsort_rec( v, 0, n-1 );
  18 }

Uwaga: nasza funkcja-opakowanie upodobniająca wywołanie qsort_rec do wywołania funkcji insort nazywa się q_sort dlatego, że w bibliotece standardowej języka C znajduje się funkcja qsort pozwalająca sortować algorytmem szybkim wektory o dowolnej zawartości.

Napiszmy teraz funkcję rozdzielającą wektor według pierwszego elementu. Podobnie, jak poprzednio, nie jest to funkcja poprawna, a zadaniem Państwa będzie znalezienie i poprawienie błędów. Idea jest prosta: używamy dwóch indeksów, z których jeden (f) posuwa się do początku, a drugi (l) od końca wektora. Indeks f pomija elementy mniejsze od pierwszego, a l - elementy większe. Jeśli po takich operacja indeksy f i l są różne, to znaczy, że należy zamienić miejscami elementy v[f] i v[l], po czym kontynuować operację sprawdzania:

   1 int divide( double v[], int f, int l ) {
   2   double tmp; // zmienna tymczasowa do zmiany elementów
   3   int s= f;   // dzielimy ze względu na pierwszy element
   4   f++;        // opuść pierwszy element
   5   while( f < l ) {
   6     while( f < l && v[f] < v[s] ) // przeskocz < v[s]
   7       f++;
   8     while( f < l && v[l] >= v[s] ) // przeskocz >= v[s]
   9       l--;
  10     if( f < l ) { // zamień v[f] i v[l], gdy są różne
  11       tmp= v[f];
  12       v[f]= v[l];
  13       v[l]= tmp;
  14     }
  15   }
  16   // zamieniamy element v[s] z v[f]
  17   // aby v[s] znalazł się pomiędzy
  18   // mniejszymi i nie-większymi od niego
  19   tmp = v[s];
  20   v[s] = v[f];
  21   v[f] = tmp;
  22   return f;
  23 }

Po skompilowaniu i uruchomieniu otrzymujemy oczywiście informacje o błędnym sortowaniu - trudno oczekiwać innych wyników, skoro funkcja divide jest błędna:

oer:~/tmp/lmp5/gr1> cc -ggdb q_sort.c -DMAX_ELEM_2_PRINT=10 qtest.c
oer:~/tmp/lmp5/gr1> ./a.out 12
Wygenerowany wektor: [ -11.4412 -2.52017 -8.29605 -0.217485 4.42139 10.7231 10.6983 -1.21285 -9.61173 10.3299 ...  ]
Posortowany wektor: [ -2.52017 -11.4412 -9.61173 4.42139 -8.29605 10.6983 -7.83603 -0.217485 -1.21285 10.3299 ...  ]
Zly algorytm sortowania: v[0]==-2.52017 > v[1]==-11.4412
Zly algorytm sortowania: v[3]==4.42139 > v[4]==-8.29605
Zly algorytm sortowania: v[5]==10.6983 > v[6]==-7.83603
Zly algorytm sortowania: v[7]==-0.217485 > v[8]==-1.21285
oer:~/tmp/lmp5/gr1> ./a.out 12
Wygenerowany wektor: [ -2.16123 -9.57884 -6.7345 -4.6326 -7.07104 -5.06879 9.11687 9.28155 5.19307 5.39595 ...  ]
Posortowany wektor: [ -9.57884 -6.7345 -7.07104 -4.6326 -5.06879 9.11687 -2.16123 5.19307 -2.04108 7.89474 ...  ]
Zly algorytm sortowania: v[1]==-6.7345 > v[2]==-7.07104
Zly algorytm sortowania: v[3]==-4.6326 > v[4]==-5.06879
Zly algorytm sortowania: v[5]==9.11687 > v[6]==-2.16123
Zly algorytm sortowania: v[7]==5.19307 > v[8]==-2.04108
Zly algorytm sortowania: v[9]==7.89474 > v[10]==5.39595
oer:~/tmp/lmp5/gr1>

Uwaga: program qtest.c musicie Państwo napisac samodzielnie. Jest to zadanie trywialne - wystarczy skopiować test.c i poprawić w nim dwie linie, zmieniając insort na q_sort.

Zwróćmy uwagę, że błędy nie są powtarzalne - zależą od zawartości (wartości elementów) sortowanego wektora. Powtarzalność możemy uzyskać inicjując generator liczb losowych ciągle tą samą wartością - służy do tego drugi argument z linii poleceń:

oer:~/tmp/lmp5/gr1> ./a.out 12 0
Wygenerowany wektor: [ 4.82253 -0.674049 -2.02809 -0.187196 -10.6023 -0.293836 -7.77327 -3.81245 9.33297 -5.52361 ...  ]
Posortowany wektor: [ -7.77327 -10.6023 -0.187196 -5.52361 -3.81245 -0.674049 -2.02809 3.46451 -0.293836 9.28765 ...  ]
Zly algorytm sortowania: v[0]==-7.77327 > v[1]==-10.6023
Zly algorytm sortowania: v[2]==-0.187196 > v[3]==-5.52361
Zly algorytm sortowania: v[5]==-0.674049 > v[6]==-2.02809
Zly algorytm sortowania: v[7]==3.46451 > v[8]==-0.293836
Zly algorytm sortowania: v[9]==9.28765 > v[10]==4.82253
oer:~/tmp/lmp5/gr1> ./a.out 12 0
Wygenerowany wektor: [ 4.82253 -0.674049 -2.02809 -0.187196 -10.6023 -0.293836 -7.77327 -3.81245 9.33297 -5.52361 ...  ]
Posortowany wektor: [ -7.77327 -10.6023 -0.187196 -5.52361 -3.81245 -0.674049 -2.02809 3.46451 -0.293836 9.28765 ...  ]
Zly algorytm sortowania: v[0]==-7.77327 > v[1]==-10.6023
Zly algorytm sortowania: v[2]==-0.187196 > v[3]==-5.52361
Zly algorytm sortowania: v[5]==-0.674049 > v[6]==-2.02809
Zly algorytm sortowania: v[7]==3.46451 > v[8]==-0.293836
Zly algorytm sortowania: v[9]==9.28765 > v[10]==4.82253
oer:~/tmp/lmp5/gr1

Poprawienie funkcji divide pozostawiamy Państwu do samodzielnego wykonania.

Zaliczenie

Przedstawienie raportu (odręcznych notatek opisujących testy, wykryte błędy i sposób ich poprawienia).


To już prawie wszystko, ale nie wolno nam zapomnieć, że

materiały zostały opracowane w ramach realizacji Programu Rozwojowego Politechniki Warszawskiej.

http://prpw.iem.pw.edu.pl/images/KAPITAL_LUDZKI.gif http://prpw.iem.pw.edu.pl/images/EU+EFS_P-kolor.gif

http://www.pr.pw.edu.pl/ jest projektem współfinansowanym przez Unię Europejską w ramach Europejskiego Funduszu społecznego (działanie 4.1.1 Programu Operacyjnego Kapitał Ludzki) i ma na celu poprawę jakości kształcenia oraz dostosowanie oferty dydaktycznej Politechniki Warszawskiej do potrzeb rynku pracy. Będzie on realizowany przez Uczelnię w latach 2008-2015.


2015-09-23 06:44