Transponowanie macierzy

Transponowanie macierzy
D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

Cześć, piszę sobie klasę do obsługi działań na macierzach:

Kopiuj
class Macierz
{
public:
	Macierz(int a, int b);
	Macierz(int a, int b, int c);
	~Macierz();
	void wypisz(void) const;
	void transponuj(void);

private:
	int **tab;
	int x;
	int y;
}; 

I chcę napisać metodę 'transponuj' która zrobi transponowanie macierzy (wiersz to kolumna, a kolumn to wiersz).

Kopiuj
void Macierz::transponuj(void)
{
	int **tab_trans = new int *[y];
	for (int k = 0; k < y; ++k)
	{
		tab[k] = new int[x];
	}
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
			tab_trans[i][j] = tab[j][i]; //tu wywala!
	}
	tab = tab_trans;
	swap(x, y);
	//tu jeszcze zwolnienie pamięci dla tab_trans
} 

W komentarzu zaznaczyłem miejsce w którym program sie wysypuje, mógłby ktoś spojrzeć i dać wskazówkę co jest nie tak?

spartanPAGE
Piszesz w C++. Nie potrzebujesz voida w nawiasach, gdy nie pobierasz argumentów.
D1
Ok, zapamiętam, mimo to nie rozwiązuje to mojego problemu.
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:3 miesiące
1
Kopiuj
    int **tab_trans = new int *[x];
    for (int k = 0; k < x; ++k)
    {
        tab[k] = new int[y];
    }

Oraz zapomniałeś zwolnić tab wraz z wierszami.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 1x, ostatnio: _13th_Dragon
D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

No tak, ale wydaje mi się że problem nie leży w samym utworzeniu tab_trans (tablica ta ma mieć rozmiar odwrotny do tab), tylko w przypisaniu do tab_trans, odpowiedniej wartości tab.

msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Tak jak pisze @_13th_Dragon masz memleak, bo nie zwalniasz tablicy (http://en.wikipedia.org/wiki/Memory_leak).
To nie bezpośrednio powód tego że Ci nie działa, to kolejny błąd. Powinieneś zwalniać tab zanim zmienisz jego wartość.

Jak o zwalnianiu mowa:

Kopiuj
//tu jeszcze zwolnienie pamięci dla tab_trans

Jeśli fatycznie zwalniasz pamięć dla tab_trans, to zwalniasz całą tablicę (w tym momencie tab_trans pokazuje na to samo na co tab) - nic dziwnego że nie działa.

Jeszcze możesz się przyjrzeć czy x i y są poprawnie inicjalizowane.

edytowany 2x, ostatnio: msm
D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

Mój pomysł przedstawiał się następująco:

  • mam tablicę którą chcę transponować, powiedzmy tab[x][y]
  • rezerwuję sobie miejsce na nową tablicę, tylko z odwróconymi rozmiarami tab_trans [y][x]
  • przepisuje wartości tab do tab_trans
  • zwalniam pamięć dla starej tab
  • niech tab pokazuje na to samo co tab_trans (tab = tab_trans)
  • zwalniam pamięć dla tab_trans

Nie ma tutaj chyba mowy o wyciekach pamięci?

msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
1
  • zwalniam pamięć dla starej tab

W kodzie który podałeś tego nie ma, dlatego zauważamy. Tak, z tym nie byłoby wycieku pamięci.

  • zwalniam pamięć dla tab_trans

Ale w ten sposób zwalniasz pamięć dla tego co jest aktualnie.

Prościej będzie opisać strzałkami. Przy wskaźnikach zawsze sprawdzają się strzałki ;)

początkowy stan:

Kopiuj
tab -----------> początkowa tablica

alokowanie pamięci:

Kopiuj
tab -----------> początkowa tablica
tab_trans  ----> nowa tablica

po pętli for robiąca transponowanie:

Kopiuj
tab -----------> początkowa tablica
tab_trans  ----> tablica po transponowaniu (wynik)

po zwolnieniu pamięci dla tab:

Kopiuj
tab -----------> [zwolniona pamięć, śmieci]
tab_trans  ----> tablica po transponowaniu (wynik)

tab = tab_trans:

Kopiuj
tab ---------\
              -----> tablica po transponowaniu (wynik)
tab_trans  --/

I to wszystko czego potrzeba - tab wskazuje już na wynik końcowy, a tab_trans (tzn. sam wskaźnik, cztery/osiem bajtów) zniknie za chwilę bo jest zmienną lokalną.

Ale jeśli teraz zwolnisz tab_trans (czyli pamięć na którą wskazuje), to skończysz z (katastrofa):

Kopiuj
tab ---------\
              -----> [zwolniona pamięć, śmieci]
tab_trans  --/
edytowany 2x, ostatnio: msm
D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

Okej, jeżeli chodzi o alokację pamięci wszystko już rozumiem, dzięki.

