Ocena programu - obliczenia na macierzach

0

Witam,

mam za zadanie napisac program wykonujacy podstawowe operacje na macierzach (dodawanie, odejmowanie, odwracanie, mnozenie, transponowanie). Ponizej umieszczam to co napisalam. Prosilabym o konstruktywna krytyke (co i dlaczego oraz jak lepiej), bo sa to moje poczatki z programowaniem wiec sporo pewnie jest do naprawy.

aha - na poczatku myslalam o stworzeniu tablic 2wymiarowej - ale mialam problemy z jej dynamicznym przydzialem pamieci i operacjach na takowej tablicy, wiec obeszlam to tworzac tablice 1wymiarowe.

najwieksze watpliwosci mam co do funkcji zaokraglij - to zaokraglanie jakos tak tez na okrak jest zrobione - ale niestety nie udalo mi sie znaleŹĆ funkcji wbudowanej np. takiej: nazwa(miejsc_po_przec, liczba) ktora by zaokraglala do zadanej ilosci miejsc po przecinku - ale zapewne cos takiego istnieje - tylko mi sie nie udalo znaleŹĆ [wstyd]
Sprawa druga ze jezeli uzytkownik poda jako zaokraglenie litere to program sie wywala... grr.. myslalam o tym ze user podawal by zmienna typu char i zamienialabym to na liczbe typu int. Czy da sie to jakos logicznie zorganizowac?

O ile

char lit;
	int licz;
	lit='8';
	licz=(int(lit)-48);

da poprawny wynik to juz przy liczbach wiekszych od 9 nie zamienia poprawnie. Z atoi ten sam problem.

Caly kod

#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <math.h>
using namespace std;

void wykonaj(char ch, int nr, ofstream *f);

/* ******************************************************************************************************************** */
/* PROGRAM GLOWNY - PROGRAM GLOWNY - PROGRAM GLOWNY - PROGRAM GLOWNY - PROGRAM GLOWNY - PROGRAM GLOWNY - PROGRAM GLOWNY */
/* ******************************************************************************************************************** */

int main()
{

	char ch,str[10];
	int nr;

	ofstream fout("out.txt");

	for(nr=1;;nr++) {
		do {
			cout<<" MENU\n\n";
			cout<<"   D .. Dodawanie\n";
			cout<<"   O .. Odejmowanie\n";
			cout<<"   T .. Transponowanie\n";
			cout<<"   M .. Mnozene\n";
            cout<<"   S .. Szukanie macierzy odwrotnej\n";
			cout<<"   K .. Koniec\n";
			cout<<"\t   Twoj wybor: ";
			cin>>str;
			ch = toupper(*str);
			if (ch!='D' && ch!='K' && ch!='O' && ch!='M' && ch!='T' && ch!='S') cout<<"\n (!!!) Nie ma takiej mozliwosci!\n\n";
		} while(ch!='D' && ch!='K' && ch!='O' && ch!='M' && ch!='T' && ch!='S');

		if (ch=='K') { fout.close(); exit(0); } 
		else wykonaj(ch,nr,&fout);

		cout<<"\n================================================\n\n";
	}

	return 0;
}


/* ********************************************************************************************************************* */
/* DEFINICJE FUNKCJI - DEFINICJE FUNKCJI - DEFINICJE FUNKCJI - DEFINICJE FUNKCJI - DEFINICJE FUNKCJI - DEFINICJE FUNKCJI */
/* ********************************************************************************************************************* */

/* ENTER MACIERZ ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */

void enter_wym(int *w, int *k, int nr) /* podawanie wymiaru */
{
	cout<<"\nPodaj wymiar macierzy "<<nr<<"\n";
		
	do{	cout<<"\t-- Wierszy "; cin>>*w; if (*w==0) cout<<"\n (!!!) Nie mozesz podac zerowego wymiaru!\n\n"; }while(*w==0);
	do{	cout<<"\t|| Kolumn ";  cin>>*k; if (*k==0) cout<<"\n (!!!) Nie mozesz podac zerowego wymiaru!\n\n"; }while(*k==0);
}

