【问题标题】:c++ remove items from vector whose values are within an intervalc ++从向量中删除值在区间内的项目
【发布时间】:2012-11-02 08:10:43
【问题描述】:

有没有一种方便的方法可以从向量(或另一个 stl 容器)中删除其值在特定间隔内的项目?

例如:我得到了一个带有浮点值的向量

1.1 1.3 2.2 3.2 4.1 5.2 5.1 1.1 8.0 2.1

和 0.2 的 delta,这应该会导致以下结果

1.1 2.2 3.2 4.1 5.1 8.0

从而删除增量中的所有“重复”项目并将其中一个值保留在范围内。 可以假设这些值是“聚集的”,它们之间的差异大于 3*delta。只应保留集群的一个值(平均值),应删除集群中的所有其他值。

当然,可以用嵌套循环进行迭代,但这似乎很复杂,因为迭代器不断变化,所以我想到了一种更方便的方法。例如,我找到了 remove_if,但是这个函数不能“比较”。

感谢您的建议。

【问题讨论】:

  • 对,从我的例子来看,它应该是中位数,但无论如何我会在之后计算平均值以进行插值。

标签: c++ stl containers


【解决方案1】:

您可以将std::unique 与谓词一起使用:

template <typename It, typename Predicate>
It unique(It first, It last, Predicate pred);

std::unique 最常用的形式不使用谓词,只是从序列中删除重复项。但是您可以编写一个实现您的过滤器(在您的情况下,使用间隙比较两个值)并且您已设置。比如:

bool CompareWithGap(double a, double b)
{
  return abs(a - b) <= 0.2;
}

并用它来调用std::unique

auto it = std::unique(v.begin(), v.end(), CompareWithGap);

v 是您的向量(或任何其他序列)。

编辑:忘记提及在使用std::unique 之前需要对序列进行排序。如果这不是一个选项,您必须编写自己的算法。

第二次编辑:为了完成,正如 Christian Rau 在他的评论中指出的那样,您现在可以使用其中的 erase 方法擦除从序列中删除的项目:

v.erase(it, v.end());

【讨论】:

  • 可能与std::vector::erase配对。
  • @ChristianRau 确实要删除无用的踪迹。
  • 根据en.cppreference.com/w/cpp/algorithm/unique,使用具有非等价关系的std::unique 是未定义的行为。 CompareWithGap 不具有传递性,因此不存在等价关系。
  • @chw 没错,但 OP 说我们可以假设值是聚集的。在这种情况下,关系确实是等价关系。在这种情况下,不确定性只是我们不知道每个集群中将保留哪个值,但我认为这并不重要。
【解决方案2】:

您的问题实际上非常复杂,因为“范围内的值之一”没有明确定义。例如给定

1.1 1.2 1.3

你想保留哪个?大概是第一个,即1.1。好的,现在怎么样

0.9 1.1 1.3

按照第一条规则,我们保留 0.9 和 1.3,但我们可以只保留 1.1。我认为的问题是 0.9 和 1.3 是否“重复”?我认为您对此的定义不够好。

这种情况呢,1.1 1.2 1.3 1.4 1.5 1.6?所有值都在一个其他值的 0.2 范围内,但并非所有值都在每个其他值的 0.2 范围内。那么它们都是重复的吗?或者你需要把它们分开吗?如果是,应该如何拆分,或许选择 1.1 和 1.4?

所以我认为,除非您更准确地定义您的问题,否则您实际上不可能编写代码或我们来帮助您。

您可能想查看disjoint-set data structure。取决于您正在尝试做什么,这可能是解决此问题的最有效方法。

【讨论】:

  • 谢谢,我编辑了我的描述。可以假设值是聚类的,所以不应该出现1.1 1.2 1.3 1.4 1.5 1.6的情况,只举例1.1 1.12 1.0 1.13 1.14 2.0 2.1 2.15 2.16
  • 嗯,不相交集数据结构确实可以工作,但我也可以使用模式识别算法,例如均值偏移,但这对于这个应用程序来说是相当大的。我更喜欢简单的基于 stl 容器的方法
  • 我完全同意你的观点,但请记住,这更像是一个评论而不是一个答案,即使它可能不太适合评论的有限空间。
【解决方案3】:

您能否在迭代现有向量并填充所需数据的同时创建一个新向量?然后,您可以删除旧向量并返回对新向量的引用。

【讨论】:

  • 这种适当的函数式方法当然是个好主意,但是“你可以……返回对新向量的引用”是错误的,你必须按值(或通过智能指针)返回新对象.您可能的意思是最后将结果与输入交换,然后让临时结果离开范围。
【解决方案4】:

如果您想要保留第一项(因此,如果您有 0.9、1.1 和 1.3,则保留 0.9 和 1.3),那么您的谓词应该是一个类。

理想情况下,您的课程应如下所示:

class IsWithinRange
{
   std::set< double > values;
   double tolerance;

public:
   explicit IsWithinRange( double tol ) : tolerance( tol )
   {
   }

   bool operator()( double val )
   {
       std::set< double >::iterator iter = values.lower_bound( val );
       if( iter != values.end() )
       {
           if( *iter - val < tolerance )
           {
              return false;
           }
       }
       if( iter != values.begin() )
       {
          --iter;
          if( val - *iter < tolerance )
          {
              return false;
          }
       }
       values.insert( val );
       return true;
    }
};

这应该可以作为std::remove_if 的函子谓词类使用,但它可能会复制内部 std::set 的次数超出您的预期,因此您可以尝试“优化”它,如果您觉得需要的话。 (使用集合创建它,虽然这很麻烦,您也可以指定 remove_if 的第三个模板参数来指示引用而不是依赖推导)。请注意,谓词 operator() 是非常量的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    相关资源
    最近更新 更多