计算STL映射不相交子区域平均值的有效方法 < >我将算法从C→C++转换成C++。算法的一小部分是计算字典中某些区域的平均值

计算STL映射不相交子区域平均值的有效方法 < >我将算法从C→C++转换成C++。算法的一小部分是计算字典中某些区域的平均值,c++,stl,map,average,C++,Stl,Map,Average,字典中的数据按以下方式存储: Index Value 1 10 3 28 290 78 1110 90 if (dictionary.Where(x => x.Key < areaWidth).Count() > 0) { avgValue = (int) dictionary.Where(x => x.Key < areaWidth).Average( x => x.Va

字典中的数据按以下方式存储:

Index     Value
1         10
3         28
290       78
1110      90
if (dictionary.Where(x => x.Key < areaWidth).Count() > 0)
{
    avgValue = (int) dictionary.Where(x => x.Key < areaWidth).Average(
        x => x.Value);
}

for (var i = 0; i < line.Length; i++)
{
    if (i == areaWidth)
    {
        avgValue = -1;
        i = line.Length - areaWidth;
        var rightBorder = i - areaWidth;

        if (dictionary.Where(x => x.Key > (rightBorder)).Count() > 0)
        {
            avgValue = (int) dictionary.Where(
                x => x.Key > (rightBorder)).Average(
                                x => x.Value);
        }
    }

    if (line[i] < avgValue * 0.8)
    {
        reallyImportantValue += (avgValue - line[i]);
    }
}
我需要计算索引小于某个数字的所有值和大于某个数字的所有索引值的平均值。在C#中,我采用以下方式:

Index     Value
1         10
3         28
290       78
1110      90
if (dictionary.Where(x => x.Key < areaWidth).Count() > 0)
{
    avgValue = (int) dictionary.Where(x => x.Key < areaWidth).Average(
        x => x.Value);
}

for (var i = 0; i < line.Length; i++)
{
    if (i == areaWidth)
    {
        avgValue = -1;
        i = line.Length - areaWidth;
        var rightBorder = i - areaWidth;

        if (dictionary.Where(x => x.Key > (rightBorder)).Count() > 0)
        {
            avgValue = (int) dictionary.Where(
                x => x.Key > (rightBorder)).Average(
                                x => x.Value);
        }
    }

    if (line[i] < avgValue * 0.8)
    {
        reallyImportantValue += (avgValue - line[i]);
    }
}
if(dictionary.Where(x=>x.Key0)
{
avgValue=(int)字典。其中(x=>x.Keyx.值);
}
对于(变量i=0;ix.Key>(rightBorder)).Count()>0)
{
avgValue=(int)字典。其中(
x=>x.键>(右边框))。平均值(
x=>x.值);
}
}
if(第[i]行<平均值*0.8)
{
reallyImportantValue+=(avgValue-行[i]);
}
}

我知道这不是非常有效和相当蹩脚的代码,但是我知道无论如何我都必须完全改写C++中的算法部分,所以我决定快速而脏地实现它。p> 不管怎样,我现在把它移植到C++上,因为它将在移动平台上运行,性能非常重要。凭借我有限的C++/STL知识,我很可能完成这项工作,但结果可能比C#代码糟糕得多

所以如果你知道一个很好的方法来完成C++的任务,请告诉我。
编辑:谢谢你的回答。正如我在帖子中提到的,我的STL知识有限,所以我很难选择解决方案,特别是因为有很多不同的观点。如果有人能通过比较这里发布的解决方案来帮助我做出决定,那就太好了。要向您提供更多的背景信息,请执行以下操作:


该函数将被调用大约500次,映射中有1000个值。最重要的方面是稳定性,性能是第二个最重要的方面。

您可以使用
std::acculate
计算值的总和,然后除以元素数。以下是一些如何使用STL计算平均值和其他统计数据的方法。

std::map中的键值对按键排序-即使使用for循环,也很容易将小于或大于某个值的键值相加(如果您不想使用或学习使用STL算法)。对于低于某些
值的键

std::map<int, int> map;
map[...] = ...;

int count = 0, sum = 0;
for (std::map<int, int>::const_iterator it = map.begin();
     it != map.end() && it->first < value; ++it, ++count)
{
    sum += it->second;
}
// check for count == 0
int avg = sum / count; // do note integer division, change if appropriate
大致:

  • map::上限
    /
    下限
    获取索引范围的迭代器
  • 累计
    计算范围内的总和(简单),并
    计数
    获取元素

在该范围内运行两次(缩放不好)。对于优化:

 struct RunningAverage
 {
     double sum;
     int count;
     RunningAverage() { sum = 0; count = 0; }
     RunningAverage & operator+=(double value) 
     { sum += value; ++count; }

     RunningAverage operator+(double value) 
     { RunningAverage result = *this; result += value; return result; }

     double Avg() { return sum / count; } 
 }
您可以通过它进行累积,在一次过程中收集计数和总和


