A Numerical Recipes programok használata Visual C környezetben

Ezen az oldalon arról lesz szó, hogy a Numerical Recipes programcsomagból kiválasztott algoritmust hogyan tudjuk a saját programunkba beilleszteni. Kifejezetten a Visual C programnyelvre koncentráltam, mivel ez ma az egyik legelterjedtebb C fordító és a tanszéken is ezt használják többségben.
A programok használatánál három fontos dologról kell említést tenni, amelyek nem egyértelműek:


Változók deklarálása

Az első fontos pont a beillesztésben a változók helyes deklarálása. Apró eltérések vannak ugyanis a vektor és mátrix típusú változók definiálásánál.
Vektorok defíniálására Visual C-ben a legalkalmasabb a new operátor használata. Ilyen módon azonban közvetlenül csak olyan vektorok állíthatók elő, amelyek sorszámozása 0-ról indul. A Numerical Recipes kicsit más, de nagyon jól kigondolt struktúrában használja a vektorokat és a mátrixokat. A számozás az NR programokban használt vektorokban és mátrixokban általában 1-től indul. A vektorok és mátrixok definiálása speciális függvények segítségével történik. Ezek a függvénypárok a következők:

float *vector(long n1,long nh)

void free_vector(float *v, long n1, long nh)
Egy [n1..nh] értéktartományú egyszeres pontosságú valós számokat tartalmazó vektorhoz memória lefoglalása majd felszabadítása.

int *ivector(long n1,long nh)

void free_ivector(int *v, long n1, long nh)
Ugyanaz mint az előbb, csak előjeles egész számokat tartalmazó vektorhoz.

char *cvector(long n1,long nh)

void free_cvector(char *v, long n1, long nh)
Ugyanez előjel nélküli, 1 bájt hosszúságú számokhoz.
long *lvector(long n1,long nh)

void free_lvector(long *v, long n1, long nh)
És végül előjeles 4 bájton ábrázolt számokat tartalmazó vektor megadása.

float **matrix(long nr1, long nrh, long nc1, long nch)

void free_matrix(float **m, long nr1, long nrh, long nc1, long nch)
Egy [nr1..nrh][nc1..nch] értéktartományú egyszeres pontosságú valós számokat tartalmazó mátrixhoz memória lefoglalása majd felszabadítása.

float **dmatrix(long nr1, long nrh, long nc1, long nch)

void free_dmatrix(double **m, long nr1, long nrh, long nc1, long nch)
Ugyanez dupla pontosságú számok esetén.

float **imatrix(long nr1, long nrh, long nc1, long nch)

void free_imatrix(int **m, long nr1, long nrh, long nc1, long nch)
Végül egész számok esetén mátrix lefoglalás. (a Numerical Recipes nem használ char és long mátrixokat)

Az így előállított vektorok ezután ugyanúgy használhatók mint a new utasítással előállított vektorok (csak itt az általunk definiált értéktartományt használjuk). Ha valaki mindenképpen 0-tól akar indexelni egy vektort de a Numerical Recipes függvényben 1-től indul a számozás, akkor sincsen baj, mert az indexelés eltolása könnyen megoldható: csak vonjuk ki a szükséges elemszámot a vektorra mutató pointerből, amikor azt átadjuk egy NR rutinnak. Szélsőséges esetként az is megoldható, ha valaki new utasítással defíniált egy vektort, de át akarja adni egy NR rutinnak. Például:

float *SampleArray = new float[1024]; //ez a tomb 0-tol indul
for(i=0;i<1024;i++) {SampleArray[i]=i;}
four1(SampleArray-1,1024,1); //egydimenzios Fourier-transzformacio, az indexeles 1 float ertekkel eltolva

Fontos: ha deklaráltunk egy vektort és adott számú elemmel el akarjuk tolni az indexelést, akkor ne a memóriaterület bájtban megadott méretével toljunk, hanem a tényleges elemszámmal, ugyanis a memóriában való pontos pozícionálást a C fordító fogja ez alapján kiszámítani.

Hibaüzenetek megjelenítése

