두 벡터 분해

Sep 11 2020

"분해"라는 단어가 정확하길 바라지 만 문제는 간단합니다. 작업 후 두 개의 목록이 있고 한 목록에서 다른 목록으로 어떤 변화가 발생했는지 알고 싶습니다. 따라서 두 개의 목록 A와 B를 "Both", "Only A"및 "Only B"로 "분해"하고 싶습니다.

template <class T>
void decompose(std::vector<T*> &a, std::vector<T*> &b, std::vector<T*> &only_a, std::vector<T*> &only_b, std::vector<T*> &both) {
    only_a = a;
    only_b = b;
    for (T* x : a) {
        for (T* y : b) {
            if (x == y) {
                both.push_back(x);
            }
        }
    }

    {
        auto it = only_a.begin();
        while (it != only_a.end()) {
            bool erase = false;
            for (T* x : both) {
                if (x == *it) {
                    it = only_a.erase(it);
                    erase = true;
                }
            }
            if (!erase) {
                it++;
            }
        }
    }

    {
        auto it = only_b.begin();
        while(it != only_b.end()) {
            bool erase = false;
            for (T* x : both) {
                if (x == *it) {
                    it = only_b.erase(it);
                    erase = true;
                }
            }
            if (!erase) {
                it++;
            }
        }
    }
}

두 번 얽힌 3 개의 루프보다 더 빠른 방법이 있어야한다고 생각합니다.

답변

4 TedLyngmo Sep 11 2020 at 07:28

읽고 유지 관리하기 쉬운 것을 사용 set_difference하고 set_intersection중복이없는 정렬 된 범위에서 잘 작동합니다.

std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(both));

only_a.reserve(a.size() - both.size());
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(only_a));

only_b.reserve(b.size() - both.size());
std::set_difference(b.begin(), b.end(), a.begin(), a.end(), std::back_inserter(only_b));

...하지만이를 위해서는 범위를 세 번 반복해야하며 더 효율적인 것을 찾고 있다고 생각합니다.

첫째, 나는 복사하여 시작되지 것입니다 a및 b에 only_a와 only_b각각. 대신 위에 링크 한 표준 함수에 대한 예제 구현에서 영감을 얻어 자신 만의 유사한 알고리즘을 만듭니다. 이를 위해서는 Ts를 다음과 비교할 수 있어야합니다 operator<.

#include <algorithm>
#include <iterator>

template <class T>
void decompose(std::vector<T>& a,
               std::vector<T>& b,
               std::vector<T>& only_a,
               std::vector<T>& only_b,
               std::vector<T>& both)
{
    // Sort the input or require the input to be sorted like some algorithms do
    // If you'd like the input to be unchanged, make a and b const and make
    // copies of them instead and sort those copies.
    std::sort(a.begin(), a.end());
    std::sort(b.begin(), b.end());

    // clear destination vectors or skip this if you want to append instead
    only_a.clear();
    only_b.clear();

    // the actual algorithm - loop for as long as both vectors have elements

    auto ait = a.begin();
    auto bit = b.begin();

    while(ait != a.end() && bit != b.end()) {
        if(*ait < *bit) {
            only_a.push_back(*ait++);  // can only be in a
        } else if(*bit < *ait) {
            only_b.push_back(*bit++);  // can only be in b
        } else {
            both.push_back(*ait++);    // must be in both
            ++bit;
        }
    }

    // Add the remaining elements if not both ait and bit have reached their end()
    if(ait != a.end()) std::copy(ait, a.end(), std::back_inserter(only_a));
    else if(bit != b.end()) std::copy(bit, b.end(), std::back_inserter(only_b));
}

또는 더 일반적으로 만들고 반복자에서만 작동하도록하고 사용자가 비교 펑터 를 제공 할 수있는 가능성을 추가 하십시오 . 이를 위해서는 범위에서 비교 펑터가 사용 된 경우와 동일한 순서로 범위를 정렬 std::sort해야합니다. 기본 비교 펑 터는 std::less<>관련된 유형에 대해 전문화되지 않은 경우 operator<요소를 비교하는 데 사용 합니다.

#include <functional> // less
#include <iterator>   // iterator_traits

template <
    class First1, class Last1, class First2, class Last2,
    class OnlyAinserter, class OnlyBinserter, class BothInserter,
    class Comp = std::less<typename std::iterator_traits<First1>::value_type>
    // class Comp = std::less<> // <- is sufficient in C++14 and forward
>
void decompose(First1 ait, Last1 aend, First2 bit, Last2 bend,
               OnlyAinserter onlyait, OnlyBinserter onlybit, BothInserter bothit,
               Comp comp = Comp{})
{
    // loop for as long as both vectors have elements
    while(ait != aend && bit != bend) {
        if(comp(*ait, *bit)) {
            *onlyait++ = *ait++;  // can only be in a
        } else if(comp(*bit, *ait)) {
            *onlybit++ = *bit++;  // can only be in b
        } else {
            *bothit++ = *ait++;   // must be in both
            ++bit;
        }
    }

    // Add the remaining elements if not both ait and bit have reached aend/bend
    if(ait != aend) std::copy(ait, aend, onlyait);
    else if(bit != bend) std::copy(bit, bend, onlybit);
}

그런 다음 기본 비교 펑터를 사용하여 다음과 같이 호출 할 수 있습니다 .

decompose(a.begin(), a.end(), b.begin(), b.end(), 
    std::back_inserter(only_a), std::back_inserter(only_b), std::back_inserter(both));

또는 아래와 같이 비교 펑터를 제공합니다 . 이 예에서는 범위를 내림차순으로 정렬해야합니다.

decompose(a.begin(), a.end(), b.begin(), b.end(), 
    std::back_inserter(only_a), std::back_inserter(only_b), std::back_inserter(both),
    [](auto& A, auto& B) { return A > B; } // std::greater<>
);