计算存储在Vector - C ++中的值的中值?

时间:2010-01-22 03:45:14

标签: c++ vector median

我是一名编程学生,对于我正在研究的项目,我要做的事情是计算int值向量的中值。我只使用STL中的sort函数和.begin().end().size()等矢量成员函数来执行此操作。

我也应该确保找到矢量具有奇数个值或偶数个值的中位数。

坚持,下面我已经包含了我的尝试。那我哪里错了?如果您愿意给我一些指导或资源以便朝着正确的方向前进,我将不胜感激。

代码:

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;
}

谢谢!

7 个答案:

答案 0 :(得分:56)

无需对矢量进行完全排序:std::nth_element可以做足够的工作来将中位数放在正确的位置。有关示例,请参阅我对this question的回答。

当然,如果您的老师禁止使用正确的工具来完成这项任务,那将无济于事。

答案 1 :(得分:31)

你正在做一个额外的分工,整体上使它比它需要的要复杂一点。此外,当2在上下文中实际上更有意义时,不需要创建DIVISOR。

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 :(得分:10)

以下是一个简单的函数,它将使用输入迭代器返回一组值的中值。它不会以分配内存为代价修改原始数据集。

// 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);
}

答案 3 :(得分: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];

答案 4 :(得分:4)

我在下面给出一个示例程序,该程序与Max S.的响应中的程序有些类似。为了帮助OP提升他的知识和理解,我做了一些改变。我有:

a)通过const引用将调用更改为按值调用,因为sort将要更改向量中元素的顺序,(编辑:我刚刚看到Rob Kennedy在我准备我的时候也说过这个交)

b)将size_t替换为更合适的向量<int&gt; :: size_type(实际上是后者的方便同义词),

c)将size / 2保存到中间变量

d)如果向量为空,则抛出异常,

e)我还介绍了条件运算符(?:)。

实际上,所有这些更正都直接来自Koenig和Moo的“Accelerated C ++”第4章。

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];
}

答案 5 :(得分:0)

我不确定你对vector的成员函数的用户有什么限制,但使用[]at()的索引访问会使访问元素变得更简单:

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

您也可以使用begin() + offset之类的迭代器,就像您目前正在做的那样,但是您需要先使用size()/2计算正确的偏移量,然后将其添加到begin(),而不是其他四处走走。此外,您需要取消引用生成的迭代器以访问该点的实际值:

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

答案 6 :(得分:0)

被接受的答案使用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,因为当向量具有偶数大小时,中位数可能只是分数。