एक वेक्टर को अवरोही क्रम में क्रमबद्ध करना


310

क्या मुझे उपयोग करना चाहिए?

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

या

std::sort(numbers.rbegin(), numbers.rend());   // note: reverse iterators

क्रम में एक वेक्टर को क्रमबद्ध करने के लिए? क्या एक दृष्टिकोण या दूसरे के साथ कोई लाभ या कमियां हैं?


2
+1 मुझे लगता है कि उत्तर स्पष्ट है, लेकिन इस प्रश्न में एक छोटा सा ट्रिवियम है। :)
विल्हेमटेल

3
मैं पहले विकल्प के लिए मतदान करूंगा, सिर्फ इसलिए कि मुझे कभी इसका सामना नहीं करना पड़ेगा reverse_iterator
इवंड्रिक्स

2
@wilhelmtell एक noob सवाल, लेकिन दूसरा एक क्रम में क्यों उतरना चाहिए? हम उसी सरणी को इनपुट विधि के रूप में दे रहे हैं। यह सिर्फ इतना है कि हम इसे रिवर्स ऑर्डर में दे रहे हैं, इसलिए इसे अवरोही क्रम में सॉर्ट किया जाना चाहिए और आरोही क्रम में नहीं होना चाहिए जैसा कि ar.begin () और ar.end के साथ मामला होगा।
शशांक

6
@ शशांक std::sort(b, e);न्यूनतम b(हमारे मामले में rbegin, इसलिए अंतिम तत्व) और अधिकतम पर e(हमारे मामले में rend, इसलिए पहला तत्व) डालता है ।
fredoverflow

जवाबों:


114

दरअसल, पहले वाला एक बुरा विचार है। या तो दूसरे का उपयोग करें , या यह:

struct greater
{
    template<class T>
    bool operator()(T const &a, T const &b) const { return a > b; }
};

std::sort(numbers.begin(), numbers.end(), greater());

इस तरह आपका कोड चुपचाप नहीं टूटेगा जब कोई फैसला करेगा कि उसके बजाय numbersपकड़ना चाहिए longया नहीं ।long longint


1
@FredOverflow: आपने अपनी टिप्पणी में सम्मान किया;)
user541686

2
या पहले वाले के साथ रहना। नंबरकंटेनर के लिए एक टाइप्डेफ का उपयोग करें - एक अच्छा विचार ताकि कोई व्यक्ति लंबे समय तक स्वैप कर सके - और लिखें: std :: sort (numbers.begin (), numbers.end (), std :: अधिक से अधिक <numContainer :: value_type> ( ));
रिचर्डहोल्स

1
+1 पहला वास्तव में भ्रामक है। greaterअन्य की तुलना में क्या है ? rbeginऔर rendएक विशिष्ट उद्देश्य के लिए बनाए गए थे।
अभिषेक दिवेकर

6
सिर्फ std::greater<typename decltype(numbers)::value_type>()या कुछ और क्यों नहीं ?
ईनपोकलम

1
यह उत्तर पुराना है - आप std::greater<>()C ++ 14 के बाद से उपयोग कर सकते हैं ।
निकोलाई

70

पहले का उपयोग करें:

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

ग़लत व्याख्या की संभावना उतनी ही कम - यह क्या हो रहा है की स्पष्ट है rbeginके रूप में beginभी एक टिप्पणी के साथ,। यह स्पष्ट और पठनीय है जो वास्तव में आप चाहते हैं।

इसके अलावा, दूसरा पुनरावृत्त चलने वालों की प्रकृति को देखते हुए पहले की तुलना में कम कुशल हो सकता है, हालांकि आपको यह सुनिश्चित करने के लिए प्रोफ़ाइल करना होगा।


68

C ++ 14 के साथ आप यह कर सकते हैं:

std::sort(numbers.begin(), numbers.end(), std::greater<>());

30

इस बारे में क्या?

std::sort(numbers.begin(), numbers.end());
std::reverse(numbers.begin(), numbers.end());

13
अतिरिक्त जटिलता से बचने का एक कारण हो सकता है: O (n * log (n)) + O (n) बनाम O (n * log (n))
greg

