Cách tìm giá trị trung bình của `std :: set` [trùng lặp]
Tôi đang cố gắng tìm trung bình của a std::set. Vì std::setđã sắp xếp mọi thứ, tôi chỉ cần chọn phần tử ở giữa. Ý tưởng của tôi là tiến tới một nửa:, std::advance(e, rtspUrls.size() / 2);nhưng tôi không chắc nó sẽ hoạt động như thế nào. Còn những con số như thế 1.5nào? Nó sẽ tiến đến một cái gì đó?
Tôi đang sử dụng try catch để cố gắng không tiến vào một thứ gì đó không xác định. Cái này có an toàn không?
Dựa theo http://www.cplusplus.com/reference/algorithm/min_element/?kw=min_element, std::advanceném nếu trình lặp ném. Tôi không chắc liệu trình lặp cho std :: set ném khi chúng tôi thử ++nó (https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator không nói gì).
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;
}
Trả lời
Với std :: set, bạn bị giới hạn trong việc sử dụng trình vòng lặp để lặp đến phần tử giữa (trong trường hợp số mục nhập lẻ trong tập hợp của bạn) hoặc lặp đến giữa 1 và giữa và lấy giá trị trung bình (trong trường hợp là số chẵn trong số các mục) để xác định giá trị trung bình.
Một vòng lặp đơn giản và một bộ đếm càng về phía trước càng tốt. Một ví dụ ngắn sẽ là:
#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;
}
}
}
(tất nhiên bạn sẽ phải điều chỉnh các loại để đáp ứng dữ liệu của mình)
Ví dụ Sử dụng / Đầu ra
Biên dịch với ODDxác định:
$ ./bin/set_median
c 3
trung vị 3
Được biên soạn mà không có định nghĩa bổ sung cho trường hợp EVEN:
$ ./bin/set_median
c 3
d 4
median 3.5
std :: tiếp theo
Bạn có thể sử dụng std :: next để chuyển tới trình lặp thứ n sau dòng hiện tại. Bạn phải gán kết quả:
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 :: tạm ứng
std :: Advance tiến trình vòng lặp được cung cấp dưới dạng tham số cho trình vòng lặp thứ n sau trình lặp hiện tại:
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';
(đầu ra cho mediangiống như với vòng lặp ở trên)
Hãy xem xét mọi thứ và cho tôi biết nếu bạn có thêm câu hỏi.
Tôi chỉ cần chọn phần tử ở giữa. Ý tưởng của tôi là tiến tới một nửa:,
std::advance(e, rtspUrls.size() / 2);nhưng tôi không chắc nó sẽ hoạt động như thế nào. Còn những số như 1.5 thì sao? Nó sẽ tiến đến một cái gì đó?
std::setchỉ mục sử dụng các giá trị số nguyên không dấu ( size_t) vì vậy double 1.5sẽ được chuyển đổi thành size_t 1.
Tôi không chắc liệu trình lặp để
std::setném khi chúng tôi cố gắng++
Không nó sẽ không, nhưng tiến xa hơn end()là không xác định.
Giá trị trung bình thực sự cho một tập hợp có số lượng phần tử chẵn sẽ lấy giá trị trung bình của hai phần tử ở giữa - nhưng điều đó yêu cầu loại bạn lưu trữ trong std::setcả hai hỗ trợ +và /. Thí dụ:
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
Trong trường hợp của bạn, kiểu trong tập hợp không giống như nó hỗ trợ +và /để tạo trung bình hai RTSPUrls trong trường hợp bạn có số phần tử chẵn, vì vậy bạn có thể chỉ nên chọn một trong hai phần tử ở giữa trong trường hợp bạn có một số tiền chẵn. Hoặc bằng cách trả về một trình lặp (để người dùng sau đó có thể kiểm tra xem nó có hay không rtspUrls.end()):
return std::next(rtspUrls.begin(), rtspUrls.size() / 2);
Hoặc bằng cách trả về một tham chiếu đến hoặc bản sao của phần tử:
if(rtspUrls.empty()) throw std::runtime_error("no elements in set");
return *std::next(rtspUrls.begin(), rtspUrls.size() / 2);
Tôi chỉ phải chọn phần tử ở giữa
Chỉ khi tập hợp chứa một số phần tử lẻ. Ngược lại, khi kích thước bằng nhau, trung vị được xác định là giá trị trung bình của hai giá trị giữa, đôi khi được gọi là trung vị trên và trung vị dưới.
Còn những số như 1.5 thì sao?
Bạn sẽ không bao giờ hiểu được điều đó vì rtspUrls.size() / 2là phép chia số nguyên cắt ngắn bất kỳ chữ số thập phân nào.
Tôi nghĩ, việc truyền một floathoặc doubledưới dạng tham số thứ hai, như std::advance(e, 1.5)không nên biên dịch. Theo như tôi có thể thấy tài liệu tham khảo không chỉ định loại tham số thứ hai. Tuy nhiên, phần "có thể triển khai" luôn sử dụng kiểu khác biệt cụ thể cho tham số đầu tiên, thường là kiểu tích phân và có vẻ hợp lý.
Tôi đang sử dụng try catch để cố gắng không tiến vào một thứ gì đó không xác định. Cái này có an toàn không?
Không, bỏ tham chiếu hoặc gia tăng một trình lặp không hợp lệ là hành vi không xác định và không bắt buộc phải đưa ra bất kỳ ngoại lệ nào. Mặc dù nhiều cách triển khai cung cấp khả năng kiểm tra lỗi rộng rãi trong các bản dựng gỡ lỗi và rất tốt để ném UB ngoại lệ xảy ra. Nhưng việc tăng lên cho đến khi một nửa kích thước bộ sẽ không trở thành vấn đề.