【问题标题】:Create ranking for vector of double为双精度向量创建排名
【发布时间】:2015-06-13 19:32:33
【问题描述】:

我有一个带有双精度的向量,我想对其进行排名(实际上它是一个向量,其中的对象具有一个名为costs 的双精度成员)。如果只有唯一值或者我忽略非唯一值,那么就没有问题。但是,我想对非唯一值使用平均排名。此外,我在 SO 上发现了一些关于排名的问题,但它们忽略了非唯一值。

例如,假设我们有 (1, 5, 4, 5, 5),那么对应的排名应该是 (1, 4, 2, 4, 4)。当我们忽略非唯一值时,排名为 (1, 3, 2, 4, 5)。

当忽略非唯一值时,我使用了以下内容:

void Population::create_ranks_costs(vector<Solution> &pop)
{
  size_t const n = pop.size();

  // Create an index vector
  vector<size_t> index(n);
  iota(begin(index), end(index), 0);

  sort(begin(index), end(index), 
       [&pop] (size_t idx, size_t idy) { 
         return pop[idx].costs() < pop[idy].costs();
       });

  // Store the result in the corresponding solutions
  for (size_t idx = 0; idx < n; ++idx)
    pop[index[idx]].set_rank_costs(idx + 1);
}

有谁知道如何考虑非唯一值?我更喜欢使用std::algorithm,因为 IMO 这会导致代码干净。

【问题讨论】:

  • average rank for non unique values 是什么意思?那不是(非唯一的)价值本身吗?
  • @barakmanos,请参阅问题中给出的示例。值 5 是非唯一的,如果我们忽略它,排名是 (1, 3, 2, 4, 5)。然后是三个 5,每个都有不同的等级。在统计学中,通常将这些等级的平均值 avg(3, 4, 5, ) = 4 分配给所有值 5。

标签: c++ algorithm c++11 rank


【解决方案1】:

正如问题的标题所暗示的,这是向量的例程:

template<typename Vector>
std::vector<double> rank(const Vector& v)
{
    std::vector<std::size_t> w(v.size());
    std::iota(begin(w), end(w), 0);
    std::sort(begin(w), end(w), 
        [&v](std::size_t i, std::size_t j) { return v[i] < v[j]; });

    std::vector<double> r(w.size());
    for (std::size_t n, i = 0; i < w.size(); i += n)
    {
        n = 1;
        while (i + n < w.size() && v[w[i]] == v[w[i+n]]) ++n;
        for (std::size_t k = 0; k < n; ++k)
        {
            r[w[i+k]] = i + (n + 1) / 2.0; // average rank of n tied values
            // r[w[i+k]] = i + 1;          // min 
            // r[w[i+k]] = i + n;          // max
            // r[w[i+k]] = i + k + 1;      // random order
        }
    }
    return r;
}

一个工作示例请参阅IDEone

对于具有相同(相等)值的排名,有不同的约定(最小值、最大值、平均排名或随机顺序)。在最里面的 for 循环中选择其中之一(平均排名在统计中很常见,在运动中排名最低)。

请注意,平均排名可能不是整数 (n+0.5)。 我不知道,如果四舍五入到整数等级n 对您的应用程序来说是个问题。

该算法可以很容易地推广到用户定义的排序,如pop[i].costs(),默认为std::less&lt;&gt;

【讨论】:

    【解决方案2】:

    一种方法是使用multimap

    • 将项目放置在将您的对象映射到size_ts 的多重映射中(初始值不重要)。你可以用一行来做到这一点(使用带有迭代器的 ctor)。

    • 循环(简单地或使用 algorithm 中的任何内容)并分配 0、1、... 作为值。

    • 循环遍历不同的键。对于每个不同的键,调用 equal_range 获取键,并将其值设置为平均值(同样,您可以使用 algorithm 中的内容)。

    整体复杂度应该是Theta(n log(n)),其中n是向量的长度。

    【讨论】:

      【解决方案3】:

      类似的东西:

      size_t run_start = 0;
      double run_cost = pop[index[0]].costs();
      for (size_t idx = 1; idx <= n; ++idx) {
        double new_cost = idx < n ? pop[index[idx]].costs() : 0;
        if (idx == n || new_cost != run_cost) {
          double avg_rank = (run_start + 1 + idx) / 2.0;
          for (size_t j = run_start; j < idx; ++j) {
             pop[index[j]].set_rank_costs(avg_rank);
          }
      
          run_start = idx;
          run_cost = new_cost;
        }
      }
      

      基本上,您遍历已排序的序列并识别相等值的运行(可能长度为 1 的运行)。对于每次这样的运行,您计算其平均排名,并为运行中的所有元素设置它。

      【讨论】:

        猜你喜欢
        • 2016-11-22
        • 1970-01-01
        • 2017-10-04
        • 2013-10-03
        • 2013-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多