void enter_el(int w, int k, double *tab, int nr) /* wprowadzanie elem macierzy */
{
	cout<<"\nPodaj elementy macierzy "<<nr<<"\n";
	
	for (int i=0,j=1,g=1;i<w*k;i++) 
	{
		cout<<"\tPodaj elemnt ("<<j<<"/"<<g<<") ";
		cin>>tab[i];
		if (g==k) { g=0; j++; }
		g++;
	}
}


/* WYSWIETLANIE WYNIKU ##################################################################################### */

void wyswietl(int w, int k, double *tab_wynik) /* wyswietlanie wyniku na ekranie */
{
	cout<<"\n-------------\n| Wynik to: |\n-------------\n\n"; 

	for (int i=0,j=1;i<w*k;i++) 
	{
		cout<<"  "<<tab_wynik[i];
		if(i+1==j*k) {cout<<"\n"; j++;}
	}
	cout<<"\n";
}

void zaokraglenie(int w, int k, double *tab_wynik) /* zakraglanie wynikow */
{

  int zaokr,i,j,licz;
  double *tab_temp;
  tab_temp = new double [w*k];
  zaokr=-3;

  for(i=0;i<w*k;i++) tab_temp[i]=tab_wynik[i]; 
  //tabela temp pamieta wyniki bez zaokraglenia, tab3 pamieta wynik z aktualnie wprowadzinym zaokragleniem
  
  do {
	  if (zaokr==-3) cout<<"\nWynik bez zaokraglenia:\n\n";
	  else cout<<"\nCzy ponizsze zaokraglenie Ci odpowiada?\n\n";
	  
	  for(i=0,j=1;i<w*k;i++) {
			  if (zaokr>=0) tab_wynik[i]=(ceil(tab_temp[i]*licz))/licz;
			  cout<<"  "<<tab_wynik[i];
		      if(i+1==j*k) {cout<<"\n"; j++;}
	  }
	  
	  cout<<"\n\tPodaj zaokraglenie {miejsc po przecinku}\n\t-1 jezeli bez zaokraglenia\n\t-2 jezeli zaokraglenie Ci odpowiada\n";
      
	  do {
		  cout<<"\t\tTwoj wybor: ";
		  cin>>zaokr;
		  if (zaokr!=-1 && zaokr!=-2 && zaokr<0 ) cout<<"\t\t\tPodaj -1, -2 lub liczbe >=0\n";
	  }while (zaokr!=-1 && zaokr!=-2 && zaokr<0);

	  if (zaokr>=0) for(i=1,licz=1;i<=zaokr;i++) licz=licz*10;

  }while (zaokr!=-2);
}


/* OPERACJE ================================================================================================ */

void dodaj(int w, int k, double *tab1, double *tab2, double *tab_wynik) /* dodawanie */
{
	for (int i=0;i<w*k;i++) tab_wynik[i]=tab1[i]+tab2[i];
}

void odej(int w, int k, double *tab1, double *tab2, double *tab_wynik) /* odejmowanie */
{
	for (int i=0;i<w*k;i++) tab_wynik[i]=tab1[i]-tab2[i];
}

void tran(int w, int k, double *tab1, double *tab_wynik) /* transponowanie */
{
	for (int j=0,i=0,g=1;j<w*k;j++) 
	{
		tab_wynik[j]=tab1[i];
		i=i+k;
		if (j+1==g*w) { g++; i=g-1; }
	}
}

void mnoz(int k1w2, int k2, int w1, double *tab1, double *tab2, double *tab_wynik) /* mnozenie */
{
	for (int g=0;g<k2;g++) 
		for (int i=0,f=g;i<w1*k1w2;f+=k2) 
		{
			tab_wynik[f]=0;
			for (int j=g,k=0;k<k1w2;j+=k2,i++,k++) tab_wynik[f]=tab_wynik[f]+tab1[i]*tab2[j];
		}
}

