Archivi
Categorie
Più cliccati
- Nessuno
Just another WordPress.com weblog
Se state usando il Fortran95 i motivi sono semplici, o vi stanno obbligando oppure volete usare un linguaggio di programmazione che non ha eguali nelle applicazioni del calcolo numerico ed in particolar modo nei calcoli dove si fa uso pesante di matrici.
Questo breve articolo ha due scopi:
Supponiamo di avere due matrici di grande dimensione la [A] e la [B], il calcolo che si vuole eseguire è c(i,j) = a(i,j) + b(i,j). Voglio trovare la matrice somma [C]=[A]+[B].
Operazione estremamente semplice, supponiamo ancora che decidiamo di usare il linguaggio C e che i dati siano interi, vediamo cosa accade usando il seguente codice:
#include<stdio.h> #include<time.h> #define X 10000 #define Y 10000 int main(void){ int i, j; float t0, t1; //int a[X][Y], b[X][Y], c[X][Y]; int **a, **b, **c; /* allocazione della memoria */ a = (int **)calloc(X,sizeof(int *)); b = (int **)calloc(X,sizeof(int *)); c = (int **)calloc(X,sizeof(int *)); for(i=0;i<X;i++){ a[i] = (int *)calloc(Y,sizeof(int)); b[i] = (int *)calloc(Y,sizeof(int)); c[i] = (int *)calloc(Y,sizeof(int)); } /* inizializzazione delle matrici */ for(i=0;i<X;i++){ for(j=0;j<Y;j++){ a[i][j] = 1; b[i][j] = 2; } } /* somma scorrendo per le colonne */ t0 = clock(); for(i=0;i<X;i++){ for(j=0;j<Y;j++){ c[i][j] = a[i][j] + b[i][j]; } } t1 = clock(); printf("%d\n",c[X-1][Y-1]); printf("tempo richiesto somme su colonne = %f\n",(double)(t1 - t0) / CLOCKS_PER_SEC); /* somma scorrendo le righe */ t0 = clock(); for(j=0;j<Y;j++){ for(i=0;i<X;i++){ c[i][j] = a[i][j] + b[i][j]; } } t1 = clock(); printf("%d\n",c[X-1][Y-1]); printf("tempo richiesto somme su colonne = %f\n",(double)(t1 - t0) / CLOCKS_PER_SEC); return 0; }
Il cui risultato sul mio povero PC è rappresentato nella seguente immagine:
Ok ma perchè ho messo un codice in C ed un test del risultato?
Ovviamente per mostrare come in Fortran95 sia comodo descrivere lo stesso algoritmo e per vedere quali sono i risultati in termini di tempo di accesso, quindi qui il codice:
program prova implicit none integer :: i,j integer, parameter :: x=10000, y=10000 real :: t0,t1 integer, dimension(x,y) :: a,b,c a=1 ; b = 2 !SOMMA SCORRENDO LE COLONNE call cpu_time(t0) do i=1,x do j=1,y c(i,j) = a(i,j) + b(i,j) enddo enddo call cpu_time(t1) print*, c(x,y) print*, "tempo richiesto somme su colonne = ",t1-t0 print*,"" !SOMMA SCORRENDO LE RIGHE do j=1,y do i=1,x c(i,j) = a(i,j) + b(i,j) enddo enddo call cpu_time(t1) print*, c(x,y) print*, "tempo richiesto somme su righe = ", t1-t0 print*,"" !SOMMA IN MODO NATIVO DEL FORTRAN call cpu_time(t0) c=a+b call cpu_time(t1) print*, c(x,y) print*, "tempo richiesto somme tra matrici = ", t1-t0 end program prova
Come vedete la principale differenza tra i due linguaggi è che in Fortran le MATRICI ed i VETTORI sono tipi nativi, quindi è possibile fare delle operazioni direttamente su di esse.
Occorre tenere presente che ho usato i seguenti compilatori gcc e gfortran, quindi il backend è lo stesso, il perchè della differenza di risultato lo lascio agli esperti, io non so spiegarlo.
E’ interessante notare come una implementazione non oculata degli accessi agli array in C può essere disastrosa.
Tutti i modi usati in Fortran sono migliori, in particolare l’uso delle operazioni a livello “nativo” sulle matrici permette di ridurre notevolmente le righe di codice oltre al non trascurabile fatto avere tempi di calcolo migliori che in C.
Una interessante possibilità dei compilatori fortran è quella di parallelizzare il codice, cosa che purtroppo non riesco a testare :(.
Buon Coding in Fortran.
E’ indubbio che Excel come tutti i fogli di calcolo sia ormai uno strumento indispensabile nell’analisi dei dati, e sicuramente tutti vi siete trovati ad affrontare il triste problema della “virgola”.
Infatti nelle impostazioni Italiane (ma forse anche straniere), i formati decimali sono separati da “,” e non da “.” .
Questo può creare dei problemi quando si devono importare file numerici realizzati da altri software o scaricati da data-logger, proprio come era successo a me anni fa.
Il problema di per se è facilmente risolvibile quando il file da importare contiene solo numeri decimali con separazione basata su “.”, in questi casi si apre il file con un qualsiasi editor e si esegue la sostituzione automatica di “.” con “,” in tal modo il gioco è fatto e si importa direttamente in Excel un file leggibile secondo il formato che esso si attende.
Tuttavia esistono casi in cui l’operazione precedente non è possibile, in particolare quando il file da importare è mostruosamente grande, ed in tal caso occore un editor di testi serio, o quando il file contiene dati promiscui, come ad esempio il file che dovevo importare io:
000000 25/10/2004 17.07.35 21.487 1.612 26.90 27.17 000001 25/10/2004 17.17.38 21.223 1.596 26.05 27.04 000002 25/10/2004 17.27.35 20.820 1.574 25.08 25.89 000003 25/10/2004 17.37.35 19.464 1.515 23.74 24.90 000004 25/10/2004 17.47.35 16.722 1.426 22.43 23.74 000005 25/10/2004 17.57.35 11.868 1.233 21.33 22.81 000006 25/10/2004 18.07.35 6.576 0.809 20.38 21.99 000007 25/10/2004 18.17.35 2.836 0.382 19.77 21.67 000008 25/10/2004 18.27.35 0.746 0.110 19.30 21.56 000009 25/10/2004 18.37.36 0.090 0.013 19.26 21.62 000010 25/10/2004 18.47.36 0.010 0.001 18.95 21.56 000011 25/10/2004 18.57.36 0.003 0.001 18.92 21.63 000012 25/10/2004 19.07.36 0.003 0.001 19.25 21.62 000013 25/10/2004 19.17.36 0.002 0.001 19.11 21.45
Nel mio caso oltre ad essere un file piuttosto grosso, (mesi di acquisizione dati), mi serviva modificare i separatori decimali solo dalla quarta colonna in poi, per cui il metodo pocanzi menzionato non era applicabile.
Per risolvere il problema sono bastate poche righe di codice C, dove imponevo di mutare i “.” in “,” dal secondo punto letto in poi per ogni riga.
Quindi ho prodotto il seguente codice, dove con DOT_LIMIT pari a 2 richiedo la modifica a partire dal terzo “.” letto, e DOT_UP_LIMIT pari a 6 per azzerare il contatore a fine linea.
Riposto il codice, per quanto banale spero possa essere utile a qualcuno.
#define DOT_LOW_LIMIT 2 #define DOT_UP_LIMIT 6 #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *fpin; FILE *fpout; int dot_count; char carattere; if (argc !=3 ) { fprintf(stderr,"Uso: %s <file_in> <file_out>\n",argv[0]); exit(0); } fpin = fopen(argv[1],"r"); fpout = fopen(argv[2],"w"); dot_count = 0; while(fscanf(fpin,"%c",&carattere) != EOF ){ if (carattere == EOF) break; if (carattere == '.'){ dot_count++; if(dot_count > DOT_LOW_LIMIT){ fprintf(fpout,"%c",','); fprintf(stdout,"%c",','); } else{ fprintf(fpout,"%c",carattere); fprintf(stdout,"%c",carattere); } if(dot_count == DOT_UP_LIMIT) dot_count = 0; } else{ fprintf(fpout,"%c",carattere); fprintf(stdout,"%c",carattere); } } fclose(fpin); fclose(fpout); return 0; }
Ottenendo questo risultato:
000000 25/10/2004 17.07.35 21,487 1,612 26,90 27,17 000001 25/10/2004 17.17.38 21,223 1,596 26,05 27,04 000002 25/10/2004 17.27.35 20,820 1,574 25,08 25,89 000003 25/10/2004 17.37.35 19,464 1,515 23,74 24,90 000004 25/10/2004 17.47.35 16,722 1,426 22,43 23,74 000005 25/10/2004 17.57.35 11,868 1,233 21,33 22,81 000006 25/10/2004 18.07.35 6,576 0,809 20,38 21,99 000007 25/10/2004 18.17.35 2,836 0,382 19,77 21,67 000008 25/10/2004 18.27.35 0,746 0,110 19,30 21,56 000009 25/10/2004 18.37.36 0,090 0,013 19,26 21,62 000010 25/10/2004 18.47.36 0,010 0,001 18,95 21,56 000011 25/10/2004 18.57.36 0,003 0,001 18,92 21,63 000012 25/10/2004 19.07.36 0,003 0,001 19,25 21,62 000013 25/10/2004 19.17.36 0,002 0,001 19,11 21,45