如何更改int向量中的重复元素,以便在保持元素数量和单调性的同时不重复任何值?

时间:2018-11-05 23:35:25

标签: c++ vector eigen

我有一个代码,可以根据参数化的方程生成从0到1的N个浮点分布。我需要将它们作为8位整数值,因此之后将它们缩放到255,并将它们四舍五入到最接近的整数。我还需要它们是唯一的,没有重复的值。测试重复项并将其删除非常简单,但是,我需要保留N个分发点的原始数字大小。在某些情况下,我可能已经有一个唯一的集合,在这种情况下,无需采取任何操作:

0 3 15 40 78 128 177 215 240 252 255->无操作

但是有时候我可能会遇到类似这样的事情:

0 0 0 2 21 128 234 253 255 255 255

在那种情况下,我想要的最终结果是一个看起来像这样的集合:

0 1 2 3 21 128 234 252 253 254 255

我正在将每个重复值调整为使其唯一所需的最小值,同时还要保持单调顺序以及原始点数。

因此,从左到右,我需要做的是将第一个重复值增加1,依此类推。但是请注意,第4个元素是2,因此我还需要考虑在增加其他值的同时创建重复项的可能性。

但是在右侧,255是我的最大可能值,因此我需要将其向左下移1。

我目前使用Eigen作为Vector容器,但是我可以在STL中使用任何东西。

其他并发症是,我无法提前知道原始点的数量N,它可以是2到255之间的任何正整数。

另一个可能相关且有用的细节可能是,我原来从0到1的双精度分布集保证是唯一且单调递增的。我不知道该如何利用,但如果有更好的解决方案,尝试在将重复次数扩展到255之前考虑重复次数是完全可以接受的。

这是当前生成双精度分布集并将其缩放为整数的代码:

Eigen::VectorXi v_i(NUMBER_OF_POINTS);  // NUMBER_OF_POINTS: int from 2 to 255
Eigen::VectorXd v_d(NUMBER_OF_POINTS);
double d;

for ( int i = 1; i < v_d.size() - 1; ++i )
    {
        d = i / ( v_d.size() - 1.0 );
        v( i ) = 1.0 / ( 1.0 + pow( d / ( 1.0 - d ), -SLOPE ) );  // SLOPE: double > 0
    }

v_d( 0 ) = 0;  // Manually setting the endpoints to 0 and 1 to avoid divide by zero error 

v_d( v_d.size() - 1 ) = 1.0;

for ( int i = 0; i < v_i.size(); ++i )
{
    v_i(i) = round( v_d( i ) * 255 );
}

std::cout << v_i << std::endl;

预先感谢您的帮助。

3 个答案:

答案 0 :(得分:0)

最简单的方法是在数组上进行两次遍历,并假设数组以以下内容开头:

  • 前进,在A[n] = A[n-1] + 1时修改A[n] <= A[n-1]并钳位到255
  • 反向传递,在A[n] = A[n+1] - 1且(可选)钳制为0时修改A[n] >= A[n+1]

假设您的数组长度为256或更小,则可以保证所有元素都是唯一的。

这不一定是最佳的,也不能保证调整后的值尽可能接近其原始值,但这似乎不是您的要求之一。

任何比这更聪明的事情都可能需要大量的努力。

答案 1 :(得分:0)

您可以通过从0,1,...,255的向量开始,对其进行随机排序,然后对前N个元素进行排序来做到这一点。可以使用前缀和在恒定时间进行排序:

#include <random>
#include <algorithm>
#include <numeric>
#include <iterator>
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;

int main()
{
  VectorXi base = VectorXi::LinSpaced(256,0,255); 
  std::random_device rd;
  std::mt19937 g(rd());
  std::shuffle(base.begin(), base.end(), g);
  int N = 10;

  std::cout << base.head(N).transpose() << "\n";

  // explicit sort
  {
    VectorXi A = base.head(N);
    std::sort(A.begin(), A.end());
    std::cout << A.transpose() << "\n";
  }

  // no sort but O(256) pass
  {
    VectorXi mask = VectorXi::Zero(256), pos(256);
    mask(base.head(N)).fill(1);
    std::partial_sum (mask.begin(), mask.end(), pos.begin());
    VectorXi A(N);
    for(auto i:base.head(N))
      A(pos[i]-1) = i;
    std::cout << A.transpose() << "\n";
  }

  // same with fused partial_sum
  {
    VectorXi mask = VectorXi::Zero(256);
    mask(base.head(N)).fill(1);
    VectorXi A(N);
    int c = 0;
    for(int i=0,c=0; i<256; ++i)
      if(mask[i])
        A(c++) = i;
    std::cout << A.transpose() << "\n";
  }
}

要使begin()/end()/range-for-loop工作,您需要Eigen的负责人,但是您可以将前者替换为vec.data(), vec.data()+vec.size(),将后者替换为经典的for循环。

答案 2 :(得分:0)

@paddy给出的答案是我基于我的解决方案的。为了社区的完整性,以下是为我解决问题的实际代码。我确信这不是最有效的方法,但是它可以完成工作,并且对于少于1000个的数据集(在我看来)具有足够的性能。

假设我的问题数据存储在Eigen::VectorXi v_int

Eigen::VectorXi v_int_unique = v_int; // Beginning and end values never change 
                                      // middle value won't change if v_int.size() is odd

for ( int i = 1; i < v_int.size() / 2; ++i )
{
    if ( v_int( i ) == v_int( i - 1 ) )
    {
        v_int_unique( i ) = v_int( i ) + 1;
    }

    if ( v_int( i ) < v_int_unique( i - 1 ) )
    {
        v_int_unique( i ) = v_int_unique( i - 1 ) + 1;
    }

}

for ( int i = v_int.size() - 2; i > v_int.size() / 2; --i )
{
    if ( v_int( i ) == v_int( i + 1 ) )
    {
        v_int_unique( i ) =  v_int( i ) - 1;
    }

    if ( v_int( i ) > v_int_unique( i + 1 ) )
    {
        v_int_unique( i ) = v_int_unique( i + 1 ) - 1;
    }

}