set_differences

Zbiór gotowych algorytmów z standardowej biblioteki języka C++ jest bardzo użyteczny, o tym nie trzeba długo przekonywać. Choć czasem chcąc wykonać kilka rzeczy, bądź kilka kolejnych algorytmów na tych samych danych, możemy stracić na optymalności, szczególnie jak danych jest wiele, a złożoność algorytmu liniowa.

Czasem warto napisać coś dedykowanego, lub połączyć 2 algorytmy, oczywiście na ile jest to możliwe i w miarę proste, szczególnie, gdy oba algorytmy maja podobną budowę.

Miałem taki problem, potrzebowałem z 2 kontenerów wyznaczyć różnice jednego kontenera względem drugiego i odwrotnie. Pierwotny problem był nieco inny, chciałem jeszcze wyznaczyć część wspólną, ale jak się później okazało, nie jest mi ona potrzebna ;)

Do wyznaczania różnicy dwóch zbiorów, STL posiada odpowiedni algorytm, jest to set_difference w standardowej przestrzeni nazw. Jego budowa wewnętrzna jest dosyć prosta, złożoność liniowa. Aczkolwiek wywołanie dwukrotne tego algorytmu na tych samych zakresach w celu wyznaczenia różnić w stosunku do siebie samych zajmie dwa razy więcej czasu. A wystarczy tylko dodać „jedną linijkę” do algorytmu i mamy zysk prawie 50%.

Poniżej „moja” implementacja algorytmu set_differences, wyznaczającego różnice dwóch posortowanych zakresów (algorytm operuje na posortowanych danych) względem siebie, tak, że dest1 zawiera wszystkie elementy występujące w pierwszym zakresie, a nie występujące w drugim, a dest2 odwrotnie, czyli elementy z drugiego nie występujące w pierwszym.

template<typename InIt1, typename InIt2, typename OutIt1, typename OutIt2>
void set_differences(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2,
					 OutIt1 destDiff1, OutIt2 destDiff2)
{
	while (first1 != last1 && first2 != last2) {
		if (*first1 < *first2) {
			*destDiff1 = *first1;
			++destDiff1;
			++first1;
		} else if (*first2 < *first1) {
			*destDiff2 = *first2;
			++destDiff2;
			++first2;
		} else {
			++first1;
			++first2;
		}
	}
	std::copy(first1, last1, destDiff1);
	std::copy(first2, last2, destDiff2);
}

I kod testujący:

	int src1[] = { 0, 1, 2, 3, 4, 5 };
	int src2[] = { 4, 5, 6, 7, 8, 9 };
 
	int* src1_end = src1 + sizeof(src1) / sizeof(int);
	int* src2_end = src2 + sizeof(src2) / sizeof(int);
 
	std::vector<int>dst1;
	std::vector<int>dst2;
 
	set_differences(src1, src1_end, src2, src2_end,
		std::back_inserter(dst1), std::back_inserter(dst2));
 
	std::ostream_iterator<int> out(std::cout, " ");
 
	std::cout << "dst1: ";
	std::copy(dst1.begin(), dst1.end(), out);
	std::cout << "\n";
 
	std::cout << "dst2: ";
	std::copy(dst2.begin(), dst2.end(), out);
	std::cout << "\n";

ukazujący, że to rzeczywiście działa, wynik powinien być następujący:

dst1: 0 1 2 3
dst2: 6 7 8 9

Chcąc dodać jeszcze wspomniane wyżej wyznaczanie sumy, wystarczyłoby dodać kolejną dodatkową linijkę do kodu, bo budowa set_intersection jest zbliżona do set_difference.

Jak widać czasem warto pomyśleć i się zastanowić, bo z niewielkim wysiłkiem można osiągnąć dużo lepsze efekty ;p

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *