【问题标题】:Computing truncated mean between two forward indicators计算两个前向指标之间的截断平均值
【发布时间】:2021-06-03 18:52:02
【问题描述】:

我已经通过函数 truncated_mean(std::vector& v, double trimming fraction) 计算了向量的截断平均值。这个函数将向量 v 和我们想要移除以计算平均值的分数作为输入(例如 10%,所以我们移除最高和最低 10% 的值,然后我们计算平均值),我使用标准库创建了它。

例如,v = [0,1,2....,9],则 truncated_mean(v, 0.10) = 4.5。

现在,我想重用相同的函数,但不是将 v 作为输入,而是希望有 2 个前向迭代器,v.begin() 和 v.end()。我得到了 typename forward 的模板,我应该用它来检查它的 value_type(通过 std::iterator_traits 访问)是否符合特定标准。我对这个问题的理解是,首先我需要检查输入是否属于一个向量,然后我应该从那里访问向量本身来计算截断的平均值。

如何调整我的函数以将向量的开头和结尾而不是向量本身作为输入?

【问题讨论】:

  • 你想要truncated_mean<typename T>(const t::iterator& begin, const T:iterator& end, double d) 吗?您的输入是否保证排序?

标签: c++ vector forward


【解决方案1】:

假设传入的序列已排序,您可以简单地使用std::distance 计算长度并在开始和结束处跳过适当数量的元素:

编辑: 将std::accumulate 用于随机访问迭代器的扩展代码;如果允许使用 C++20 功能,请使用概念而不是区分迭代器类型和附加参数。

template<typename RandomAccessIterator>
double truncated_mean_impl(RandomAccessIterator begin, RandomAccessIterator end, double trimming_fraction, std::random_access_iterator_tag)
{
    if (trimming_fraction < 0)
    {
        throw std::range_error("trimming_fraction must not be negative");
    }
    if(trimming_fraction >= 0.5)
    {
        return std::numeric_limits<double>::quiet_NaN(); // no elements left after trimming
    }

    auto const count = std::distance(begin, end);
    auto const skippedElementCountFront = static_cast<decltype(count)>(count * trimming_fraction);
    auto const summandCount = count - 2 * skippedElementCountFront;

    return std::accumulate<RandomAccessIterator, double>(begin + skippedElementCountFront, end - skippedElementCountFront, 0) / summandCount;
}


template<typename ForwardIterator>
double truncated_mean_impl(ForwardIterator begin, ForwardIterator end, double trimming_fraction, std::forward_iterator_tag)
{
    if (trimming_fraction < 0)
    {
        throw std::range_error("trimming_fraction must not be negative");
    }
    if(trimming_fraction >= 0.5)
    {
        return std::numeric_limits<double>::quiet_NaN(); // no elements left after trimming
    }

    auto const count = std::distance(begin, end);

    auto const skippedElementCountFront = static_cast<decltype(count)>(count * trimming_fraction);
    
    // skip elements in the front
    for (auto i = skippedElementCountFront; i != 0; --i, ++begin) {}

    auto const summandCount = count - 2 * skippedElementCountFront;

    double sum = 0;

    for (auto i = summandCount; i != 0; --i, ++begin)
    {
        sum += *begin;
    }

    return sum / summandCount;
}

template<typename ForwardIterator>
double truncated_mean(ForwardIterator begin, ForwardIterator end, double trimming_fraction)
{
    return truncated_mean_impl<ForwardIterator>(begin, end, trimming_fraction, typename std::iterator_traits<ForwardIterator>::iterator_category());
}

int main()
{
    std::vector<int> const values  { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    std::cout << truncated_mean(values.cbegin(), values.cend(), 0.1) << '\n';
}

如果输入序列未排序,并且您不能或不想对输入进行排序,则将元素复制到新向量并将原始算法应用于该向量可能是最好的。

【讨论】:

  • 感谢您的回答!这就是我一直在寻找的理由!您认为可以使用任何显式循环(for)来实现吗?我没有在原始 truncated_mean 中使用任何循环,但那是因为我已经将完整向量作为输入。
  • @Rigtee 您能否详细说明为什么您不考虑代码使用“显式循环”?代码中使用 for 循环在开头跳过不相关的元素,并对相关范围内的元素求和。
  • 您的代码使用了显式循环,即 for 循环来跳过元素并增加总和。实际上,这是我必须做的练习的一部分:使用循环很好,但没有任何明确的循环更好(把它当作一个挑战)。
  • @Rigtee 为随机访问迭代器添加了一个专门的版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-07
  • 1970-01-01
  • 2021-11-21
  • 2017-01-23
  • 2021-01-13
相关资源
最近更新 更多