如果方法是常量,如何找到向量的中位数?

how to find the median of a vector if the method is const?

本文关键字:向量 中位数 何找 方法 常量 如果      更新时间:2023-10-16

>我创建了一个名为 Collect 的方法,该方法将一堆值添加到向量(如下所示)

void Median::Collect(double datum)
{
myVector.push_back(datum);
}

我需要创建一个方法来计算我在上述方法的向量中收集的所有值的中位数。函数定义写在下面

/* Calculates the median of the data (datum) from the Collect method.
*/
double Median::Calculate() const
{
}

所以我知道我首先需要对向量进行排序才能找到中位数。以下是我的尝试:

double Median::Calculate() const
{
std::sort(myVector.begin(), myVector.end());
double median;
if (myVector.size() % 2 == 0)
{// even
median = (myVector[myVector.size() / 2 - 1] + myVector[myVector.size() / 2]) / 2;
}
else
{// odd
median = myVector[myVector.size() / 2];
}
return median;
}

但我意识到这不是编译,因为该方法是常量,因此对向量的值进行排序会改变向量,这在 const 函数中是不允许的。那么我应该为这种方法做什么呢?

复制myVector,对其进行排序,然后计算其中位数。

我们可以做得比仅仅使用std::sort更好一点。我们不需要为了找到中位数而对向量进行完全排序。我们可以使用std::nth_element来找到中间元素。由于具有偶数个元素的向量的中位数是中间两个元素的平均值,在这种情况下,我们需要做更多的工作来找到另一个中间元素。std::nth_element确保中间之前的所有元素都小于中间元素。它不能保证它们的顺序超出此范围,因此我们需要使用std::max_element来查找中间元素之前的最大元素。

您可能没有考虑的另一件事是myVector为空的情况。找到空向量的中位数实际上没有任何意义。对于此示例,我只是使用了assert但您可能希望抛出异常或其他内容。

double Median::calculate() const {
assert(!myVector.empty());
std::vector<double> myVectorCopy = myVector;
const auto middleItr = myVectorCopy.begin() + myVectorCopy.size() / 2;
std::nth_element(myVectorCopy.begin(), middleItr, myVectorCopy.end());
if (myVectorCopy.size() % 2 == 0) {
const auto leftMiddleItr = std::max_element(myVectorCopy.begin(), middleItr);
return (*leftMiddleItr + *middleItr) / 2.0;
} else {
return *middleItr;
}
}

另一种选择是使用不同的容器来确保元素始终排序。您可以考虑使用std::set。当你插入到std::set时,集合保持排序,所以不必使用std::sortstd::nth_elementstd::max_element来查找中位数。你会得到中间元素。

const方法是一种只能在它所属类的const实例上调用的方法。 因此,如果您已经声明了一个类Median并且在其上声明了一个const方法,则只能使用Median类的const实例调用它。不可能影响不同的阶级,就像std::vector一样。

无论如何,如果您决定从std::vector派生一个新类并考虑向其添加median计算中位数的方法,则最好将其声明为const。这样做的原因是你不需要修改数组来获得它的中位数(见下文)。

如果您需要对数组进行排序,则可以复制,或者更好的是,考虑使用指向数组元素的指针数组,然后根据指向的值对该数组进行排序,然后考虑该数组的中心元素。 这样,您就不会接触原始实例,仍然可以维护该方法的const属性。

您可以将myVector声明为mutable。这将允许数据在其中更改,即使您处于const函数中也是如此。

如果由于某种原因,这不是一个选项,您可以考虑使用某种数据类型来保持数据排序并将新数据插入正确的位置。然后,您不需要在每次运行此功能时对其进行排序,但会减慢插入速度。考虑一下将更频繁地发生什么:插入或获取中位数。


更难的方法是两全其美。您的向量将保持不变,并且相同函数的第二次运行实际上会比第一次更快地返回答案。

然后,您可以执行以下操作:

// Median.hpp
class Median
{
std::vector<double> myVector;
mutable double median;
mutable bool medianCalculated;
// the rest is the same
};
// Median.cpp
double Median::calculate() const
{
if(!medianCalculated)
{
std::vector<double> copyVector = myVector;
std::sort(copyVector.begin(), copyVector.end();
const auto m1 = copyVector.begin() + (copyVector.size() / 2);
const auto m2 = copyVector.begin() + ((copyVector.size() + 1) / 2);
median = (*m1 + m2) / 2; // m1==m2 for even sized vector m1+1==m2 for odd sized
medianCalculated=true;
}
return median;  
}
void Median::Collect(double datum)
{
myVector.push_back(datum);
medianCalculated=false;
}