Java 生成用户必须对ASC/DESC进行排序的混乱随机数列表 背景
我和其他一些人正在我们大学的一个项目中开发一个android报警应用程序。 我们有一个称为“挑战”的概念,在这个概念中,用户必须完成一个挑战才能关闭警报。其中一个挑战/用户案例是对数字列表ASC/DESC进行正确排序 问题 目标/问题是为用户提供一个提供最大混乱度的列表,以便列表尽可能难以为人排序 我的基本想法是,如果你得到一个混乱的数字列表,例如:[131129315328931953],那将很难排序(如果你对混乱有更好的理解,请分享) 计算性能不是我们主要关心的问题,而是列表的质量 解决问题的尝试 首先,我立即搜索Fisher-Yates、shuffling,然后继续寻找方差和标准差的信息 我的一个朋友建议,如果我们说(步骤1)从100-999生成3个数字,然后(步骤2)生成3个较小的数字(比如说1——对于每个大的数字,加上大的数字,我们会得到一个漂亮而混乱的数字列表。也许会进行一些检查,以确保大的数字变化足够大,小的数字变化不太大。最后,我们对计算出的数字进行洗牌 我想到的最好的(用Java编写,但任何语言都可以)是:Java 生成用户必须对ASC/DESC进行排序的混乱随机数列表 背景,java,algorithm,random,numbers,statistics,Java,Algorithm,Random,Numbers,Statistics,我和其他一些人正在我们大学的一个项目中开发一个android报警应用程序。 我们有一个称为“挑战”的概念,在这个概念中,用户必须完成一个挑战才能关闭警报。其中一个挑战/用户案例是对数字列表ASC/DESC进行正确排序 问题 目标/问题是为用户提供一个提供最大混乱度的列表,以便列表尽可能难以为人排序 我的基本想法是,如果你得到一个混乱的数字列表,例如:[131129315328931953],那将很难排序(如果你对混乱有更好的理解,请分享) 计算性能不是我们主要关心的问题,而是列表的质量 解决问题
//配置变量。
int min=101;
int max=999;
int innerMin=1;
int innerMax=99;
int innerSize=3;
int outerSize=3;
//这里的数字只是“随机”挑选的。
双最小方差=100.0;
双最大内方差=33.0;
//java.util.Random可能不是最优的,但目前。。。
随机rng=新随机();
int[]number=新int[outerSize*innerSize];
//先填充大数组。
int[]大=新的int[outerSize];
while(计算方差(大)最大内方差){
对于(int j=0;i
如您所见,代码似乎相当复杂,4个嵌套循环-哎哟?有没有更好的方法在概念上或算法上做到这一点,等等
编辑
编辑1,在@ElKamina:s的评论后使一些假设更清晰
我做了以下视觉假设:
-数字列表在视觉上被洗牌。
-它们再次被洗牌,为数字提供背景色,以增加混乱。
-为了解决你提出的认知问题,数字用网格表示,因此不适用
现在是一个模型假设:
-所有数字都有3位数字(长度相同)
工作溶液
重新定义了整个实现,并使用nextGaussian等使其工作。此解决方案保证每个集群中的唯一性(AFAIK),可能很慢,但在这里它很健壮,质量>速度(非常欢迎对代码进行优化)
使用2.0标准差可以得到我感觉良好的传播。
更多代码@
@覆盖
公共整数[]生成列表(随机rng,整数大小){
//外部=索引0,内部=索引1。
int[]大小=计算(大小);
int[]number=新的int[size[0]*size[1]];
int outerMultiplier=com.google.common.math.IntMath.pow(10,this.numDigits-1);
int innerMax=外乘法器-1;
//首先填充外部数组。
int[]外部=新的int[大小[0]];
对于(int i=0;i // Config variables.
int min = 101;
int max = 999;
int innerMin = 1;
int innerMax = 99;
int innerSize = 3;
int outerSize = 3;
// The numbers here were just picked "at random".
double minVariance = 100.0;
double maxInnerVariance = 33.0;
// java.util.Random is maybe not optimal, but for now...
Random rng = new Random();
int[] numbers = new int[outerSize * innerSize];
// Fill big array first.
int[] big = new int[outerSize];
while ( computeVariance( big ) < minVariance ) {
for ( int i = 0; i < outerSize; ++i ) {
int random;
do {
// Maybe use nextGaussian here instead?
random = (int) (min + (rng.nextDouble() * (max - min)));
} while ( random % 10 == 0 ); // Exclude all numbers that are modulo 10, too easy.
big[i] = random;
}
}
for ( int i = 0; i < outerSize; ++i ) {
// Fill a small array for each big array.
int[] small = new int[innerSize];
while ( computeVariance( small ) > maxInnerVariance ) {
for ( int j = 0; i < innerSize; ++i ) {
int random;
do {
// Maybe use nextGaussian here instead?
random = (int) (innerMin + (rng.nextDouble() * (innerMax - innerMin)));
} while ( random % 10 == 0 ); // Exclude all numbers that are modulo 10, too easy.
small[i] = big[i] + random;
numbers[innerSize * i + j] = small[i];
}
}
}
// Finally shuffle.
fisherYatesShuffle( numbers, rng );
@Override
public int[] generateList( Random rng, int size ) {
// outer = index 0, inner = index 1.
int[] sizes = computeSizes( size );
int[] numbers = new int[sizes[0] * sizes[1]];
int outerMultiplier = com.google.common.math.IntMath.pow( 10, this.numDigits - 1 );
int innerMax = outerMultiplier - 1;
// Fill outer array first.
int[] outer = new int[sizes[0]];
for ( int i = 0; i < sizes[0]; ++i ) {
outer[i] = RandomMath.nextRandomRanged( rng, 1, 9 ) * outerMultiplier;
}
// Fill inner array for each outer array.
for ( int i = 0; i < sizes[0]; ++i ) {
// Calculate bounds [min, max].
int[] innerBounds = new int[] { RandomMath.nextRandomNon10( rng, 1, innerMax ), RandomMath.nextRandomNon10( rng, 1, innerMax ) };
int diff = innerBounds[1] - innerBounds[0];
if ( diff < 0 ) {
// Wrong order, swap!
PrimitiveArrays.swap( innerBounds, 0, 1 );
diff = -diff;
}
if ( diff < sizes[1] ) {
// Difference is too small, make sure we got room!
innerBounds[0] = Math.max( 1, innerBounds[0] - sizes[1] );
innerBounds[1] = innerBounds[0] + sizes[1];
diff = innerBounds[1] - innerBounds[0];
}
BitSet bits = new BitSet( diff );
boolean filledModulo10 = false;
// Now do the filling.
int[] inner = new int[sizes[1]];
for ( int j = 0; j < sizes[1]; ++j ) {
inner[j] = RandomMath.nextGaussianNon10( rng, innerBounds[0], innerBounds[1], MAX_GAUSS_ITERATIONS, INNER_STANDARD_DEVIATIONS );
// Protect against same numbers all the time, can we do away with this loop? not O(n) but still...
boolean hasDuplicate = false;
for ( int k = 0; k < j; ++k ) {
if ( inner[k] == inner[j] ) {
hasDuplicate = true;
}
}
if ( hasDuplicate ) {
if ( !filledModulo10 ) {
// Set all numbers that end with 0 in BitSet, we don't want them!
// This assumes that neither innerBounds[0, 1] are modulo 10.
for ( int l = ((innerBounds[0] / 10) + 1) * 10; l <= innerBounds[1]; l += 10 ) {
bits.set( l - innerBounds[0] );
}
filledModulo10 = true;
}
// Find first false bit.
// This beats the idea of randomness, but avoiding duplicates is more important!
inner[j] = bits.nextClearBit( 0 ) + innerBounds[0];
}
bits.set( inner[j] - innerBounds[0] );
numbers[sizes[1] * i + j] = outer[i] + inner[j];
}
}
return numbers;
}
111111
99999
111111, 99999