[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 4: Przetwarzanie danych numerycznych

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 implementacji w języku C prostych metod i algorytmów numerycznych. Napiszemy trzy programy, które będą operować na danych liczbowych.

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ęć

lmp4.tgz

Praca nad kodem

Pierwszy program

Pierwszy program będzie składał się z trzech plików. Pierwszy z nich (bibl.c) to prościutka biblioteka zawierająca kilka funkcji operujących na wektorach:

   1  #include "bibl.h"              
   2 
   3 #include <math.h>               /* dzieki temu kompilator sprawdzi, czy
   4                                  * dobrze uzywamy funkcji sqrt */      
   5 
   6 /* Definicje funkcji */
   7 
   8 /*
   9  * read: czyta z pliku p wektor double v[], nie dluzszy niz max_l_e elementow
  10  * w v musi byc miejsce na co najmniej n elementow zwraca liczbe             
  11  * przeczytanych elemento w razie sukcesu, -1 w razie bledu                  
  12  *                                                                           
  13  * Format wektora w pliku - np.:  5 [ 0.1 1.2 2.3 3.4 4.5 ]                  
  14  */                                                                          
  15 int                                                                          
  16 read(FILE * p, double v[], int max_l_e)                                      
  17 {                                                                            
  18         int             n, i;   /* zmienne robocze */                        
  19 
  20         if (fscanf(p, "%i", &n) != 1 || n <= 0 || n > max_l_e) {        /* probujemy przeczytac
  21                                                                          * liczbe elementow */
  22                 /*
  23                  * nie udalo sie (fscanf zwrocila wartosc inna niz 1 lub
  24                  * wczytana wartosc jest niedodatnia albo zbyt duza )
  25                  */
  26                 return -1;
  27         }
  28         while ((i = fgetc(p)) == ' ')   /* pomijamy spacje po liczbie
  29                                          * elementow */
  30                 ;
  31 
  32         /* mamy znak rozny od spacji - powinien to byc znak '[' */
  33         if (i != '[')
  34                 return -1;      /* nie jest - bledny format pliku */
  35 
  36         /* teraz czytamy n liczb */
  37         for (i = 0; i < n; i++)
  38                 if (fscanf(p, "%f", &(v[i])) != 1)      /* fscanf powinno zawsze
  39                                                          * zwracac 1 - jesli
  40                                                          * zwroci cos innego */
  41                         return -1;      /* to znaczy, ze format pliku jest
  42                                          * zly */
  43 
  44         /* mamy liczby, teraz szukamy zamykajacego nawiasu: */
  45         while ((i = fgetc(p)) == ' ')   /* pomijamy spacje */
  46                 ;
  47 
  48         /* mamy znak rozny od spacji - powinien to byc znak ']' */
  49         if (i != ']')
  50                 return -1;      /* nie jest - bledny format pliku */
  51 
  52         /*
  53          * jesli doszlismy juz tu, to wszystko jest ok, mamy w wektorze n
  54          * liczb
  55          */
  56         return n;
  57 }
  58 
  59 /*
  60  * L2: oblicza norme L2 (pierwiastek z sumy kwadratow) wektora double v[] o
  61  * dlugosci n
  62  */
  63 double
  64 L2(double v[], int n)
  65 {
  66         int             i;
  67         int             l2 = 0; /* wazne jest, aby nadac l2 wartosc
  68                                  * poczatkowa */
  69         for (i = 0; i < n; i++)
  70                 l2 += v[i] * v[i];
  71         return sqrt(l2);
  72 }
  73 
  74 /*
  75  * dot_product: oblicza iloczyn skalarny wektorow double v[] i u[] v i u maja
  76  * dlugosc n
  77  */
  78 double
  79 dot_product(double v[], double u[], int n)
  80 {
  81         int             i;
  82         double           dp = 0; /* wazne jest, aby nadac dp wartosc
  83                                  * poczatkowa */
  84         for (i = 0; i < n; i++)
  85                 dp += v[i] * u[i];
  86         return dp;
  87 } 

Pierwsza linia pliku bibl.c poleca kompilatorowi włączyć (wczytać) plik bibl.h:

   1 #ifndef _BIBL_H_                /* zabezpieczenie przed wielokrotnym
   2                                  * wlaczeniem tego pliku */
   3 #define _BIBL_H_
   4 
   5 #include <stdio.h>
   6 
   7 /* Prototypy funkcji */
   8 
   9 /*
  10  * read: czyta z pliku p wektor double v[], nie dluzszy niz max_l_e elementow
  11  * w v musi byc miejsce na co najmniej n elementow zwraca liczbe
  12  * przeczytanych elemento w razie sukcesu, -1 w razie bledu format wektora to
  13  * <liczba_elementow> [ <element> ... ] np. 3 [ 1.2 4.5, 7.8]
  14  */
  15 int             read(FILE * p, double v[], int max_l_e);
  16 
  17 /*
  18  * L2: oblicza norme L2 (pierwiastek z sumy kwadratow) wektora double v[] o
  19  * dlugosci n
  20  */
  21 double           L2(double v[], int n);
  22 
  23 /*
  24  * dot_product: oblicza iloczyn skalarny wektorow double v[] i u[] v i u maja
  25  * dlugosc n
  26  */
  27 double           dot_product(double v[], double u[], int n);
  28 
  29 #endif
  30 

Plik bibl.h zawiera prototypy funkcji zdefiniowanych w bibl.c Włączenie bibl.h do bibl.c pozwala kompilatorowi sprawdzić, czy deklaracje w bibl.h zgadzają się z definicjami funkcji w bibl.c (chodzi o liczbę i typy argumentół oraz o typ zwracany przez funkcję). Dzięki istnieniu pliku bibl.h możemy napisać w osobnym pliku (i osobno skompilować) funkcję main, która będzie wykorzystywać funkcje zdefiniowane w bibl.c. Oto plik main.c:

   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 
   4 #include "bibl.h"               /* dzieki temu kompilator moze sprawdzic, czy
   5                                  * dobrze wywolujemy funkcje z naszej
   6                                  * biblioteki */
   7 
   8 int
   9 main(int argc, char *argv[])
  10 {
  11         double           w_1[10];
  12         double           w_2[10];
  13         int             n1;
  14         int             n2;
  15         int             i;
  16 
  17         FILE           *we = argc > 1 ? fopen(argv[1], "r") : stdin;    /* jesli argc > 1 to
  18                                                                          * program zostal
  19                                                                          * wywolany z argumentem
  20                                                                          * - probujemy otworzyc
  21                                                                          * taki plik do czytania */
  22 
  23         if (we == NULL) {       /* stdin != NULL, a wiec to mozliwe tylko,
  24                                  * gdy argv[1] zawiera cos, co nie jest nazwa
  25                                  * istniejacego pliku */
  26                 fprintf(stderr, "Plik wejsciowy %s nie istnieje!\n", argv[1]);
  27                 exit(1);
  28         }
  29         n1 = read(we, w_1, 10);   /* czytamy z wejscia wektor i pakujemy go
  30                                  * do w_1 */
  31 
  32         if (n1 > 0) {
  33                 printf("Wczytano %i-elementowy wektor:\n( ", n1);
  34                 for (i = 0; i < n1; i++)
  35                         printf("%f ", w_1[i]);
  36                 printf(")\n");
  37         } else {
  38                 fprintf(stderr, "Wystapil blad przy wczytywaniu wektora: kod bledu=%i\n", n1);
  39                 return 1;
  40         }
  41 
  42         printf("Norma L2 wczytanego wektora = %f\n", L2(w_1, n1));
  43 
  44         n2 = read(we, w_2, 10);   /* czytamy z wejscia wektor i pakujemy go
  45                                  * do w_2 */
  46 
  47         if (n2 > 0) {
  48                 printf("Wczytano %i-elementowy wektor:\n( ", n2);
  49                 for (i = 0; i < n2; i++)
  50                         printf("%f ", w_2[i]);
  51                 printf(")\n");
  52         } else {
  53                 fprintf(stderr, "Wystapil blad przy wczytywaniu wektora: kod bledu=%i\n", n2);
  54                 return 1;
  55         }
  56 
  57         printf("Norma L2 wczytanego wektora = %f\n", L2(w_2, n2));
  58 
  59         if (n1 == n2 ) {
  60                 printf("Iloczyn skalarny wczytanych wektorow = %f\n", dot_product(w_1, w_2, n1));
  61         } else {
  62                 printf("Wczytane wektory maja rozna dlugosci i nie mozna ich pomnozyc przez siebie\n" );
  63         }
  64         return 0;
  65 }

Kompilacja programu (kompilujemy kazdy z plików osobno i następnie łączymy wyniki kompilacji w jeden program):

volt:~/lmp/lmp4/gr1> cc -c bibl.c
volt:~/lmp/lmp4/gr1> cc -c main.c
volt:~/lmp/lmp4/gr1> cc main.o bibl.o
volt:~/lmp/lmp4/gr1>

Przygotujemy teraz pierwszy plik z danymi (dane_1)"

2 [ 1.1 1.1 ]
2 [ 3.4 4.4 ]

i uruchomimy program:

volt:~/lmp/lmp4/gr1> ./a.out dane_1
Wczytano 2-elementowy wektor:
( 0.000000 0.000000 )
Norma L2 wczytanego wektora = 0.000000
Wczytano 2-elementowy wektor:
( 0.000000 0.000000 )
Norma L2 wczytanego wektora = 0.000000
Iloczyn skalarny wczytanych wektorow = 0.000000
volt:~/lmp/lmp4/gr1>

Widać, że program nie działa poprawnie - błędne jest już wczytywanie wektorów, bo choć poprawnie rozpoznawana jest ich długość, to już nie wartości elementów. Ten błąd jest wynikiem zastosowania niewłaściwego formatu wczytywania danych w wywołaniu funkcji scanf - do wczytywania liczb double należy używać formatu %lf, a więc linia nr 38 pliku bibl.c powinna wyglądać następująco:

   1 if (fscanf(p, "%lf", &(v[i])) != 1)      /* fscanf powinno zawsze

Różnica jest bardzo drobna, ale efekt dość łatwo zauważalny - po ponownej kompilacji (tym razem kompilujemy tylko bibl.c i wykorzystujemy poprzednio utworzony plik main.o):

volt:~/lmp/lmp4/gr1> cc -c bibl.c
volt:~/lmp/lmp4/gr1> cc main.o bibl.o

i uruchomieniu:

volt:~/lmp/lmp4/gr1> ./a.out dane_1
Wczytano 2-elementowy wektor:
( 1.100000 1.100000 )
Norma L2 wczytanego wektora = 1.414214
Wczytano 2-elementowy wektor:
( 3.400000 4.400000 )
Norma L2 wczytanego wektora = 5.477226
Iloczyn skalarny wczytanych wektorow = 8.580000
volt:~/lmp/lmp4/gr1

wszystko jest już w porządku.

Program na zaliczenie

Do wyboru przez prowadzącego: rozszerzenie mini-biblioteki o kolejne funkcje (normy wektorów, algebra wektorów, inne formaty we/wy).


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