double wyznacz(int w, double *tab1) /* sprawdzenie czy mozna policzyc mac odwrotna */
{
    int i,j,g,f,wiel1,wiel2,licznik1,licznik2;
	double suma1,suma2,dz1,dz2;

	if (w==1) return tab1[0];
	else
	{
		i=g=0;
		do {
			suma1=suma2=wiel1=wiel2=licznik1=licznik2=0;
			j=g;f=0;
			dz1=tab1[i]/tab1[i+w];
			dz2=tab1[j]/tab1[j+1];
			do {
				suma1=suma1+tab1[i]; //zerowy wiersz
			    suma2=suma2+tab1[j]; //zerowa kolumna
				if (i+w<w*w) {
					if (dz1==tab1[i]/tab1[i+w]) wiel1++; //proporcjonalnosc wierszy
					if (dz2==tab1[j]/tab1[j+1]) wiel2++; //proporcjonalnosc kolumn
					if (tab1[i]==tab1[i+w]) licznik1++; //rownosc wierszy
					if (tab1[j]==tab1[j+1]) licznik2++; //rownosc kolumn
				};
				j+=w;f++;i++;
			} while (f<w);
			g++;
		} while (i<w*w && licznik1<w && licznik2<w && suma1!=0 && suma2!=0 && wiel1<w && wiel2<w);

		if (licznik1>=w || licznik2>=w || suma1==0 || suma2==0 || wiel1>=w || wiel2>=w) return 0;
		else return 1;
	}
}

void odwr(int W, double *tab1, double *tab_wynik) /* odwracanie */
{
  int i,ii,j,jj,k,kk,gg;

      for(i=0;i<W*W;i++) tab_wynik[i]=tab1[i];

	  // >>>> BEGIN: Zmodyfikowany kod ze strony http://www.ee.pw.edu.pl/~tomzdun/index.php?mid=2253
	  for(i=0,ii=0;i<W;i++,ii+=W+1)
	  {
		  tab_wynik[ii] = 1./tab_wynik[ii];
		  for(j=0,jj=i;j<W;j++,jj+=W)
			  if(j!=i)
			  {
				  tab_wynik[jj] = tab_wynik[jj]*tab_wynik[ii];
				  for (k=0,kk=jj-i,gg=ii-i;k<W;k++,kk++,gg++) {
					  if(k!=i)
					  {
						  tab_wynik[kk] -= tab_wynik[jj]*tab_wynik[gg];
						  if(j==(W-1))
							  tab_wynik[gg] = -(tab_wynik[gg]*tab_wynik[ii]);
					  }
				  }
			  }
	  }
	  if (W>1)
	  for(;kk<gg-1;kk++) {
		  tab_wynik[kk] = -(tab_wynik[kk]*tab_wynik[gg-1]);
	  }
	  // >>>> END
}

/* ZAPISANIE DO PLIKU @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ */


void zap(int w, int k, double *tab_elem, ofstream *f) /* w pliku wynikowym wypisanie macierzy operacyjnych */
{
	for (int i=0,j=1;i<w*k;i++) 
		{
			(*f)<<tab_elem[i];
			if(i+1==j*k && i+1!=w*k) {(*f)<<" / "; j++;}
			else if (i+1!=w*k) (*f)<<", ";
		}
}