32
@ समूह O (n * log (n)) = O (n * log (n) + n) वे एक ही सेट को परिभाषित करने के दो तरीके हैं। आपके कहने का अर्थ है "यह धीमा हो सकता है।"
२०:५० पर pjvandehaar

4
@pjvandehaar ग्रेग ठीक है। उन्होंने स्पष्ट रूप से नहीं कहा, O (n * log (n) + n), उन्होंने O (n * log (n)) + O (n) कहा। आप सही कह रहे हैं कि उनका शब्दांकन अस्पष्ट है (विशेष रूप से शब्द जटिलता का उनका दुरुपयोग), लेकिन आप एक दयालु तरीके से जवाब दे सकते हैं। उदा: हो सकता है कि आपका मतलब 'जटिलता' शब्द के बजाय 'अभिकलन' शब्द का इस्तेमाल करना हो। संख्याओं को उलट देना एक अनावश्यक O (n) चरण है अन्यथा एक समान O (n * log (n)) चरण के लिए।
तोक गिला

3
@OfekGila मेरी समझ यह है कि बिग-ओ नोटेशन कार्यों के सेट के बारे में है, और नोटेशन शामिल है =और +सिर्फ उपयुक्त अर्थ हैं और । उस मामले में, O(n*log(n)) + O(n)एक सुविधाजनक संकेतन है O(n*log(n)) ∪ O(n)जिसके लिए समान है O(n*log(n))। शब्द "अभिकलन" एक अच्छा सुझाव है और आप टोन के बारे में सही हैं।
pjvandehaar

22

मेहरदाद के प्रस्ताव के अनुसार एक फ़ंक्टर के बजाय, आप एक लैम्ब्डा फ़ंक्शन का उपयोग कर सकते हैं।

sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });

16

मेरी मशीन के अनुसार, long longपहली विधि का उपयोग करते हुए [1..3000000] के सदिश को छांटने में लगभग 4 सेकंड लगते हैं, जबकि दूसरे का उपयोग करने में लगभग दो बार समय लगता है। यह स्पष्ट रूप से कुछ कहता है, लेकिन मुझे समझ में नहीं आता कि या तो क्यों। जरा सोचिए यह मददगार होगा।

वही बात यहाँ रिपोर्ट की गई

जैसा कि Xeo द्वारा कहा गया है, -O3वे खत्म होने के लिए उसी समय का उपयोग करते हैं।


12
क्या आप शायद सिर्फ अनुकूलन के साथ संकलित नहीं हुए थे? ऐसा लगता है कि reverse_iteratorऑपरेशन बहुत अधिक नहीं थे, और यह देखते हुए कि वे वास्तविक पुनरावृत्तियों के चारों ओर एक रैपर हैं, यह कोई आश्चर्य नहीं है कि वे बिना इनलाइन किए दोगुना समय लेते हैं।
Xeo

@ Xeo भले ही वे कुछ अंकीयकरण का उपयोग कर रहे थे, प्रति अनुमापन के अतिरिक्त जोड़ का उपयोग करते हैं।
पबबी

@ गिल्डरन: क्योंकि यह ऐसा है? base()उदाहरण के रिटर्न के लिए सदस्य समारोह इटरेटर लपेटा।
Xeo

1
@ Xeo अब वे दोनों एक सेकंड में खत्म हो गए। धन्यवाद!
zw324

3
@ Xeo: मैं इसे वापस लेता हूं; मानक वास्तव में अनिवार्य है जो के std::vector<>::reverse_iteratorसंदर्भ में लागू किया गया है std::reverse_iterator<>। विचित्र; आज मैंने सीखा। :-P
ildjarn

11

पहला दृष्टिकोण संदर्भित करता है:

    std::sort(numbers.begin(), numbers.end(), std::greater<>());

दूसरे की तुलना में अधिक दक्षता प्राप्त करने के कारण आप पहले दृष्टिकोण का उपयोग कर सकते हैं।
पहले दृष्टिकोण का समय जटिलता दूसरे से कम है।


यह उसी तरह का उत्तर है जैसे कि mrexciting का। जटिलता के बारे में टिप्पणी भी मेरे लिए स्पष्ट नहीं है।
फिलिप क्लेमेन

7
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);

