벡터 중 하나만 사용하는 기준을 사용하여 동일한 방식으로 두 벡터를 정렬하려면 어떻게해야합니까?


84

벡터 중 하나만 사용하는 기준을 사용하여 동일한 방식으로 두 벡터를 정렬하려면 어떻게해야합니까?

예를 들어 동일한 크기의 벡터가 두 개 있다고 가정합니다.

vector<MyObject> vectorA;
vector<int> vectorB;

그런 다음 vectorA비교 기능을 사용하여 정렬 합니다. 정렬 순서가 변경되었습니다 vectorA. 동일한 재정렬을 vectorB어떻게 적용 할 수 있습니까?


한 가지 옵션은 구조체를 만드는 것입니다.

struct ExampleStruct {
    MyObject mo;
    int i;
};

다음 정렬의 내용을 포함하는 벡터 vectorAvectorB하나의 벡터로 압축 업 :

// vectorC[i] is vectorA[i] and vectorB[i] combined
vector<ExampleStruct> vectorC;

이것은 이상적인 해결책이 아닌 것 같습니다. 특히 C ++ 11에 다른 옵션이 있습니까?


일부 입력 및 해당 원하는 출력과 함께 예제를 제공 할 수 있습니까? 질문을 이해하는 데 문제가 있습니다
Andy Prowl

나는 그가 (효과적으로) 내용 vectorA과 내용 을 vectorB모두 vectorB.
Mooing Duck 2013-06-12

내 생각 이 질문은 거의 중복하지 않을 경우 정확한 중복
음매을 오리

vectorA의 값을 기준으로 세 번째 벡터 (인덱스 0, ... vectorA.size ())를 정렬하고 해당 인덱스를 vectorB에 "적용"하는 것은 어떻습니까? 예 : stackoverflow.com/a/10581051/417197
André

개인적으로 저는 vector<pair<MyObject, int>>. 그러면 두 목록이 동기화되지 않는 것에 대해 걱정할 필요가 없습니다. 한 정렬은 두 데이터 세트를 동시에 재정렬합니다. 그리고 작성해야 할 추가 구조체가 없습니다.
cHao

답변:


117

정렬 순열 찾기

std::vector<T>a와의 비교가 주어지면 T이 비교를 사용하여 벡터를 정렬하는 경우 사용할 순열을 찾을 수 있기를 원합니다.

template <typename T, typename Compare>
std::vector<std::size_t> sort_permutation(
    const std::vector<T>& vec,
    Compare& compare)
{
    std::vector<std::size_t> p(vec.size());
    std::iota(p.begin(), p.end(), 0);
    std::sort(p.begin(), p.end(),
        [&](std::size_t i, std::size_t j){ return compare(vec[i], vec[j]); });
    return p;
}

정렬 순열 적용

a std::vector<T>와 순열이 주어지면 순열 std::vector<T>에 따라 재정렬 되는 새로운 것을 만들 수 있기를 원합니다 .

template <typename T>
std::vector<T> apply_permutation(
    const std::vector<T>& vec,
    const std::vector<std::size_t>& p)
{
    std::vector<T> sorted_vec(vec.size());
    std::transform(p.begin(), p.end(), sorted_vec.begin(),
        [&](std::size_t i){ return vec[i]; });
    return sorted_vec;
}

물론 apply_permutation새로 정렬 된 복사본을 반환하는 대신 벡터를 변경 하도록 수정할 수 있습니다. 이 접근 방식은 여전히 ​​선형 시간 복잡성이며 벡터의 항목 당 1 비트를 사용합니다. 이론적으로는 여전히 선형적인 공간 복잡성입니다. 그러나 실제로 sizeof(T)큰 경우 메모리 사용량이 크게 감소 할 수 있습니다. ( 자세히보기 )

template <typename T>
void apply_permutation_in_place(
    std::vector<T>& vec,
    const std::vector<std::size_t>& p)
{
    std::vector<bool> done(vec.size());
    for (std::size_t i = 0; i < vec.size(); ++i)
    {
        if (done[i])
        {
            continue;
        }
        done[i] = true;
        std::size_t prev_j = i;
        std::size_t j = p[i];
        while (i != j)
        {
            std::swap(vec[prev_j], vec[j]);
            done[j] = true;
            prev_j = j;
            j = p[j];
        }
    }
}