void zapisz(int w, int k, int k2, char ch, int nr, ofstream *f, double *tab1, double *tab2, double *tab_wynik) /* zapisanie do pliku wynikow */
{
	int i,j;
	char tn,ciag[10];

    cout<<"\tZapisac wyniki do pliku? (out.txt)\n";
	
	do {
			cout<<"\t\t(T)ak\n";
			cout<<"\t\t(N)ie\n";
			cout<<"\t\t\tTwoj wybor: ";
			cin>>ciag;
			tn = toupper(*ciag);
			if (tn!='T' && tn!='N') cout<<"\nNie ma takiej mozliwosci!\n\n";
		} while(tn!='T' && tn!='N');

	if (tn=='T') {

	(*f)<<"Wynik operacji nr "<<nr;

	switch (ch) {
		case 'M' :
			(*f)<<", mnozenie,\n\tmacierzy ("<<w<<", "<<k<<") = {"; /*oo*/ zap(w,k,&*tab1,&*f); /*oo*/ (*f)<<"}, z\n\tmacierza ("<<k<<", "<<k2<<") = {"; /*oo*/ zap(k,k2,&*tab2,&*f); /*oo*/ (*f)<<"}, to:\n\n";
			k=k2;break;
		case 'T' :
			(*f)<<", transponowanie,\n\tmacierzy ("<<w<<", "<<k<<") = {"; /*oo*/ zap(w,k,&*tab1,&*f); /*oo*/ (*f)<<"}, to:\n\n";
			i=w; w=k; k=i; break;
	    case 'S' :
			(*f)<<", odwracanie,\n\tmacierzy ("<<w<<", "<<w<<") = {"; /*oo*/ zap(w,w,&*tab1,&*f); /*oo*/ (*f)<<"}, to:\n\n";
			break;
		case 'D' :
			(*f)<<", dodawanie,\n\tmacierzy ("<<w<<", "<<k<<") = {"; /*oo*/ zap(w,k,&*tab1,&*f); /*oo*/ (*f)<<"}, do\n\tmacierzy ("<<w<<", "<<k2<<") = {"; /*oo*/ zap(w,k2,&*tab2,&*f); /*oo*/ (*f)<<"}, to:\n\n";
			break;
	    case 'O' :
			(*f)<<", odejmowanie,\n\tod macierzy ("<<w<<", "<<k<<") = {"; /*oo*/ zap(w,k,&*tab1,&*f); /*oo*/ (*f)<<"},\n\tmacierzy ("<<w<<", "<<k2<<") = {";zap(w,k2,&*tab2,&*f); /*oo*/ (*f)<<"}, to:\n\n";
			break;
	}

	if (k2==-1)  (*f)<<"  Tej macierzy nie mozna odwrocic\n";
	else
		for (i=0,j=1;i<w*k;i++) 
		{
			(*f)<<"  "<<tab_wynik[i];
			if(i+1==j*k) {(*f)<<"\n"; j++;}
		}
		(*f)<<"\n\n";
	}
}

