【发布时间】:2021-08-03 13:29:35
【问题描述】:
有人能想出一个干净(快速)的解决方案来解决以下问题吗:
- 我有一个条目序列,基本上包含一个键和一个值,比如一个
struct Value {
int index = 0;
int cost = 0;
}
- 我现在想合并条目,这样每个键只包含一次,但值应该合并 - 即每个
index应该只包含在序列中一次,并且每个重复索引的cost应该累积.
我想出的基本解决方案是对序列进行排序,当在传递给std::sort 的BinaryPredicate 中检测到相等的条目时,cost 将与lhs 相加。然后rhs 的成本将设置为 0。然后是 remove_if,它删除了 0 成本值。示例见此处:
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <iostream>
struct Value
{
int index = 0;
int cost = 0;
};
// generate a bunch of random values in a vector
// values will have indices in range [0..10]
std::vector<Value> generator()
{
std::vector<Value> v(20);
std::generate(v.begin(), v.end(), []() { return Value{std::rand() % 10, std::rand() % 10}; });
return v;
}
void print(const std::vector<Value> &values)
{
for (auto v : values)
std::cout << "{i=" << v.index << ", c=" << v.cost << "}, ";
std::cout << "\n";
}
//
void merge(std::vector<Value> &values)
{
// sort values and merge costs
std::sort(values.begin(), values.end(), [](auto &lhs , auto &rhs) {
if (lhs.index == rhs.index) {
lhs.cost += rhs.cost;
rhs.cost = 0;
}
return lhs.index < rhs.index;
});
// remove entries with empty cost
auto it = std::remove_if(values.begin(), values.end(), [](const auto &v) { return v.cost == 0; });
values.erase(it, values.end());
}
int main()
{
auto v = generator();
std::cout << "generated values: ";
print(v);
merge(v);
std::cout << "merged values: ";
print(v);
}
问题是:虽然上面的示例产生了正确的结果,但我可以看出它不符合 C++ 标准。 BinaryPredicate“不应通过取消引用的迭代器应用任何非常量函数”http://eel.is/c++draft/algorithms.requirements#8.sentence-4。比较是一个 BinaryPredicate。 http://eel.is/c++draft/alg.sorting#general-2.sentence-1)
这是否意味着我唯一的选择是推出自定义 inplace_unique_reduce 或类似的,或者是否有其他优雅的方法来解决这个问题?我宁愿不必为此编写自己的重要算法。
谢谢
【问题讨论】:
-
请内嵌示例代码,而不仅仅是通过指向外部易失资源的链接。
-
在
std::sort里面做积累是个坏主意。 -
您可以先排序,然后处理相等的键(现在应该连续存储),最后删除所有重复的键。当然,第二步和第三步可以同时完成。但是,第 2 步和第 3 步的复杂度为 O(n)。因此,我不会太在意 1 或 2 次额外的循环运行。
-
绝对需要就地完成吗?地图将是一个非常合适的解决方案,imo。另外,您希望解决方案有多通用?在您提供的答案中,该解决方案非常通用,但通过指定一些提取函数参数(projection)可能会更加通用。
-
我同意 Scheff 的观点,即在排序之后,大多数 STL 解决方案可能是两阶段(for_each+remove_if)。然后你可以使用
Value* prev = nullptr来跟踪当前的 value.index。对于大量数据,您可以改用自定义合并排序,合并也可以进行归约。