我有一个代码,可以根据参数化的方程生成从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;
预先感谢您的帮助。
答案 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;
}
}