/* GLOWNE WYWOLANIE ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

void wykonaj(char ch, int nr, ofstream *f) /* funkcja glowna */
{ 
	int w1,k1,w2,k2;
    double *tab1,*tab2,*tab3;

	switch (ch) 
	{

/* wybrano dodawanie */ 
		case 'D' :

			do {
				enter_wym(&w1,&k1,1);
				enter_wym(&w2,&k2,2);
				if (k1!=k2 || w1!=w2) cout<<"\n (!!!) Macierzy o takich wymiarach nie da sie dodac. Wymiary musza byc rowne.\n";
			}while(k1!=k2 || w1!=w2);

			tab1 = new double [w1*k1];
			tab2 = new double [w1*k1];
			tab3 = new double [w1*k1];

	        enter_el(w1,k1,&*tab1,1);
	        enter_el(w1,k1,&*tab2,2);

			dodaj(w1,k1,&*tab1,&*tab2,&*tab3);
			zaokraglenie(w1,k1,&*tab3);

			wyswietl(w1,k1,&*tab3);

			zapisz(w1,k1,k1,ch,nr,&*f,&*tab1,&*tab2,&*tab3);

			free(tab1);free(tab2);free(tab3);
			break;

/* wybrano odejmowanie */ 
		case 'O' :

			do {
				enter_wym(&w1,&k1,1);
				enter_wym(&w2,&k2,2);
				if (k1!=k2 || w1!=w2) cout<<"\n (!!!) Macierzy o takich wymiarach nie da sie odjac. Wymiary musza byc rowne.\n";
			}while(k1!=k2 || w1!=w2);

			tab1 = new double [w1*k1];
			tab2 = new double [w1*k1];
			tab3 = new double [w1*k1];

	        enter_el(w1,k1,&*tab1,1);
	        enter_el(w1,k1,&*tab2,2);

			odej(w1,k1,&*tab1,&*tab2,&*tab3);
			zaokraglenie(w1,k1,&*tab3);

			wyswietl(w1,k1,&*tab3);

			zapisz(w1,k1,k2,ch,nr,&*f,&*tab1,&*tab2,&*tab3);

			free(tab1);free(tab2);free(tab3);
			break;

/* wybrano transponowanie */ 
		case 'T' :

			enter_wym(&w1,&k1,1);

			tab1 = new double [w1*k1];
			tab2 = new double [w1*k1];

	        enter_el(w1,k1,&*tab1,1);

            tran(w1,k1,&*tab1,&*tab2);
			zaokraglenie(k1,w1,&*tab2);

			wyswietl(k1,w1,&*tab2);

			zapisz(w1,k1,0,ch,nr,&*f,&*tab1,0,&*tab2);

			free(tab1);free(tab2);
			break;

/* wybrano mnozenie */ 
		case 'M' :

			do {
				enter_wym(&w1,&k1,1);
				enter_wym(&w2,&k2,2);
				if (k1!=w2) cout<<"\n (!!!) BLAD! Zly wymiar. Prawidlowy wymiar to w m1 = k m2\n";
			}while(k1!=w2);

			tab1 = new double [w1*k1];
			tab2 = new double [w2*k2];
			tab3 = new double [w1*k2];

			nr=1;

	        enter_el(w1,k1,&*tab1,1);
	        enter_el(w2,k2,&*tab2,2);

			mnoz(k1,k2,w1,&*tab1,&*tab2,&*tab3);
			zaokraglenie(w1,k2,&*tab3);

			wyswietl(w1,k2,&*tab3);

			zapisz(w1,k1,k2,ch,nr,&*f,&*tab1,&*tab2,&*tab3);

			free(tab1);free(tab2);free(tab3);
			break;

/* wybrano odwracanie */ 
		case 'S' :

			do { 
				enter_wym(&w1,&k1,1);
				if (w1!=k1) cout<<"\n (!!!) Odwracac mozna tylko macierz kwadratowa\n";
			}while(w1!=k1);

			tab1 = new double [w1*k1];
			tab2 = new double [w1*k1];

	        enter_el(w1,k1,&*tab1,1);

			if(wyznacz(w1,&*tab1)!=0) {
				odwr(w1,&*tab1,&*tab2);
				zaokraglenie(w1,w1,&*tab2);
				wyswietl(w1,w1,&*tab2);
				zapisz(w1,k1,0,ch,nr,&*f,&*tab1,0,&*tab2);
			}
			else {
				cout<<"\n-------------\n| Wynik to: |\n-------------\n\n  Tej macierzy nie da sie odwrocic\n\n";
				zapisz(w1,k1,-1,ch,nr,&*f,&*tab1,0,&*tab2);

			}

			free(tab1);free(tab2);
			break;
	}
}
0

Nie musisz sprawdzać czy macierz jest odwracalna. Po prostu spróbuj ją odwrócić, a jeśli trafisz na dzielenie przez zero to znaczy, że nie da się odwrócić. Pamiętaj o błędach zaokrągleń, tzn aby stwierdzić, czy jakaś liczba jest równa zero, to nie przyrównuj ją bezpośrednio do zera, tylko weź mały margines np abs(liczba) < 0.000001

Poza tym algorytm do odwracania macierzy do którego link podałeś jest jakiś lipny.
Jakby wziąć odwracalną macierz ((<font color="#008000">0</span>,1), (1,1)) algorytm ten podczas pierwszej iteracji robi:
Tab[i][i] = 1./Tab[i][i];
Kiedy i=0, a Tab[0][0]=<font color="#008000">0</span>, co nam daje dzielenie przez 0

A zaokrągleniami martw się tylko przy wypisywaniu:
cout.precision(5);
cout << 0.123456789;

0

dla macierzy o 1x1 o elemencie 0 nie da sie wyliczyc macierzy odwrotnej bo jej wskaznik jest rowny 0, macierz 1x1 o elemencie x ma macierz odwrotna 1/x.

z kolei macierz np.
0 1
2 3
ma macierz odwrotna
-1,5 0,5
1 0
mimo ze ma zerowy element, ale wyznacznik jest rozny od 0.

za to macierz
1 1
2 2
macierzy odwrotnej nie ma bo ma zerowy wyznacznik, mimo ze nie ma zerowego elementu

Sam algorytm liczenia macierzy odwrotnej jest ok.

0

No właśnie jest lipny, bo np. dla macierzy 2x2 z zerowym pierwszym elementem, wykonuje dzielenie przez zero.

0