4
एक वैध जवाब होने के लिए, आपको अपने ओपी के उल्लेख के तरीकों के फायदे / कमियों के बारे में कुछ लिखने पर विचार करना चाहिए
स्टेफन हेगनी

3

टी एल; डॉ

किसी भी उपयोग करें। वे लगभग समान हैं।

बोरिंग जवाब

हमेशा की तरह, पेशेवरों और विपक्ष हैं।

उपयोग करें std::reverse_iterator:

  • जब आप कस्टम प्रकारों को छांट रहे हैं और आप लागू नहीं करना चाहते हैं operator>()
  • जब आप टाइप करने के लिए बहुत आलसी होते हैं std::greater<int>()

std::greaterजब उपयोग करें :

  • जब आप अधिक स्पष्ट कोड रखना चाहते हैं
  • जब आप अस्पष्ट रिवर्स पुनरावृत्तियों का उपयोग करने से बचना चाहते हैं

प्रदर्शन के लिए, दोनों विधियां समान रूप से कुशल हैं। मैंने निम्नलिखित बेंचमार्क की कोशिश की:

#include <algorithm>
#include <chrono>
#include <iostream>
#include <fstream>
#include <vector>

using namespace std::chrono;

/* 64 Megabytes. */
#define VECTOR_SIZE (((1 << 20) * 64) / sizeof(int))
/* Number of elements to sort. */
#define SORT_SIZE 100000

int main(int argc, char **argv) {
    std::vector<int> vec;
    vec.resize(VECTOR_SIZE);

    /* We generate more data here, so the first SORT_SIZE elements are evicted
       from the cache. */
    std::ifstream urandom("/dev/urandom", std::ios::in | std::ifstream::binary);
    urandom.read((char*)vec.data(), vec.size() * sizeof(int));
    urandom.close();

    auto start = steady_clock::now();
#if USE_REVERSE_ITER
    auto it_rbegin = vec.rend() - SORT_SIZE;
    std::sort(it_rbegin, vec.rend());
#else
    auto it_end = vec.begin() + SORT_SIZE;
    std::sort(vec.begin(), it_end, std::greater<int>());
#endif
    auto stop = steady_clock::now();

    std::cout << "Sorting time: "
          << duration_cast<microseconds>(stop - start).count()
          << "us" << std::endl;
    return 0;
}

इस कमांड लाइन के साथ:

g++ -g -DUSE_REVERSE_ITER=0 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out
g++ -g -DUSE_REVERSE_ITER=1 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out

std::greater demo std::reverse_iterator demo

समय समान हैं। Valgrind ने उसी संख्या में कैश मिस होने की रिपोर्ट की।


2

मुझे नहीं लगता कि आपको प्रश्न में उन दोनों विधियों का उपयोग करना चाहिए, क्योंकि वे दोनों भ्रामक हैं, और दूसरा मेहरदाद सुझाव के अनुसार नाजुक है।

मैं निम्नलिखित की वकालत करूंगा, क्योंकि यह एक मानक पुस्तकालय समारोह की तरह दिखता है और इसका उद्देश्य स्पष्ट करता है:

#include <iterator>

template <class RandomIt>
void reverse_sort(RandomIt first, RandomIt last)
{
    std::sort(first, last, 
        std::greater<typename std::iterator_traits<RandomIt>::value_type>());
}

2
यह सिर्फ std::greaterतुलनित्र का उपयोग करने की तुलना में एक हजार गुना अधिक भ्रमित करने वाला है ....
Apollys

@Apollys मैं सहमत हूं कि C ++ 14, std :: ज़्यादा <> से शुरू करना पसंद किया गया समाधान है। यदि आपके पास C ++ 14 नहीं है, तो यह तब भी उपयोगी हो सकता है यदि आप std :: more <int> के साथ किसी भी आश्चर्य को नियंत्रित करना चाहते हैं (उदाहरण के लिए, कुछ बिंदु पर प्रकार जब int से लंबे समय तक बदलते हैं)।
फिलिप क्लेयन

2

आप या तो पहले एक का उपयोग कर सकते हैं या नीचे दिए गए कोड की कोशिश कर सकते हैं जो समान रूप से कुशल है

sort(&a[0], &a[n], greater<int>());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.