Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Przesuwanie wskaźników w tablicach dwuwymiarowych

Ostatnio zmodyfikowano 2016-09-08 00:41
Autor Wiadomość
Merulo
Temat założony przez niniejszego użytkownika
Przesuwanie wskaźników w tablicach dwuwymiarowych
» 2016-09-05 17:28:49
Witam!

Posiadam dość skomplikowany kod, tutaj jest uproszczona wersja:

C/C++
int tab[ 7 ][ 7 ];
int * p[ 7 ][ 7 ];

int k = 10;

for( int i = 0; i < 7; i++ ) {
    for( int j = 0; j < 7; j++ ) {
        //wypełnienie tablicy
        tab[ i ][ j ] = k;
        k++;
        //wskaźnik na 0,0 dla danych na 0,0
        p[ i ][ j ] = & tab[ i ][ j ];
        //wypisanie
        cout <<* p[ i ][ j ] << " ";
    }
    cout << endl;
}

cout << "Przesuwam " << endl;

for( int i = 0; i < 7; i++ ) {
    for( int j = 0; j < 6; j++ ) {
        //wskaźnik przyjmuje wartość wskaźnika obok, dane "przesuwają" się w lewo
        p[ i ][ j ] = p[ i ][ j + 1 ];
    }
}
//w prawdziwym kodzie mam zmienną która wie gdzie dane powinny być wklejone, zastępuje ona 0 w tab
for( int i = 0; i < 7; i++ ) {
    //wypelnienie innymi danymi
    tab[ i ][ 0 ] = 99;
    //nowy adres dla ostatniego rzędu
    p[ i ][ 6 ] = & tab[ i ][ 0 ];
}


//ponowne wypisanie
for( int i = 0; i < 7; i++ ) {
    for( int j = 0; j < 7; j++ ) {
        cout <<* p[ i ][ j ] << " ";
    }
    cout << endl;
}

Ten kod umożliwia przesuwanie wyświetlanej zawartości tablicy bez przesuwania danych (tab[x][y] = tab[x][y+1]). Związane jest to z faktem że przerzucanie danych obok jest zawolne. (w normalnej wersji to nie inty a spore ilości danych). Posiadam funkcje przesuwającą wyświetlanie w każdą stronę.
Pewna sytuacja(mianowicie desynchronizacja) pojawia się gdy np. przesunę dane w lewo, do góry, a następnie w prawo.
(w analogicznych sytuacjach też zachodzi desynchronizacja, np. Góra, lewo dół itp.)

Wydaje mi się że problemem jest ta linijka:

C/C++
p[ i ][ j ] = p[ i ][ j + 1 ];

Ponieważ po pierwszych dwóch operacjach wskaźniki (zmienna p) wskazują na dane które są w innej kolumnie oraz rzędzie (zmienna tab) po kolejnym przesunięciu następuje desynchronizacja.
Czy ktoś wie jak to rozwiązać?

Tutaj jest graficzna reprezentacja problemu:
https://s12.postimg.io​/azqh6m1t9/image.png(jak powinno działać)
https://s13.postimg.io​/5pr2pb8t3/image.png(jak działa, w ostatnim etapie "w prawo", w prawej górnej części widać błąd
P-151467
mateczek
» 2016-09-05 20:12:05
nadpisujesz zerowy rząd i tym samym go tracisz !!!
C/C++
p[ i ][ j ] = p[ i ][ j + 1 ];
P-151475
Merulo
Temat założony przez niniejszego użytkownika
» 2016-09-05 21:01:19
Ale ja chce go stracić!!!

Dane wczytywane są z plików. Dlatego "stracenie" jest ok, ponieważ na to miejsce lądują nowe dane (z pliku) które wyświetlane są w odpowiednim miejscu. Jeżeli program stwierdzi że dane który były kiedyś na 0 są potrzebne to je sobie wczyta.