vector<MyObject> vectorA;
vector<int> vectorB;

auto p = sort_permutation(vectorA,
    [](T const& a, T const& b){ /*some comparison*/ });

vectorA = apply_permutation(vectorA, p);
vectorB = apply_permutation(vectorB, p);

자원


4
내 컴파일러에서 "비교 및 비교"를 "비교 비교"로 변경해야했습니다.
SmallChess

2
std :: iota 함수 (vs2012)를 실행하려면 헤더 <numeric>도 포함해야했습니다.
스미스

1
" 물론 apply_permutation새로운 정렬 된 복사본을 반환하는 대신 제공 한 벡터를 변경 하도록 수정할 수 있습니다. "적어도 개념적으로는 답변에 유용한 추가 기능이 있다는 것을 알 수 있습니다. 이를 apply_permutation자체 와 동일한 방식으로 구현 하시겠습니까 ?
ildjarn

2
@ildjarn 귀하의 제안으로 답변을 업데이트했습니다.
Timothy Shields

2
훌륭한 스 니펫에 감사드립니다! Compare& compare그러나 sort_permutation 선언 은 const 여야합니다. 그렇지 않으면 std :: less <T> 또는 이와 유사한 것을 사용할 수 없습니다.
kotakotakota

9

range-v3을 사용하면 간단하며 zip보기를 정렬 할 수 있습니다.

std::vector<MyObject> vectorA = /*..*/;
std::vector<int> vectorB = /*..*/;

ranges::v3::sort(ranges::view::zip(vectorA, vectorB));

또는 명시 적으로 투영을 사용합니다.

ranges::v3::sort(ranges::view::zip(vectorA, vectorB),
                 std::less<>{},
                 [](const auto& t) -> decltype(auto) { return std::get<0>(t); });

데모


VS2017에서 시도했지만 내가 무엇을 시도했는지에 상관없이 컴파일되거나 작동하지 않았습니다. 이 라이브러리는 VS2019, GCC 및 LLVM에서 작동하는 것으로 알려져 있습니다.
Contango 2010 년

4

제가 생각 해낸 확장 프로그램으로 기여하고 싶습니다. 목표는 간단한 구문을 사용하여 여러 벡터를 동시에 정렬 할 수있는 것입니다.

sortVectorsAscending(criteriaVec, vec1, vec2, ...)

알고리즘은 Timothy가 제안한 것과 동일하지만 가변 템플릿을 사용 하므로 임의 유형의 여러 벡터를 동시에 정렬 할 수 있습니다.

다음은 코드 조각입니다.

template <typename T, typename Compare>
void getSortPermutation(
    std::vector<unsigned>& out,
    const std::vector<T>& v,
    Compare compare = std::less<T>())
{
    out.resize(v.size());
    std::iota(out.begin(), out.end(), 0);

    std::sort(out.begin(), out.end(),
        [&](unsigned i, unsigned j){ return compare(v[i], v[j]); });
}

template <typename T>
void applyPermutation(
    const std::vector<unsigned>& order,
    std::vector<T>& t)
{
    assert(order.size() == t.size());
    std::vector<T> st(t.size());
    for(unsigned i=0; i<t.size(); i++)
    {
        st[i] = t[order[i]];
    }
    t = st;
}

template <typename T, typename... S>
void applyPermutation(
    const std::vector<unsigned>& order,
    std::vector<T>& t,
    std::vector<S>&... s)
{
    applyPermutation(order, t);
    applyPermutation(order, s...);
}

template<typename T, typename Compare, typename... SS>
void sortVectors(
    const std::vector<T>& t,
    Compare comp,
    std::vector<SS>&... ss)
{
    std::vector<unsigned> order;
    getSortPermutation(order, t, comp);
    applyPermutation(order, ss...);
}

// make less verbose for the usual ascending order
template<typename T, typename... SS>
void sortVectorsAscending(
    const std::vector<T>& t,
    std::vector<SS>&... ss)
{
    sortVectors(t, std::less<T>(), ss...);
}

