Подтвердить что ты не робот

Вычислить медиану значений, хранящихся в векторе - С++?

Я участвую в программировании, и для проекта, над которым я работаю, мне нужно вычислить медианное значение вектора значений int. Я должен сделать это, используя только функцию сортировки из функций STL и векторных членов, таких как .begin(), .end() и .size().

Я также должен убедиться, что я нашел медианную информацию о том, имеет ли вектор нечетное число значений или четное число значений.

И я Stuck, ниже я включил свою попытку. Так где я иду не так? Я был бы признателен, если бы вы были готовы дать мне несколько указателей или ресурсов, чтобы идти в правильном направлении.

Код:

int CalcMHWScore(const vector<int>& hWScores)
{
     const int DIVISOR = 2;
     double median;
     sort(hWScores.begin(), hWScores.end());
     if ((hWScores.size() % DIVISOR) == 0)
     {
         median = ((hWScores.begin() + hWScores.size()) + (hWScores.begin() + (hWScores.size() + 1))) / DIVISOR);
     }
     else 
     {
       median = ((hWScores.begin() + hWScores.size()) / DIVISOR)
     }

    return median;
}

Спасибо!!

4b9b3361

Ответ 1

Вы делаете дополнительное деление и в целом делаете это немного сложнее, чем нужно. Кроме того, нет необходимости создавать DIVISOR, когда 2 на самом деле более значимо в контексте.

double CalcMHWScore(vector<int> scores)
{
  size_t size = scores.size();

  if (size == 0)
  {
    return 0;  // Undefined, really.
  }
  else
  {
    sort(scores.begin(), scores.end());
    if (size % 2 == 0)
    {
      return (scores[size / 2 - 1] + scores[size / 2]) / 2;
    }
    else 
    {
      return scores[size / 2];
    }
  }
}

Ответ 2

Нет необходимости полностью сортировать вектор: std::nth_element может сделать достаточно работы, чтобы поместить медиану в правильное положение. См. Мой ответ на этот вопрос для примера.

Конечно, это не поможет, если ваш учитель запрещает использовать правильный инструмент для задания.

Ответ 3

Следующая простая функция, которая вернет медиану набора значений с помощью итераторов ввода. Он не будет изменять исходный набор данных за счет выделения памяти.

// Get the median of an unordered set of numbers of arbitrary 
// type without modifying the underlying dataset.
template <typename It>
auto Median(It begin, It end)
{
    using T = typename std::iterator_traits<It>::value_type;
    std::vector<T> data(begin, end);
    std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end());
    return data[data.size() / 2];
}

Если вы хотите избежать затрат на выделение копии набора данных и готовы изменить базовый набор данных, вы можете использовать это вместо:

// Get the median of an unordered set of numbers of arbitrary 
// type (this will modify the underlying dataset).
template <typename It>
auto Median(It begin, It end)
{
    const auto size = std::distance(begin, end)
    std::nth_element(begin, begin + size / 2, end);
    return *std::next(begin, size / 2);
}

Ответ 4

const int DIVISOR = 2;

Не делай этого. Это просто делает ваш код более запутанным. Вероятно, вы читали инструкции о том, чтобы не использовать магические числа, но четкость и нечетность чисел является фундаментальным свойством, поэтому абстрагирование этого результата не приносит никакой пользы, но затрудняет читаемость.

if ((hWScores.size() % DIVISOR) == 0)
{
    median = ((hWScores.begin() + hWScores.size()) + (hWScores.begin() + (hWScores.size() + 1))) / DIVISOR);

Вы переносите итератор в конец вектора, беря еще один итератор, который указывает один за концом вектора, добавляя вместе итераторы (что не является операцией, которая имеет смысл), а затем деля итог итератор (что также не имеет смысла). Это более сложный случай; Сначала я объясню, что делать для вектора с нечетным размером, и оставьте случай с четным размером в качестве упражнения для вас.

}
else 
{
    median = ((hWScores.begin() + hWScores.size()) / DIVISOR)

Опять же, вы делите итератор. Вместо этого вы хотите увеличить итератор до начала вектора с помощью элементов hWScores.size() / 2:

    median = *(hWScores.begin() + hWScores.size() / 2);

И обратите внимание, что вам нужно разыгрывать итераторы, чтобы получить значения из них. Было бы проще, если бы вы использовали индексы:

    median = hWScores[hWScores.size() / 2];

Ответ 5

Ниже приводится примерная программа, которая несколько похожа на пример в ответе Макс. Чтобы помочь OP продвинуть свои знания и понимание, я внес ряд изменений. У меня есть:

a) изменил вызов с помощью ссылки const на вызов по значению, так как сортировка захочет изменить порядок элементов в вашем векторе, (EDIT: я просто видел, что Роб Кеннеди также сказал об этом, пока я готовил запись)

b) заменил size_t более подходящим вектором <int > :: size_type (фактически, удобным синонимом последнего),

c) сохраненный размер /2 для промежуточной переменной,

d) выбрасывает исключение, если вектор пуст, а

e) Я также ввел условный оператор (?:).

Собственно, все эти исправления прямо из главы 4 "Ускоренного С++" Koenig и Moo.

double median(vector<int> vec)
{
        typedef vector<int>::size_type vec_sz;

        vec_sz size = vec.size();
        if (size == 0)
                throw domain_error("median of an empty vector");

        sort(vec.begin(), vec.end());

        vec_sz mid = size/2;

        return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid];
}

Ответ 6

Я не совсем уверен, что ваши ограничения на пользовательские функции-члены вектора, но доступ к индексу с помощью [] или at() упростит доступ к элементам:

median = hWScores.at(hWScores.size() / 2);

Вы также можете работать с итераторами, такими как begin() + offset, как вы сейчас делаете, но тогда вам нужно сначала вычислить правильное смещение с помощью size()/2 и добавить это к begin(), а не наоборот. Также вам необходимо разыменовать полученный итератор для доступа к фактическому значению в этой точке:

median = *(hWScores.begin() + hWScores.size()/2)

Ответ 7

Принятый ответ использует std::sort который выполняет больше работы, чем нам нужно. Ответы, использующие std::nth_element, не обрабатывают регистр четного размера правильно.


Мы можем сделать немного лучше, чем просто использовать std::sort. Нам не нужно сортировать вектор полностью, чтобы найти медиану. Мы можем использовать std::nth_element чтобы найти средний элемент. Поскольку медиана вектора с четным числом элементов является средним из двух средних, нам нужно проделать еще немного работы, чтобы найти другой средний элемент в этом случае. std::nth_element гарантирует, что все элементы, предшествующие середине, меньше, чем середина. Это не гарантирует их порядок, поэтому нам нужно использовать std::max_element чтобы найти самый большой элемент, предшествующий среднему элементу.

int CalcMHWScore(std::vector<int> hWScores) {
  assert(!hWScores.empty());
  const auto middleItr = hWScores.begin() + hWScores.size() / 2;
  std::nth_element(hWScores.begin(), middleItr, hWScores.end());
  if (hWScores.size() % 2 == 0) {
    const auto leftMiddleItr = std::max_element(hWScores.begin(), middleItr);
    return (*leftMiddleItr + *middleItr) / 2;
  } else {
    return *middleItr;
  }
}

Возможно, вы захотите вернуть значение double поскольку медиана может быть дробной, если вектор имеет четный размер.