"Std :: set" medyanı nasıl bulunur [kopya]

Sep 14 2020

A'nın medyanını bulmaya çalışıyorum std::set. Yana std::setzaten herşeyi sıralar, sadece orta eleman almak için. Benim fikrim yarıya ilerlemek: std::advance(e, rtspUrls.size() / 2);ama nasıl davranacağından emin değilim. Ya gibi sayılar 1.5? Bir şeye ilerleyecek mi?

Tanımlanmamış bir şeye ilerlememek için bir deneme yakalama kullanıyorum. Bu güvenli mi?

Göre http://www.cplusplus.com/reference/algorithm/min_element/?kw=min_element, std::advanceyineleyici atarsa ​​atar. Std :: set için yineleyicinin onu denediğimizde atıp atmadığından emin değilim ++(https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator hiçbir şey söylemiyor).

std::set<RTSPUrl, decltype(compare_rtsp_url)*> rtspUrls(compare_rtsp_url);
std::set<RTSPUrl, decltype(compare_rtsp_url)*>::iterator e = rtspUrls.begin();
for (const RTSPUrl &rtspUrl : stream.rtsp_urls())
{
    if (rtspUrl.has_resolution())
    {
        rtspUrls.push_back(rtspUrl);
    }
}
try
{
    std::advance(e, rtspUrls.size() / 2);
    return *e;
}
catch (std::exception &e)
{
    return std::nullopt;
}

Yanıtlar

3 DavidC.Rankin Sep 14 2020 at 06:30

İle std :: seti size (sizin sette girişlerinin tek sayıda durumunda) orta elemana yineleme iteratörler kullanırken veya 1-orta ve orta için yineleme ve ortalama alma ile sınırlıdır (bir çift sayıda davayı int ortancayı belirlemek için giriş sayısı).

Basit bir döngü ve bir sayaç, olabildiğince basittir. Kısa bir örnek şöyle olabilir:

#include <iostream>
#include <set>

int main (void) {
    
#ifdef ODD
    std::set<std::pair<char,int>> s {{'a',1}, {'b',2}, {'c',3}, {'d',4}, {'e',5}};
#else
    std::set<std::pair<char,int>> s {{'a',1}, {'b',2}, {'c',3}, {'d',4}, {'e',5}, {'f',6}};
#endif
    double median = 0.;
    size_t n = 0;
    
    for (auto iter = s.begin(); iter != s.end(); iter++, n++) {
        if (n == s.size() / 2 - 1 && s.size() % 2 == 0) {
            median += iter->second;
            std::cout << iter->first << "  " << iter->second << '\n';
        }
        if (n == s.size() / 2) {
            median += iter->second;
            if (s.size() % 2 == 0)
                median /= 2.;
            std::cout << iter->first << "  " << iter->second
                    << "\n\nmedian " << median << '\n';
            break;
        }
    }
}

(tabii ki türlerini verilerinize uyacak şekilde ayarlamanız gerekecek)

Örnek Kullanım / Çıktı

ODDTanımlanmış olarak derlendi :

$ ./bin/set_median
c  3

medyan 3

EVEN vakası için ek tanım olmadan derlenmiştir:

$ ./bin/set_median
c  3
d  4

median 3.5

std :: sonraki

Akımdan sonra n'inci yineleyiciye ilerlemek için std :: next'i kullanabilirsiniz . Sonucu atamalısınız:

    median = 0.;
    auto iter = s.begin();
    
    if (s.size() % 2 == 0) {
        iter = std::next(iter, s.size() / 2 - 1);
        median += iter->second;
        iter = std::next(iter);
        median += iter->second;
        median /= 2.;
    }
    else {
        iter = std::next(iter, s.size() / 2);
        median += iter->second;
    }
    std::cout << "\nmedian " << median << '\n';

std :: advance