Ideone 에서 테스트하십시오 .

나는 이 블로그 포스트 에서 이것을 조금 더 잘 설명한다 .


3

순열을 사용한 내부 정렬

데이터가 너무 크고 정렬 된 벡터에 더 많은 메모리를 할당하고 싶지 않다면 제자리에서 수행해야하지만 Timothy와 같은 순열을 사용합니다 . 다음은 순열을 사용한 O (n) (선형 복잡도) 내부 정렬 의 예입니다 .

트릭은 순열과 역순 열을 가져와 마지막 정렬 단계에서 데이터를 덮어 쓸 위치를 아는 것입니다.

template <class K, class T> 
void sortByKey(K * keys, T * data, size_t size){
    std::vector<size_t> p(size,0);
    std::vector<size_t> rp(size);
    std::vector<bool> sorted(size, false);
    size_t i = 0;

    // Sort
    std::iota(p.begin(), p.end(), 0);
    std::sort(p.begin(), p.end(),
                    [&](size_t i, size_t j){ return keys[i] < keys[j]; });

    // ----------- Apply permutation in-place ---------- //

    // Get reverse permutation item>position
    for (i = 0; i < size; ++i){
        rp[p[i]] = i;
    }

    i = 0;
    K savedKey;
    T savedData;
    while ( i < size){
        size_t pos = i;
        // Save This element;
        if ( ! sorted[pos] ){
            savedKey = keys[p[pos]];
            savedData = data[p[pos]];
        }
        while ( ! sorted[pos] ){
            // Hold item to be replaced
            K heldKey  = keys[pos];
            T heldData = data[pos];
            // Save where it should go
            size_t heldPos = rp[pos];

            // Replace 
            keys[pos] = savedKey;
            data[pos] = savedData;

            // Get last item to be the pivot
            savedKey = heldKey;
            savedData = heldData;

            // Mark this item as sorted
            sorted[pos] = true;

            // Go to the held item proper location
            pos = heldPos;
        }
        ++i;
    }
}

1
O (N ^ 2)처럼 보이지만 그렇지 않습니다. 내부 while은 항목이 정렬되지 않은 경우에만 실행됩니다. 내부가 데이터를 정렬하는 동안 반복하는 동안 외부를 건너 뜁니다.
MtCS

2
  1. 개별 벡터에서 쌍으로 구성된 벡터를 만듭니다.
    쌍의 벡터 초기화 쌍의 벡터에
    더하기

  2. 사용자 정의 정렬 비교기 만들기 :
    사용자 정의 개체의 벡터 정렬
    http://rosettacode.org/wiki/Sort_using_a_custom_comparator#C.2B.2B

  3. 쌍으로 구성된 벡터를 정렬하십시오.

  4. 쌍으로 구성된 벡터를 개별 벡터로 분리하십시오.

  5. 이 모든 것을 함수에 넣으십시오.

암호:

std::vector<MyObject> vectorA;
std::vector<int> vectorB;

struct less_than_int
{
    inline bool operator() (const std::pair<MyObject,int>& a, const std::pair<MyObject,int>& b)
    {
        return (a.second < b.second);
    }
};

sortVecPair(vectorA, vectorB, less_than_int());