C/C++
for( int i = 0; i < 7; i++ ) {
    //wypelnienie innymi danymi
    tab[ i ][ 0 ] = 99;
    //nowy adres dla ostatniego rzędu
    p[ i ][ 6 ] = & tab[ i ][ 0 ];
}
Tutaj widać wyraźnie że na zerowym rzędzie lądują nowe dane. Dopiero gdy wylądują nowe następuje wyświetlanie.

Wydaje mi się, że rozwiązanie powinno być takie:

Ale w jednym masz rację, ta linijka jest problemem

C/C++
p[ i ][ j ] = p[ i ][ j + 1 ];

Zakłada ona że odpowiednie wskaźniki ze sobą graniczą, a tak nie musi być. Pytanie jak je w takim razie przesunąć.
P-151477
mateczek
» 2016-09-06 00:00:47
Swój kod najlepiej samemu prześledzić debuggerem w pracy krokowej i dowiedzieć się co źle robi. Ja kiedyś na potrzeby samo-dokształcania założyłem wątek odnośnie konstruktorów przenoszących i dla poćwiczenia postanowiłem napisać klasę macierzy 2D
http://cpp0x.pl/forum/temat/​?id=22952

Macierz2D była w tym kodzie jednowymiarową macierzą z interfejsem 2D przeciążony operator.

Do mojej starej klasy dopisałem funkcję przesuwania góra, dół, prawo, lewo
C/C++
//funkcja prawie nic nie robi. w ogóle nie rusza danych tylko ustawia zmienne offset
void matrix::setOffset( int offw, int offk ) {
    if( offw > 0 ) offsetw = offw;
    else if( offw < 0 ) offsetw = wiersze + offw;
    else offsetw = 0;
   
    if( offk > 0 ) offsetk = offk;
    else if( offk < 0 ) offsetk = kolumny + offk;
    else offsetk = 0;
   
}

Oraz zmodyfikowałem operator dostępu do elementu. Niewątpliwie wadą rozwiązania jest to że każdorazowo żądając dostępu do elementu wywołuje się ta funkcja
C/C++
int & operator ()( int w, int k ) { //wiersz kolumna do którego żądamy dostępu
    int index_w = w + offsetw; //do żądanego wiersza, kolumny dodajemy offset. offset jest ustawiony funkcją set offset
    int index_k = k + offsetk;
    if( index_w >= wiersze ) index_w -= wiersze; //nakład w porównaniu ze zwykłym dostępem do elementu
   
    if( index_k >= kolumny ) index_k -= kolumny; //nakład w porównaniu ze zwykłym dostępem do elementu
   
    return tab[ index_w * kolumny + index_k ]; // w normannym indeksowanu macierzy a[3][4] kompilator i tak takie obliczenie wykonuje
}



Całość kodu wygląda tak jak poniżej. Zdaję sobie sprawę, że dla pełnej funkcjonalności powinienem przerobić tą klasę na szablon, coby nie tylko INTy  trzymać w niej było można.
zobacz czy o taki efekt Ci  chodziło ???


Poniżej kod gotowy do kompilacji!

C/C++
#include <iostream>
using namespace std;

class matrix {
    int * tab = nullptr;
    int offsetw = 0, offsetk = 0;
    int wiersze = 0, kolumny = 0;
public:
    friend ostream & operator <<( ostream & s, matrix & m );
   
    matrix( int w, int k )
        : wiersze( w )
         , kolumny( k )
    { //konstruktor
        cout << "jestem konstruktor" << endl;
        int rozmiar = w * k;
        tab = new int[ rozmiar ];
    }
   
    matrix() { } //konstruktor domyślny nic nie robi
    ~matrix() {
        if( tab ) {
            delete[] tab;
            tab = nullptr;
        }
    }
   
   
    int & operator ()( int w, int k ) {
        int index_w = w + offsetw;
        int index_k = k + offsetk;
        if( index_w >= wiersze ) index_w -= wiersze;
       
        if( index_k >= kolumny ) index_k -= kolumny;
       
        return tab[ index_w * kolumny + index_k ];
    }
   