Jednak problemem jest teraz przepisanie wartości ze starej tablicy do nowej, gdyż np. linijka tab_trans[i][j] = tab[j][i] nie działa, czyli trzeba tu wymyślić chyba jakiś sprytniejszy sposób, niż ten znany ze statycznych tablic.

msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Mógłbyś dać cały kod macierzy? Bo w tym co podałeś nie ma (albo nie widzę na pierwszy rzut oka) błędu, może coś wcześniej idzie źle.

D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0
Kopiuj
 class Macierz
{
public:
	Macierz(int a, int b);
	Macierz(int a, int b, int c);
	~Macierz();
	void wypisz() const;
	void transponuj();
	//void wyznacznik();

private:
	int **tab;
	int x;
	int y;
};
Kopiuj
#include "Macierz.h"
#include <iostream>

using namespace std;

Macierz::Macierz(int a, int b)
{
	x = a;
	y = b;
	tab = new int *[a];
	for (int i = 0; i < a; ++i)
	{
		tab[i] = new int[b];
	}
}

Macierz::Macierz(int a, int b, int c)
{
	x = a;
	y = b;
	tab = new int *[a];
	for (int i = 0; i < a; ++i)
	{
		tab[i] = new int[b];
	}
	//wypełnienie tablicy argumentem c
	for (int i = 0; i < a; i++)
	{
		for (int j = 0; j < b; j++)
			tab[i][j] = c;
	}
}


Macierz::~Macierz()
{
	for (int i = 0; i < x; ++i)
	{
		delete tab[i];
	}
	delete tab;
}

void Macierz::wypisz(void) const
{
	for (int i = 0; i < x; i++)
	{
		for (int j = 0; j < y; j++)
		{
			cout << tab[i][j] << " ";
		}
		cout << endl;
	}
}

void Macierz::transponuj()
{
	int **tab_trans = new int *[y];
	for (int k = 0; k < x; ++k)
	{
		tab[k] = new int[x];
	}
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			tab_trans[i][j] = tab[j][i]; //tu wywala!
		}
	}
	tab = tab_trans;
	swap(x, y);
} 

Jeszcze nie wcieliłem w życie (w kod), w metodzie transponuj zwalniania pamięci, o którym rozmawialiśmy, ale to chyba nie jest powód tego że program się wysypuje?

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0
Kopiuj
    int **tab_trans = new int *[y];
    for (int k = 0; k < x; ++k)

Hint: warunek pętli.

PS nazywaj sensowniej parametry;
PPS //wypełnienie tablicy argumentem c - c jest parametrem, nie argumentem;
PPPS raczej rozmiar macierzy nie może być ujemny, więc int jest nie na miejscu. unsigned int/size_t/(...) byłoby lepszym wyborem.


edytowany 3x, ostatnio: Patryk27
D1
PS sensowniej, tzn?
Patryk27
Widzisz nagłówek metody: Macierz::Macierz(int a, int b, int c). Jak bez patrzenia w jej ciało masz się domyślić, czym są a, b oraz c?
D1
Czyli na ten przykład, zamiast a, będzie wiersz, zamiast b, kolumna itp. Poza tym, lepiej chyba pisać programy po angielsku?
Patryk27
zamiast a, będzie wiersz, zamiast b, kolumna ale to wiesz, ponieważ zajrzałeś do ciała metody. Nie uważasz, że sensowniej byłoby: Macierz::Macierz(unsigned int width, unsigned int height, int defaultValue)?
D1
Tak, masz rację, lepiej nauczyć się zasad pisania, dobrego czytelnego kodu, dzięki za wskazówki!
D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

Tak tak, powinno być:

Kopiuj
 
int **tab_trans = new int *[y];
	for (int k = 0; k < y; ++k)

Ale mimo to dalej wysypuje się w linijce przepisanie wartości: "Instrukcja spod <adres> odwołuje się do pamięci pod adresem <adres>. Pamięć nie może być written".

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
1

Zauważ jeszcze, do czego przypisujesz:
tab[k] = new int[x];


edytowany 1x, ostatnio: Patryk27
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
1

Faktycznie, nie zauważyłem tego (i nikt inny wcześniej) (chciaż problem jest trochę niżej):

Kopiuj
int **tab_trans = new int *[y];
for (int k = 0; k < y; ++k)
{
    tab[k] = new int[x]; // chcesz stworzyć tablicę 2d z /tab_trans/. W ten sposób tylko "zapominasz" wszystkie dane z tab
    // czyli tab_trans[k] = new int[x];
}

for (int i = 0; i < y; i++)
{
    for (int j = 0; j < x; j++)
        tab_trans[i][j] = tab[j][i]; //tu wywala - bo tab_trans[i] nigdy nie miało przypisanej wartości (i są tam jakieś śmieci).
}

?

D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

Bingo, głupia literówka, zamiast tab_trans miałem w kodzie tab, a tyle kombinowania. Mimo wszystko, dobrze, że zwróciłem się z pomocą, gdyż zwróciliście mi uwagę na te wycieki pamięci. Serdeczne dzięki!