// make sure vectorA and vectorB are of the same size, before calling function
template <typename T, typename R, typename Compare>
sortVecPair(std::vector<T>& vecA, std::vector<R>& vecB, Compare cmp)
{

    std::vector<pair<T,R>> vecC;
    vecC.reserve(vecA.size());
    for(int i=0; i<vecA.size(); i++)
     {
        vecC.push_back(std::make_pair(vecA[i],vecB[i]);   
     }

    std::sort(vecC.begin(), vecC.end(), cmp);

    vecA.clear();
    vecB.clear();
    vecA.reserve(vecC.size());
    vecB.reserve(vecC.size());
    for(int i=0; i<vecC.size(); i++)
     {
        vecA.push_back(vecC[i].first);
        vecB.push_back(vecC[i].second);
     }
}

참조 쌍의 벡터?
Mooing Duck 2013-06-12

무슨 뜻인지 알지만 참조 쌍은 쉽게 작동하지 않을 것입니다.
ruben2020

1
@ ruben2020 : 할 수 있지만 그렇게하면 문제가 해결됩니다. 두 개의 데이터가 서로 얽혀서 하나를 정렬하는 것이 다른 하나를 정렬해야한다면 실제로 가지고있는 것은 아직 통합되지 않은 개체 인 것처럼 보일 것입니다.
cHao

1

vectorA와 vectorB의 길이가 같다고 가정합니다. 또 다른 벡터를 생성 할 수 있습니다. pos라고합시다. 여기서 :

pos[i] = the position of vectorA[i] after sorting phase

그런 다음 pos를 사용하여 vectorB를 정렬 할 수 있습니다. 즉, 다음과 같이 vectorBsorted를 생성합니다.

vectorBsorted[pos[i]] = vectorB[i]

그런 다음 vectorBsorted는 vectorA와 동일한 인덱스 순열로 정렬됩니다.


0

이것이 작동하는지 확실하지 않지만 이와 같은 것을 사용할 것입니다. 예를 들어 두 벡터를 정렬하려면 내림차순 버블 정렬 방법과 벡터 쌍을 사용합니다.

내림차순 버블 정렬의 경우 벡터 쌍이 필요한 함수를 만듭니다.

void bubbleSort(vector< pair<MyObject,int> >& a)
{
    bool swapp = true;
    while (swapp) {
        int key;
        MyObject temp_obj;
        swapp = false;
        for (size_t i = 0; i < a.size() - 1; i++) {
            if (a[i].first < a[i + 1].first) {
                temp_obj = a[i].first;
                key = a[i].second;

                a[i].first = a[i + 1].first;
                a[i + 1].first = temp_obj;

                a[i].second = a[i + 1].second;
                a[i + 1].second = key;

                swapp = true;
            }
        }
    }
}

그 후 두 벡터 값을 하나의 벡터 쌍에 넣습니다. 동시에 값을 추가 할 수 있다면 이것을 사용하고 버블 정렬 함수를 호출하십시오.

vector< pair<MyObject,int> > my_vector;

my_vector.push_back( pair<MyObject,int> (object_value,int_value));

bubbleSort(my_vector);

2 개의 벡터를 더한 후 값을 사용하려면이 벡터를 사용하고 버블 정렬 함수를 호출하면됩니다.

vector< pair<MyObject,int> > temp_vector;

for (size_t i = 0; i < vectorA.size(); i++) {
            temp_vector.push_back(pair<MyObject,int> (vectorA[i],vectorB[i]));
        }

bubbleSort(temp_vector);

이게 도움이 되길 바란다. 안부, Caner


0

최근에 stl 알고리즘과 함께 작동하는 적절한 zip 반복기를 작성했습니다. 다음과 같은 코드를 생성 할 수 있습니다.

std::vector<int> a{3,1,4,2};
std::vector<std::string> b{"Alice","Bob","Charles","David"};

auto zip = Zip(a,b);
std::sort(zip.begin(), zip.end());

for (const auto & z: zip) std::cout << z << std::endl;

단일 헤더에 포함되며 유일한 요구 사항은 C ++ 17입니다. GitHub 에서 확인하세요 .

모든 소스 코드를 포함하는 코드 리뷰에 대한 게시물도 있습니다.


0

Timothy Shields 답변을 기반으로합니다.
약간만 조정 apply_permutaion하면 접기 표현식을 사용하여 한 번에 여러 유형의 여러 벡터에 순열을 적용 할 수 있습니다.

template <typename T, typename... Ts>
void apply_permutation(const std::vector<size_t>& perm, std::vector<T>& v, std::vector<Ts>&... vs) {

    std::vector<bool> done(v.size());
    for(size_t i = 0; i < v.size(); ++i) {
        if(done[i]) continue;
        done[i] = true;
        size_t prev = i;
        size_t curr = perm[i];
        while(i != curr) {
            std::swap(v[prev], v[curr]);
            (std::swap(vs[prev], vs[curr]), ...);
            done[curr] = true;
            prev = curr;
            curr = perm[curr];
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.