    int row() { return wiersze; }
    int column() { return kolumny; }
    matrix & operator =( matrix && wzor ); //operator przenoszenia
    matrix( matrix && wzor ); //konstruktor przenoszący
    matrix( const matrix & wzor ); //konstruktor kopiujący
    matrix operator *( matrix & m ); //operator mnożenia macierzy
    matrix & operator =( const matrix & wzor ); //operator przypisania. Ten nie powinien się odpalić w c++11
   
    void setOffset( int ofw, int ofk );
   
};



matrix & matrix::operator =( matrix && wzor )
{
    //operator przenoszenia
    cout << "jestem operator przenoszenia" << endl;
    std::swap( wzor.tab, tab ); //przepisanie tylko wskaźnika na macierz bez kopiowania danych
    std::swap( wiersze, wzor.wiersze ); //przepisanie zmiennych "wiersz" oraz "kolumna"
    std::swap( kolumny, wzor.kolumny );
    return * this;
}
matrix::matrix( matrix && wzor )
{
    //konstruktor przenoszący
    cout << "jestem konstruktor przenoszący" << endl;
    tab = wzor.tab; //przepisanie tylko wskaźnika na macierz bez kopiowania danych
    wiersze = wzor.wiersze; //przepisanie zmiennych "wiersz" oraz "kolumna"
    kolumny = wzor.kolumny;
    wzor.tab = nullptr;
}

matrix::matrix( const matrix & wzor ) {
    cout << "jestem konstruktor kopiujący" << endl;
    wiersze = wzor.wiersze;
    kolumny = wzor.kolumny;
    int rozmiar = wiersze * kolumny;
    tab = new int[ rozmiar ];
    for( int i = 0; i < rozmiar; i++ ) { //kopiowanie bufora
        tab[ i ] = wzor.tab[ i ];
    }
}

matrix & matrix::operator =( const matrix & wzor ) {
    cout << "jestem operator przypisania" << endl;
    wiersze = wzor.wiersze;
    kolumny = wzor.kolumny;
    int rozmiar = wiersze * kolumny;
    if( tab ) delete[] tab; // zwalnianie pamięci ze starej tablicy
   
    tab = new int[ rozmiar ]; // alokacja nowej
    for( int i = 0; i < rozmiar; i++ ) { //kopiowanie bufora
        tab[ i ] = wzor.tab[ i ];
    }
    return * this;
}


void matrix::setOffset( int offw, int offk ) {
    if( offw > 0 ) offsetw = offw;
    else if( offw < 0 ) offsetw = wiersze + offw;
    else offsetw = 0;
   
    if( offk > 0 ) offsetk = offk;
    else if( offk < 0 ) offsetk = kolumny + offk;
    else offsetk = 0;
   
}

ostream & operator <<( ostream & s, matrix & m ) {
    for( int w = 0; w < m.wiersze; w++ ) {
        for( int k = 0; k < m.kolumny; k++ ) {
            s << m( w, k ) << " ";
        }
        s << endl;
    }
    return s;
}


int main() {
    matrix m1( 4, 4 ); //macierz 4 na 4
   
    //wypełniam
    int liczba = 10;
    for( int w = 0; w < m1.row(); w++ ) {
        for( int k = 0; k < m1.column(); k++ ) {
            m1( w, k ) = liczba++;
        }
    }
    cout << m1 << endl;
    m1.setOffset( 1, 1 ); //góra lewo o jeden
    cout << m1 << endl;
    m1.setOffset( - 1, - 1 ); //dół prawo o jeden
    cout << m1 << endl;
   
    //dalsze testy klasy matrix!!!
    m1.setOffset( 0, 0 );
    m1( 1, 1 ) = 66; //modyfikuje element (1,1)
    cout << m1( 1, 1 ) << endl; //wyświetlam zmodyfikowany element
    m1.setOffset( 1, 1 ); //tranformacja macierzy (góra i lewo o jeden)
    cout << m1( 0, 0 ) << endl; //zmodyfikowany element to teraz pozycja 0 0
}