D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

Nie ma sensu zakładać nowego tematu, a jedna rzecz mnie jeszcze interesuje przy okazji rozwijania mojej klasy.
Otóż chcę zrobić funkcjonalność dodawania macierzy, za pomocą przeciążenia operatora +, np:

Kopiuj
macierz A, B, C;
C = A + B 

Dodawać macierze można tylko gdy mają ten sam rozmiar. Ciekawi mnie, czy w przypadku gdy macierze będą miały różne wymiary, to zamiast w metodzie dać ifa sprawdzającego czy mają taki sam rozmiar i wypisującego np. tylko że nie ma możliwości dodania tych macierzy, można zrobić taki mechanizm, że program się w ogóle nie skompiluje, kompilator wyrzuci błąd, iż nie można dodawać macierzy o różnych wymiarach. Analogicznie kompilator protestuje, gdy będziemy chcieli dodać do siebie intigera i np. double'a. Da się coś takiego zrobić w C++?

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:3 miesiące
0

Potrzebujesz następujących rzeczy:

Kopiuj
Macierz operator+(const Macierz &m)const { ... }
Macierz &operator=(const Macierz &m) { ... }
Macierz(const Macierz &m) { ... }

oraz jeżeli to C++11 zastanów się nad:

Kopiuj
Macierz &operator=(Macierz &&m) { ... }
Macierz(Macierz &&m) { ... }

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Ciekawi mnie, czy w przypadku gdy macierze będą miały różne wymiary, to zamiast w metodzie dać ifa sprawdzającego czy mają taki sam rozmiar i wypisującego np. tylko że nie ma możliwości dodania tych macierzy, można zrobić taki mechanizm, że program się w ogóle nie skompiluje, kompilator wyrzuci błąd, iż nie można dodawać macierzy o różnych wymiarach

Można, tylko musiałbyś poczytać trochę o szablonach w C++ (http://en.wikipedia.org/wiki/Template_%28C%2B%2B%29)
Wtedy wyglądałoby to tak że można dodać Macierz<A, B> tylko do Macierz<A, B> (gdzie A, B to parametry określające wysokośc i szerokość). Ogólnie ciekawa rzecz do napisania, polecam.

edytowany 1x, ostatnio: msm
D1
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 5 lat
  • Lokalizacja:Sosnowiec
  • Postów:21
0

A jakieś informację gdzie można o tym znaleźć? Ew. jak fachowo nazywa się taki zabieg?

msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Szablony, po prostu szablony.

Pierwszy (jeden z pierwszych) kurs z brzegu - http://www.tutorialspoint.com/cplusplus/cpp_templates.htm. To dość podstawy, ale wystarczyłoby.

Ale jeśli chcesz zepsuć sobie zabawę, sample (bardzo niekompletny ofc, i pamięć dla tab nie jest nigdy zwalniana ;) ):

Kopiuj
template <int X, int Y>
struct Matrix {
    int **tab;

    Matrix<X, Y>() {
        tab = new int*[X];
        for (int i = 0; i < X; i++) {
            tab[i] = new int[Y];
        }
    }

    Matrix<X, Y> operator +=(Matrix<X, Y> other) {
        // add
    }
};

int main() {
    Matrix<2, 3> m;
    Matrix<2, 3> n;
    Matrix<5, 5> o;

    m += n;
    n += m;
    // n += o; - kompilator głośno krzyczy
}

Przy czym ważne jest zrozumieć że w tym przypadku jest problem z wczytywaniem macierzy o nieznanych rozmiarach (bo kompilator musi znać rozmiar każdej macierzy na etapie kompilacji żeby móc ja zweryfikować).

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:3 miesiące
0

@msm, przekombinowałeś, jeżeli już masz rozmiary ustawione w parametrach to:

Kopiuj
template<size_t X,size_t Y> struct Matrix
  {
   double tab[Y][X];
   Matrix() { memset(&tab[0][0],0,sizeof(tab)); }
   Matrix &operator+=(const Matrix &other)
     {
       // add
     }
  };

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 1x, ostatnio: _13th_Dragon
Zobacz pozostałe 8 komentarzy
Endrju
Nic nie poradzę na to, że tak zostało to zdefiniowane w standardzie. Nie można wyjść wskaźnikiem poza tablicę z której on pochodzi i koniec kropka. This is the law. (5.7.5)
_13th_Dragon
Problem w tym że nie rozumiesz że to ta sama tablica.
Endrju
No niestety to nie jest ta sama tablica. Musisz zrozumieć jak są zdefiniowane takie tablice w C/C++. Takie coś: T t[N][M] to są dwie różne tablice: jedna to tablica N-elementów typu T[M] a druga to tablica M-elementów typu T. Dokładnie jest to opisane w 8.3.4.
_13th_Dragon
Rozumiem - chrzanimy logikę.
Ranides
nie mam pojęcia, który ma rację, więc może by tak... Matrix() : tab{{0,},} { }

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.