Jump to content

SUBIECTE NOI
« 1 / 5 »
RSS
Durere umar AC Joint

Care este cea mai sanatoasa paine?

Zgomot ritmic ce urmeaza rotirea ...

Merita Lumix FZ82 in 2024?
 Nu pot activa Memory Integrity

Supratensiuni accidentale

Cuțit/ briceag drumetie

Cum am acces la o parte dintr-un ...
 Mother's Day

Recomandare aparat de vidat alime...

Izolatie exterioara casa parter P...

Cuvinte si expresii neclare
 Mod de lucru Purmo Tempco Digital...

Samsung S90C vs LG C3

Problema sunet RCS

Amortizor sertare bucatarie
 

[C++] Parcurgerea elegantă a matricilor, în orice ordine

- - - - -
  • Please log in to reply
No replies to this topic

#1
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,253
  • Înscris: 24.02.2007
Probleme ce implica matrici sunt printre cele mai frecvente din liceu, si nu numai.

Sa luam un exemplu: https://forum.softpe...onacci-matrice/ . Cerinta, pe scurt: popularea unei matrici cu un sir Fibonacci, insa matricea trebuie parcursa intr-o ordine mai aparte.

Si unde-i problema?

Elevul incepe, probabil, sa scrie diverse for'uri, pentru a parcurge matricea in ordinea dorita, amesteca apoi logica cu generarea sirului lui Fibonacci rezultand:

  • Fie un amalgam care-l zapaceste de elev, acesta nereusind sa rezolve problema pana la capat (cum se intampla in topicul indicat)
  • Fie un amalgam functional, dar aproape ilizibil de oricine altcineva, astfel incat urmatorul elev ce are aceeasi problema nu intelege rezolvarea si-o ia de la capat.

De ce se intampla asta?

Fiindca elevii pun totul la gramada si nu stiu/vor/constientizeaza ca probleme diferite ar trebui rezolvate separat.

  • O matrice o parcurgi intr-un anume fel indiferent de ce citesti din/scrii in ea
  • Sirul lui Fibonacci il generezi intr-un anume fel indiferent ce faci apoi cu el

Se poate rezolva mai elegant?

Evident

Cum?

C++ abstractizeaza iterarea prin structuri de date cu ajutorul iteratorilor. Drept urmare, de ce sa nu folosim iteratori si pentru a parcurge matrici?

Ne aducem aminte ca o matrice gen int[3][3] e asezata secvential in memorie, ca un sir de 3*3 = 9 elemente. Asa cum putem folosi un pointer pentru a ne plimba prin sir, la fel putem folosi un pointer pentru a ne plimba prin matrice.

Iteratorul practic va avea un comportament asemenea unui astfel de pointer. Va retine:
  • Pointerul spre datele din matrice
  • Dimensiunile matricii
  • Pozitia curenta
  • O strategie de parcurgere a matricii

template<typename T, typename Strategy>
class MatrixIterator
{
public:
	typedef std::random_access_iterator_tag iterator_category;
	typedef T value_type;
	typedef ptrdiff_t difference_type;
	typedef T* pointer;
	typedef T& reference;
	MatrixIterator(T* matrix, size_t width, size_t height, Strategy strategy)
		: matrix_(matrix), width_(width), height_(height), current_index_(0), strategy_(strategy)
	{}
	template<size_t Width, size_t Height>
	explicit MatrixIterator(T (&matrix)[Width][Height], Strategy strategy)
		: MatrixIterator(matrix, Width, Height, strategy)
	{}
	MatrixIterator(const MatrixIterator& other) = default;
	MatrixIterator(MatrixIterator&& other) = default;
	MatrixIterator& operator=(const MatrixIterator& other) = default;
	MatrixIterator& operator=(MatrixIterator&& other) = default;
	T& operator*() const
	{
		return this->operator[](0);
	}
	T& operator[](int offset) const
	{
		return matrix_[strategy_(width_, height_, current_index_ + offset)];
	}
	MatrixIterator& operator++()
	{
		current_index_ += 1;
		return *this;
	}
	MatrixIterator operator++(int)
	{
		MatrixIterator temp = *this;
		++*this;
		return temp;
	}
	MatrixIterator& operator--()
	{
		current_index_ -= 1;		   
		return *this;
	}
	MatrixIterator operator--(int)
	{
		MatrixIterator temp = *this;
		--*this;
		return temp;
	}
	MatrixIterator& operator+=(int n)
	{
		current_index_ += n;
		return *this;
	}
	MatrixIterator& operator-=(int n)
	{
		return *this += -n;
	}
	MatrixIterator operator+(int n) const
	{
		MatrixIterator copy(*this);
		return copy += n;
	}
	MatrixIterator operator-(int n) const
	{
		MatrixIterator copy(*this);
		return copy -= n;
	}
	difference_type operator-(const MatrixIterator& other) const
	{
		return current_index_ - other.current_index_;
	}
	bool operator==(const MatrixIterator& other) const
	{
		return std::tie(width_, height_, current_index_, strategy_)
			== std::tie(other.width_, other.height_, other.current_index_, strategy_);
	}
	bool operator!=(const MatrixIterator& other) const
	{
		return !(*this == other);
	}