std :: advance , parametre olarak sağlanan yineleyiciyi akımdan sonra n'inci yineleyiciye ilerletir :

    median = 0.;
    iter = s.begin();
    if (s.size() % 2 == 0) {
        std::advance(iter, s.size() / 2 - 1);
        median += iter->second;
        std::advance(iter, 1);
        median += iter->second;
        median /= 2.;
    }
    else {
        std::advance(iter, s.size() / 2);
        median += iter->second;
    }
    std::cout << "\nmedian " << median << '\n';

(için çıktı median, yukarıdaki döngü ile aynıdır)

Her şeyi gözden geçirin ve başka sorunuz olursa bana bildirin.

4 TedLyngmo Sep 14 2020 at 07:40

Sadece orta öğeyi seçmem gerekiyor. Benim fikrim yarıya ilerlemek: std::advance(e, rtspUrls.size() / 2);ama nasıl davranacağından emin değilim. 1.5 gibi sayılar ne olacak? Bir şeye ilerleyecek mi?

std::setindisler işaretsiz tamsayı değerleri ( size_t) kullanır, bu yüzden double 1.5dönüştürülecektir size_t 1.

std::setDenediğimizde atışlar için yineleyici olup olmadığından emin değilim++

Hayır olmayacak, ama ötesine geçmek end()tanımsız.

Elemanların eşit miktarda kümesi için gerçek bir ortanca iki orta elemanların ortalama alacaktı - ama bu sizin depoladığınız bu tür gerektirir std::sethem desteklerle +ve /. Misal:

std::set<double> foo{1., 2., 3., 10.};

if(foo.empty()) throw std::runtime_error("no elements in set");

double median;

if(foo.size() % 2 == 0) {                 // even number of elements 
    auto lo = std::next(foo.begin(), foo.size() / 2 - 1);
    auto hi = std::next(lo);
    median = (*lo + *hi) / 2.;
} else {                                  // odd number of elements
    median = *std::next(foo.begin(), foo.size() / 2);
}

std::cout << median << '\n'; // prints 2.5

Sizin durumunuzda, kümedeki tür destekleyici gibi görünmüyor +ve çift ​​sayıda öğeniz olması durumunda /ortalama iki RTSPUrls oluşturmak için , bu nedenle muhtemelen iki orta öğeden birini seçmelisiniz. eşit bir miktar. Ya bir yineleyici döndürerek (böylece kullanıcı daha sonra olup olmadığını kontrol edebilir rtspUrls.end()):

return std::next(rtspUrls.begin(), rtspUrls.size() / 2);

Veya öğeye bir referans veya bir kopyasını döndürerek:

if(rtspUrls.empty()) throw std::runtime_error("no elements in set");
return *std::next(rtspUrls.begin(), rtspUrls.size() / 2);
1 churill Sep 14 2020 at 05:55

Sadece orta öğeyi seçmeliyim

Yalnızca set tek sayıda öğe içerdiğinde. Aksi takdirde, boyut çift olduğunda medyan, bazen üst ve alt medyan olarak adlandırılan iki orta değerin ortalaması olarak tanımlanır.

1.5 gibi sayılar ne olacak?

rtspUrls.size() / 2Herhangi bir ondalık basamağı kesen bir tamsayı bölümü olduğu için bunu asla elde edemezsiniz.

Sanırım, bir floatveya doubleikinci parametre olarak geçmek std::advance(e, 1.5)derlenmemeli. Görebildiğim kadarıyla referans , ikinci parametrenin türünü belirtmiyor. Bununla birlikte, "olası uygulamalar" bölümü her zaman ilk parametreye özgü fark türünü kullanır, bu genellikle bir integral türüdür ve makul görünür.

Tanımlanmamış bir şeye ilerlememek için bir deneme yakalama kullanıyorum. Bu güvenli mi?

Hayır, geçersiz bir yineleyicinin referansının kaldırılması veya artırılması tanımsız bir davranıştır ve herhangi bir istisna atmak zorunda değildir. Her ne kadar birçok uygulama, hata ayıklama yapılarında kapsamlı hata denetimi sağlasa ve bir istisna UB oluşması çok güzel. Ancak set boyutunun yarısına kadar ilerlemek sorun olmayacak.