no tez racja.. ale czy w takim razie znacie jakis inny sensowny algorytm na odwracanie.. ja osobiscie probowalam odwracac metoda gaussa-jordana (za pomoca wyznacznikow odpada), ale pomijajac ze nie dzialalo do konca dobrze - bylo na pewno bardziej zlozone i skomplikowane. Znalazlam (zreszta na tej stronie) taki algorytm w pascalu, ale wydaje mi sie bardziej zlozony.

0

Dlaczego metoda wyznaczników odpada ? Będzie bardzo prosta. A że mało wydajna to chyba u ciebie nie ma znaczenia ? Poza tym pokaż tego gaussa jordana, może sie coś poradzi.

0

Hmm... nie dalej jak we wtorek na laboratoriach robilismy programik do obliczania rownan liniowych. Na podstawie tego programu da się też wyznaczyć macierz odwrotną:
AX=B
gdzie A to macierz podstawowa
X to k-ta kolumna macierzy odwrotnej, czyli nasze niewiadome.
B to k-ta kolumna macierzy jednostkowej (element k = 1, reszta to zera).
Trzeba rozwiązać k takich równań, poskładać do kupy i ma sie macierz odwrotną.
Problem jest tego typu, ze trzeba zaprogramować rozwiazywanie równań liniowych (my robiliśmy metodą eliminacji Gaussa, metoda dokładna) i potem te k równań policzyć, ale jest to w sumie do zrobienia w parę godzin swobodnej pracy.

0
pako1337 napisał(a)

Hmm... nie dalej jak we wtorek na laboratoriach robilismy programik do obliczania rownan liniowych.

podeslij ten kod. dzx.

co do metody wyznacznikow to zle napisalam - chodzilo mi o metode dopelnienien algebraiczenych (mam w poleceniu ze nie a po drugie - za duzo liczenie przy duzych macierzach).

co do tej metody gaussa jordana to juz wykasowalam to co mialam bo i tak nie dzialalo.

0

Poszło na maila. Kod nie jest idealny, ale działa i sprawnie liczy ;)

0
adf88 napisał(a)

A zaokrągleniami martw się tylko przy wypisywaniu:
cout.precision(5);
cout << 0.123456789;

no tak tyle ze:

cout.precision(5);
cout<<1.234567890<<"\n";
cout<<12345.67890<<"\n";

daje wynik
1.23456
123456

zamiast oczekiwanych 5 miejsc po przecinku (kropce), jak ponizej
1.23456
12345.67890

0

A dałaś wybór elementu głównego w macierzy ?

0

no coz - jezeli chodzi o macierze to mialam jedynie podstawy - teraz dopiero majac ekonometrie dowiaduje sie kilku nowych rzeczy o macierzach - ale czegos takiego jak element glowny nie mialam :/

0

http://wazniak.mimuw.edu.pl/index.php?title=MN05
Dobrze opisane o co chodzi. Gdy zrobisz wybór elementu głównego odpada problem z dzieleniem przez 0 oraz poprawia się dokładność wyników.

0

no niezle to to :) rzeczywiscie na tej podstawie mozna wyznaczyc macierz odwrotna - sproboje to wykorzystac, mysle ze napisac program na podstawie tego co jest tam na tej stronce nie jest trudne. Trudniejsze natomiast bedzie wytlumaczenie tego dziewczyna ktore ten projekt teoretycznie robia ze mna - praktycznie maja problemy z podstawowymi programami w c, wiec projekt robie ja.... w zamian za notatki z roznych wykladow.

a tak odbiegajac juz od samego wyznaczania macierzy odwrotnej - jakie zastrzezenia co do samej konstrukcji programu?

cout.precision nie dziala tak jak bym tego chciala (patrz ostatni post na 1 stronie tematu)

0

atwiej zrobic przez printf() , przez cout mozesz uzycz fixed setprecision() np. : :

cout << "Zmienna" << fixed << setprecision(3) << jakaszmienna
0

dzieki. Dziala, ale troche w innej postaci:

cout.precision(3);
cout << "Zmienna " << fixed << jakaszmienna;

1 użytkowników online, w tym zalogowanych: 0, gości: 1