	bool operator<(const MatrixIterator& other) const
	{
		return std::tie(width_, height_, current_index_, strategy_)
			 < std::tie(other.width_, other.height_, other.current_index_, strategy_);
	}
	bool operator>(const MatrixIterator& other) const
	{
		return std::tie(width_, height_, current_index_, strategy_)
			 > std::tie(other.width_, other.height_, other.current_index_, strategy_);
	}
	bool operator<=(const MatrixIterator& other) const
	{
		return !(*this > other);
	}
	bool operator>=(const MatrixIterator& other) const
	{
		return !(*this < other);
	}
private:
	T* matrix_;
	size_t width_;
	size_t height_;
	size_t current_index_;
	Strategy strategy_;
};


Pare complicat la prima vedere, dar nu prea face altceva decat sa reproduca operatii primitive.
Atentie, iteratorul trebuie folosit cu grija. Nu se verifica accesul la zone de memorie nedorite daca sunt depasite limitele.

Constructorul are cam multi parametrii?

Hai sa scriem niste functii ajutatoare:

template<typename T, typename Strategy>
MatrixIterator<T, Strategy> make_matrix_iterator(T* matrix, size_t width, size_t height, Strategy strategy)
{
	return MatrixIterator<T, Strategy>(matrix, width, height, strategy);
}

template<typename T, typename Strategy, size_t Width, size_t Height>
MatrixIterator<T, Strategy> make_matrix_iterator(T(&matrix)[Width][Height], Strategy strategy)
{
	return make_matrix_iterator<T, Strategy>(matrix[0], Width, Height, strategy);
}

template<typename T, typename Strategy>
MatrixIterator<T, Strategy> make_matrix_iterator_end(T* matrix, size_t width, size_t height, Strategy strategy)
{
	auto result = make_matrix_iterator(matrix, width, height, strategy);
	result += width * height;
	return result;
}

template<typename T, typename Strategy, size_t Width, size_t Height>
MatrixIterator<T, Strategy> make_matrix_iterator_end(T(&matrix)[Width][Height], Strategy strategy)
{
	return make_matrix_iterator_end<T, Strategy>(matrix[0], Width, Height, strategy);
}


Si cum ma ajuta asta sa parcug matricea intr-o ordine diferita?

Iteratorul tine evidenta pozitiei curente intr-o matrice de dimensiuni date. Cand ii este cerut in element, el nu va folosi direct pozitia curenta ci va cere unei strategii de iterare ce pozitie sa foloseasca defapt.

Daca se doreste iterarea in ordine normala sus/jos, stanga/dreapta, strategia nu face altceva decat sa returneze pozitia curenta:

size_t normal_matrix_order(size_t width, size_t height, size_t current_index)
{
	return current_index;
}


Daca insa se vrea, de exemplu, iterarea in ordine inversa, jos/sus, dreapta/stanga, strategia calculeaza o alta pozitie:

size_t reverse_matrix_order(size_t width, size_t height, size_t current_index)
{
	return width * height - current_index - 1;
}


Cum folosesc asta?

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &normal_matrix_order);
auto end = make_matrix_iterator_end(matrix, &normal_matrix_order);

for (auto it = start; it != end; ++it)
{
	std::cout << *it << ' ';
}


Quote

1 2 3 4 5 6 7 8 9

Ordine inversa:

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &reverse_matrix_order);
auto end = make_matrix_iterator_end(matrix, &reverse_matrix_order);

for (auto it = start; it != end; ++it)
{
	std::cout << *it << ' ';
}


Quote

9 8 7 6 5 4 3 2 1

S-a schimbat ceva? Doar am zis ca vreau sa iterez in alta ordine, indicand strategia folosita

Pot itera si in ordinea de mai sus?

Definesc o strategie noua:

size_t alternate_matrix_order(size_t width, size_t height, size_t current_index)
{
	auto x = current_index % width;
	auto y = current_index / height;

	if (y % 2 == 0)
	{
		//normal order
		return current_index;
	}
	else
	{
		//alternate order
		auto firstInRow = y * height;
		auto offsetInRow = width - x - 1;
		return firstInRow + offsetInRow;
	}
}

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &alternate_matrix_order);
auto end = make_matrix_iterator_end(matrix, &alternate_matrix_order);

for (auto it = start; it != end; ++it)
{
	std::cout << *it << ' ';
}


Quote

1 2 3 6 5 4 7 8 9

Am vazut cum citesc elemente din matrice, dar cum le modific?

Iteratorul permite si modificarea elementului curent.

Exemplul de mai jos populeaza, in ordine inversa, o matrice cu puteri de-a lui 2, iar apoi le afiseaza in ordinea obisnuita a matricii

int powers_of_two[3][3];
auto powers_of_two_start = make_matrix_iterator(powers_of_two, &reverse_matrix_order);
auto powers_of_two_end = make_matrix_iterator_end(powers_of_two, &reverse_matrix_order);

int current_value = 1;

for (auto it = powers_of_two_start; it != powers_of_two_end; ++it)
{
	*it = current_value;
	current_value *= 2;
}

powers_of_two_start = make_matrix_iterator(powers_of_two, &normal_matrix_order);
powers_of_two_end = make_matrix_iterator_end(powers_of_two, &normal_matrix_order);

for (auto it = powers_of_two_start; it != powers_of_two_end; ++it)
{
	std::cout << *it << ' ';
}


Quote

256 128 64 32 16 8 4 2 1

Combinatiile sunt atat de flexibile incat se pot copia elementele dintr-o matrice, parcursa intr-un anumit mod, in alta matrice, parcursa in alt mod:

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &alternate_matrix_order);
auto end = make_matrix_iterator_end(matrix, &alternate_matrix_order);


int destination_matrix[3][3];

auto destination_start = make_matrix_iterator(destination_matrix, &reverse_matrix_order);

std::copy(start, end, destination_start);


Attached File  Untitled.png   7.23K   22 downloads

Ce fac daca am o matrice de siruri de caractere?

Acelasi lucru, codului de mai sus nu-i pasa ce stochezi in matrice.

Cool Posted Image

Anunturi

Neurochirurgie minim invazivă Neurochirurgie minim invazivă

"Primum non nocere" este ideea ce a deschis drumul medicinei spre minim invaziv.

Avansul tehnologic extraordinar din ultimele decenii a permis dezvoltarea tuturor domeniilor medicinei. Microscopul operator, neuronavigația, tehnicile anestezice avansate permit intervenții chirurgicale tot mai precise, tot mai sigure. Neurochirurgia minim invazivă, sau prin "gaura cheii", oferă pacienților posibilitatea de a se opera cu riscuri minime, fie ele neurologice, infecțioase, medicale sau estetice.

www.neurohope.ro

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

Forumul Softpedia foloseste "cookies" pentru a imbunatati experienta utilizatorilor Accept
Pentru detalii si optiuni legate de cookies si datele personale, consultati Politica de utilizare cookies si Politica de confidentialitate