PS.
Reasumując. Jeśli masz duże obiekty to trzymaj w tablicy obiektów wskaźniki do nich. stwórz sobie drugą tablice wskaźników i zwyczajnie przekopiuj same wskaźniki. zgodnie z ideą transformacji. Ja zaś zaproponowałem sposób, który sprawia że procedura transformacji jest szybka(tylko ustawienie zmiennych). I możesz z nią szaleć do woli. Ale kosztem nakładu przy dostępie do elementu. 





//edit
kopiowanie wskaźników jest tak samo szybkie jak kopiownaie intów więc robiąc transformację na int zadziała na wskaźnikach.
Poniżej kod gotowy do kompilacji i sprawdzenia


C/C++
#include <iostream>
using namespace std;



int main() {
    int matrix[ 5 ][ 5 ];
    int matrixT[ 5 ][ 5 ];
    //wypełniam
    int liczba = 10;
    for( int w = 0; w < 5; w++ ) {
        for( int k = 0; k < 5; k++ ) {
            matrix[ w ][ k ] = liczba;
            matrixT[ w ][ k ] = liczba;
            liczba++;
        }
    }
   
    int offw = 1, offk = 1; // Tu wybierz o ile i w którą stronę dokonać transformaci
   
   
    //skopiowana funkcja set offset :P nie chciało mi się wymyślać innej metody
    int offsetw, offsetk;
    if( offw > 0 ) offsetw = offw;
    else if( offw < 0 ) offsetw = 5 + offw;
    else offsetw = 0;
   
    if( offk > 0 ) offsetk = offk;
    else if( offk < 0 ) offsetk = 5 + offk;
    else offsetk = 0;
   
   
    // prawie skopiowany operator dostępu do elementu transformacja
    for( int w = 0; w < 5; w++ ) {
        for( int k = 0; k < 5; k++ ) {
            int index_w = w + offsetw;
            int index_k = k + offsetk;
            if( index_w >= 5 ) index_w -= 5;
           
            if( index_k >= 5 ) index_k -= 5;
           
            matrixT[ w ][ k ] = matrix[ index_w ][ index_k ];
        }
    }
   
    //wyświetlenie po transformacji
    for( int w = 0; w < 5; w++ ) {
        for( int k = 0; k < 5; k++ ) {
            cout << matrixT[ w ][ k ] << " ";
        }
        cout << endl;
    }
   
}
P-151488
Merulo
Temat założony przez niniejszego użytkownika
» 2016-09-07 22:11:05
Ok, dzięki za kod.
Popatrzę sobie.

Jednak problem jest rozwiązany,
zrobiłem sobie tablicę która przechowuje informacje o wskaźnikach -> nie muszę przepisywać wartości, więc nie tracę danych. Bardzo wygodne rozwiązanie.

W każdym razie kod który wkleiłeś wygląda sensownie, sprawdzę.
P-151535
mateczek
» 2016-09-08 00:41:27
Najlepsze rozwiązanie. kopiowanie tablicy wskaźników to nie jest jakiś problem. A z wszelkimi rotacjami i transformacjami macierzy jest taki problem, że ciężko to zrobić bez dodatkowej tablicy. Z prostego powodu, że przenosząc element zawsze nadpisujesz któryś inny. Z dodatkową tablicą problem prawie zawsze jest łatwiejszy do ogarnięcia.

Kod wkleiłem tylko po to byś sobie skompilował i zobaczył czy o taki efekt ci chodziło:) Analiza czyjegoś kodu zawsze jest kłopotliwa!!! (dla mnie to nie był wielki problem dopisać jedną funkcję do klasy którą już miałem :P )

P-151539
« 1 »
  Strona 1 z 1