Java 生成用户必须对ASC/DESC进行排序的混乱随机数列表 背景

Java 生成用户必须对ASC/DESC进行排序的混乱随机数列表 背景,java,algorithm,random,numbers,statistics,Java,Algorithm,Random,Numbers,Statistics,我和其他一些人正在我们大学的一个项目中开发一个android报警应用程序。 我们有一个称为“挑战”的概念,在这个概念中,用户必须完成一个挑战才能关闭警报。其中一个挑战/用户案例是对数字列表ASC/DESC进行正确排序 问题 目标/问题是为用户提供一个提供最大混乱度的列表,以便列表尽可能难以为人排序 我的基本想法是,如果你得到一个混乱的数字列表,例如:[131129315328931953],那将很难排序(如果你对混乱有更好的理解,请分享) 计算性能不是我们主要关心的问题,而是列表的质量 解决问题

我和其他一些人正在我们大学的一个项目中开发一个android报警应用程序。 我们有一个称为“挑战”的概念,在这个概念中,用户必须完成一个挑战才能关闭警报。其中一个挑战/用户案例是对数字列表ASC/DESC进行正确排序

问题 目标/问题是为用户提供一个提供最大混乱度的列表,以便列表尽可能难以为人排序

我的基本想法是,如果你得到一个混乱的数字列表,例如:[131129315328931953],那将很难排序(如果你对混乱有更好的理解,请分享)

计算性能不是我们主要关心的问题,而是列表的质量

解决问题的尝试 首先,我立即搜索Fisher-Yates、shuffling,然后继续寻找方差和标准差的信息

我的一个朋友建议,如果我们说(步骤1)从100-999生成3个数字,然后(步骤2)生成3个较小的数字(比如说1——对于每个大的数字,加上大的数字,我们会得到一个漂亮而混乱的数字列表。也许会进行一些检查,以确保大的数字变化足够大,小的数字变化不太大。最后,我们对计算出的数字进行洗牌

我想到的最好的(用Java编写,但任何语言都可以)是:

//配置变量。
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