[编辑]根据评论,以下是优化的基本原理:

 struct RunningAverage
 {
     double sum;
     int count;
     RunningAverage() { sum = 0; count = 0; }
     RunningAverage & operator+=(double value) 
     { sum += value; ++count; }

     RunningAverage operator+(double value) 
     { RunningAverage result = *this; result += value; return result; }

     double Avg() { return sum / count; } 
 }
  • 一个对N没有限制的O(N)算法
  • 基本操作(节点遍历和添加)
  • 随机存取模式是可能的
在这些情况下,不再保证内存访问是缓存备份的,因此与每元素操作相比,成本可能会变得非常重要(甚至超过这一点)。迭代两次将使内存访问成本加倍

本讨论中的“变量”仅取决于数据集和客户端计算机配置,而不是算法

我更喜欢这个解决方案,而不是自定义的“累积”,因为它很容易扩展或修改其他操作,而“累积”细节仍然是孤立的。它还可以与一个假设的并行访问的
accumulate\p
方法一起使用(您也需要
struct+struct
操作符,但这很简单)

哦,常量正确性留给读者作为练习:)

  • 您使用std::lower_bound和std::upper_bound查找范围,不同之处在于,lower_bound包含您的值,因此将给出第一个迭代器>=您的值,而upper_bound将给出第一个迭代器>您的值。如果您的值不在映射中,它们将返回相同的迭代器

  • 您可以使用accumulate,但不能只将std::pairs添加在一起,这样您就需要在这里使用自定义functor,或者使用boost::transform\u迭代器,或者在找到边界后只进行循环。循环并不像一些人所说的那样邪恶(而累积实际上是最可怕的算法之一)


如果谓词是映射的比较函数,最好使用
std::map::lower\u-bound()
std::map::upper\u-bound()
。获取指向相关边界的迭代器,并将其与
中的
std::acculate()
一起使用。因为您使用的是关联容器,所以在计算平均值时需要进行调整,以便使用
second
值,而不是
std::pair

如果谓词可能更改为其他内容,则可以使用
std::partition()

//tmp容器:应该使用std::distance()快速
typedef std::向量序列;
seq tmp(dict.size());
迭代器结束(std::partition(dict.begin(),dict.end(),
tmp.begin(),
std::bind2nd(std::tmp(),上界));
//std::vector与std::distance()配合使用效果很好
seq::difference_type new_count=std::distance(tmp.begin(),end);
双下限平均值=标准::累加(tmp.begin(),end,0.0)/新计数;
seq::difference_type new_count=std::distance(end,tmp.end());
双高平均=标准::累积(tmp.begin(),end,0.0)
#include <map>
#include <algorithm>
#include <numeric>
#include <vector>

using namespace std;

typedef map<unsigned int, unsigned int> Values;

int main()
{
    Values values;

    values[1] = 10;
    values[3] = 28;
    values[290] = 78;
    values[1110] = 110;

    size_t boundary(100);
    Values::iterator iter = values.upper_bound(boundary);

    vector<int> lowerRange(values.size(), -1);

    transform(values.begin(), iter, lowerRange.begin(), 
        [](std::pair<unsigned int, unsigned int> p) 
                -> int { return p.second; });

    vector<int>::iterator invalid(find(lowerRange.begin(), 
        lowerRange.end(), -1));
    size_t lowerCount(distance(lowerRange.begin(), invalid));
    lowerRange.resize(lowerCount);

    vector<int> upperRange(values.size() - lowerCount);
    transform(iter, values.end(), upperRange.begin(), 
        [](std::pair<unsigned int, unsigned int> p) 
                -> int { return p.second; });

    size_t lowerAverage = accumulate(lowerRange.begin(), 
        lowerRange.end(), 0) / lowerRange.size();
    size_t upperAverage = accumulate(upperRange.begin(), 
        upperRange.end(), 0) / upperRange.size();

    return 0;
};
int key = <whatever>;

std::map<int, int>::const_iterator it = map.begin(), end = map.end();

size_t num1 = 0;
long total1 = 0;

while (it != end && it->first < key) {
    total1 += it->second;
    ++num1;
    ++it;
}

size_t num2 = map.size() - num1;
long total2 = 0;

while (it != end) {
    total2 += it->second;
    ++it;
}

int avg_less = num1 > 0 ? total1 / num1 : 0;
int avg_greater_equal = num2 > 0 ? total2 / num2 : 0;
class StatsCollector
{
public:
   StatsCollector();

   void add(double val);

 // some stats you might want
   size_t count() const;
   double mean() const;
   double variance() const;
   double skewness() const;
   double kurtosis() const;
};
struct AddPairToStats
{
  template< typename T >
  StatsCollector * operator()( StatsCollector * stats, const T& value_type ) const
  { 
     stats->add( value_type.second );
     return stats;
  }
};
StatsCollector stats;
std::accumuluate( iterStart, iterEnd, &stats, AddPairToStats() );