对每个元素上计算的值排序向量,而不是每个元素多次执行计算

时间:2010-06-30 13:21:37

标签: c++ sorting

任何人都可以推荐一种漂亮而整洁的方法来实现这一目标:

float CalculateGoodness(const Thing& thing);

void SortThings(std::vector<Thing>& things)
{
    // sort 'things' on value returned from CalculateGoodness, without calling CalculateGoodness more than 'things.size()' times
}

显然,我可以将std::sort与调用CalculateGoodness的比较函数一起使用,但随后将Thing调用几次,因为它与其他元素进行比较,这是不好的如果CalculateGoodness很贵。我可以创建另一个std::vector只是为了存储评分和std::sort,并以同样的方式重新排列things,但我看不出这样做的整洁方式。有什么想法吗?

编辑:道歉,我应该说而不修改Thing,否则这是一个相当容易解决的问题:)

5 个答案:

答案 0 :(得分:4)

在排序之前,您可以调用CalculateGoodness来调用每个元素,然后CalculateGoodness只更新内部成员变量。然后,您可以根据该成员变量进行排序。

如果您无法修改类型,另一种可能性是为您的对象及其先前计算的值存储某种std::map。您的排序函数将使用充当缓存的映射。

答案 1 :(得分:4)

我可以想到一个简单的转换(两个)来获得你想要的东西。您可以将std::transform与合适的谓词一起使用。

  • std::vector<Thing>std::vector< std::pair<Result,Thing> >
  • 对第二个向量进行排序(因为一对按第一个成员排序),
  • 逆向转换

Tadaam:)

编辑:尽量减少副本数量

  • std::vector<Thing>std::vector< std::pair<Result,Thing*> >
  • 对第二个向量进行排序
  • 转换回辅助向量(本地)
  • 交换原始和本地向量

这样您只需复制每个Thing一次。值得注意的是,请记住sort执行副本,因此值得使用。

因为我感觉很自负:

typedef std::pair<float, Thing*> cached_type;
typedef std::vector<cached_type> cached_vector;

struct Compute: std::unary_function< Thing, cached_type >
{
  cached_type operator()(Thing& t) const
  {
    return cached_type(CalculateGoodness(t), &t);
  }
};

struct Back: std::unary_function< cached_type, Thing >
{
  Thing operator()(cached_type t) const { return *t.second; }
};


void SortThings(std::vector<Thing>& things)
{
  // Reserve to only allocate once
  cached_vector cache; cache.reserve(things.size());

  // Compute Goodness once and for all
  std::transform(things.begin(), things.end(),
                 std::back_inserter(cache), Compute());

  // Sort
  std::sort(cache.begin(), cache.end());

  // We have references inside `things` so we can't modify it
  // while dereferencing...
  std::vector<Thing> local; local.reserve(things.size());

  // Back transformation
  std::transform(cache.begin(), cache.end(),
                 std::back_inserter(local), Back());

  // Put result in `things`
  swap(things, local);
}

提供通常的警告:在我的头顶上,可能会杀死小猫......

答案 2 :(得分:2)

我赞成Brian's answer,因为它显然最能满足您的需求。但是你应该考虑的另一个解决方案是只是简单地写它。处理器每天都变得越来越强大。使其正确并继续前进。您可以稍后对其进行分析,看看CalculateGoodness是否确实是瓶颈。

答案 3 :(得分:1)

我会创建一对评级和事物,每次调用一次CalculateGoodness,然后对评级进行排序。如果适用,您也可以将其移动到从评级到事物的地图

另一种选择是将Thing本身的CalculateGoodness缓存为一个简单的字段,或者将CalculateGoodness作为Thing的方法(确保缓存是可变的,因此const Things仍然有效)

答案 4 :(得分:1)

执行单独vector事情的一种简洁方法可能是实际创建vector< pair<float, Thing*> >,其中第二个元素指向具有相应Thing值的float对象。如果您按vector值对float进行排序,则可以对其进行迭代并按正确的顺序读取Thing个对象,可能会将它们播放到另一个vector或{{ 1}}所以它们最终按顺序存储。