Windows könyezetben minden a grafikus képernyőn történik, tehát a hibaüzenetet is oda szokás kiírni, viszont a Numerical Recipes függvények szöveges képernyőt tételeznek fel és a printf függvényt használják. A printf függvény Windows könyezetben való lefutásakor nincs semmi probléma, kivéve, hogy nem jelenik meg a hibaüzenet és esetleg nem értjük, hogy miért áll már órák hosszat a programunk. Ennek a kiküszöböléséhez néhány ponton át kell írni az NRUTIL.C programfájlt, hogy a hibaüzeneteket AfxMessageBox függvényeken keresztül írja ki a képernyőre. Ezt én meg is tettem. A módosított programfájl letölthető erről az oldalról itt:

                   NRUTIL.CPP

Beillesztés és fordítás

A Numerical Recipes programcsomag eredetileg ANSI C-re és Borland C-re, DOS környezethez készült, viszont a következő műveletsor szerint eljárva tökéletesen használhatók és fordíthatók a programok Visual C-ben is.

  1. A Numerical Recipes programból, a "Header and Utility Files" pont alól válasszuk ki és mentsük ki a merevlemezre az NR.H, NRUTIL.H programokat, illetve, ha a Numerical Recipes csomag komplex aritmetikáját is használni akarjuk, akkor a COMPLEX.H és COMPLEX.C programokat is. Szükség van ezenkívül az NRUTIL.C fájlra is (pontosabban NRUTIL.CPP fájlra), ezt erről a lapról töltsük le.
  2. Válasszuk ki és mentsük ki merevlemezre az általunk használni kívánt függvényeket. Nézzünk bele az egyes programokba és nézzük meg azoknak a fejléc után következő néhány sorát, ha ugyanis felhasználnak más rutinokat, akkor a rutin fejléce után szerepel azoknak a fejléce is. Ha találtunk ilyen függvényeket, amiket még nem mentettünk ki, akkor mentsük ki ezeket is.
  3. Nevezzük át az összes kimentett Numerical Recipes C fájlt CPP-re. Ez fontos, egyébként függvény hívási problémák merülhetnek fel később.
  4. Hozzuk létre Visual C-ben a kívánt project-et és készítsük el az osztályokat. Pakoljuk be a munkaterületre a Numerical Recipes programfájlokat.
  5. A Visual C Project/Add to Project/Files… opciója segítségével válasszuk ki az NR fájljainkat és adjuk őket hozzá a project-hez.
  6. Nagyon fontos lépés: a Project/Settings pontnál a C/C++ fül alatt válasszuk ki a "Precompiled Headers" kategóriát és itt állítsuk be a "Not using precompiled headers" vagy az "Automatic use of precompiled headers" lehetőséget. Ha nem tesszük meg, akkor könnyen előfordulhat, hogy egy csomó érthetetlen hibaüzenetet kapunk fordításkor.
  7. Annak a Visual C CPP fájlnak az elejére, amelyikben használni akarjuk az adott függvényt, "include-oljuk be" az NR.H és NRUTIL.H fájlokat. Lehetőleg ne a legelső sorba, hanem a többi #include "xyz.h" sor után írjuk be ezt a két újabb sort. Így biztosan nem lesznek fordítási gondok.
  8. Alkalmazzuk a kiválasztott függvényt a szükséges helyen. Ügyeljünk a vektorok és mátrixok helyes indexelésére. Ezek helyes használatához érdemes lehet megnézni a Numerical Recipes programból a "Numerical Recipes Example Programs" pont alatt az adott függvényre vonatkozó példaprogramot.


A programok használatának jobb megértéséért kidolgoztam két darab Visual C 6.0 példaprogramot, amelyek forrása letölthető itt:

                   DEMO1.ZIP (52770 bájt) Egy példaprogram a Numerical Recipes
                                         FOUR1.C FFT rutinjának a használatához.
                                         A programmal wav fájlok amplitúdóspektruma nézegethető
                   DEMO2.ZIP (45720 bájt) Egy példaprogram a Numerical recipes
                                          SVDCMP.C szinguláris érték dekompozíció rutinjának a használatához.
                                          A programmal szöveges fájlban megadott mátrixok dekomponálhatók.
                                          Az eredmény látható a képernyőn és az SVDresult szöveges fájlban.
 
 
 

Vissza az előző lapra