如何更改 int 向量中的重复元素,以便在保持元素数量和单调性的同时不重复任何值?
How can I change duplicate elements in a vector of ints so no values are repeated while also maintaining the number of elements and monotonicity?
我有代码可以根据参数化方程生成从 0 到 1 的 N 个浮点数的分布。我需要它们作为 8 位整数值,所以之后我将它们放大到 255 并将它们四舍五入到最接近的 int。我还需要它们是唯一的,没有重复的值。测试重复项并删除它们是相当简单的,但是,我需要保留 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;
在此先感谢您的帮助。
解决此问题的最简单方法是对数组执行两次遍历,假设它是从以下位置开始排序的:
- 前向传球,在
A[n] <= A[n-1]
时修改 A[n] = A[n-1] + 1
并固定到 255
- 反向传递,在
A[n] >= A[n+1]
时修改 A[n] = A[n+1] - 1
并且(可选)固定为 0
如果您的数组长度为 256 或更短,则保证所有元素都是唯一的。
它不一定是最优的,也不能保证调整后的值尽可能接近其原始值,但这似乎不是您的要求之一。
任何比这更聪明的事情都可能需要大量的努力。
您可以从 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 循环。
@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;
}
}
我有代码可以根据参数化方程生成从 0 到 1 的 N 个浮点数的分布。我需要它们作为 8 位整数值,所以之后我将它们放大到 255 并将它们四舍五入到最接近的 int。我还需要它们是唯一的,没有重复的值。测试重复项并删除它们是相当简单的,但是,我需要保留 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;
在此先感谢您的帮助。
解决此问题的最简单方法是对数组执行两次遍历,假设它是从以下位置开始排序的:
- 前向传球,在
A[n] <= A[n-1]
时修改A[n] = A[n-1] + 1
并固定到 255 - 反向传递,在
A[n] >= A[n+1]
时修改A[n] = A[n+1] - 1
并且(可选)固定为 0
如果您的数组长度为 256 或更短,则保证所有元素都是唯一的。
它不一定是最优的,也不能保证调整后的值尽可能接近其原始值,但这似乎不是您的要求之一。
任何比这更聪明的事情都可能需要大量的努力。
您可以从 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 循环。
@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;
}
}