java—如何将数组中的数字与同一数组的整个列进行比较

xytpbqjk  于 2021-07-11  发布在  Java
关注(0)|答案(1)|浏览(402)

关闭。这个问题需要更加突出重点。它目前不接受答案。
**想改进这个问题吗?**通过编辑这篇文章更新这个问题,使它只关注一个问题。

上个月关门了。
改进这个问题
在学校里,我们有一个任务,我们必须创建一个有20个槽的数组,然后用1到100之间的随机数填充它,用随机数生成器,类似这样:

int arr[] = new int[20];

for (i = 0; i < arr.length; i++) {
    arr[i] = (int) (Math.random() * 100) + 1
}

然后我们必须在这个随机数发生器的序列中找到最大的一个。比如:

int maximum = arr[0]; 
/*a random integer that's initialized after the line-up is made,
 *since initializing ahead of that would be problematic,
 *and then starts with the first value in the array
 */

for (i = 0; i < arr.length; i++) {
    if (maximum < arr[i]) {
        maximum = arr[i]
    }
}

然后我们只打印数组的内容,以及从数组中新找到的最大值。我在这里也有点创意;纯粹出于审美原因,你会看到:

System.out.println("The line-up of random numbers generated into the array:");
for (i = 0; i < arr.lenghth; i++) {
    if (i == arr.length - 1) {
        System.out.println(arr[i]);
    } else {
        System.out.print(arr[i] + ", ");
    }
}

(如果你没有抓住它;它将数组的所有元素打印在同一行中,用逗号和空格分隔。然后,当它到达末尾时,它将在同一行中打印它,但超出的内容将在下一行中,并且它也不会打印逗号和/或空格。纯粹为了美观。)
这是我变得更有创造力的地方;我想把所有整数按增长顺序排列。经过一些研究,插入排序是相当简单的(如果你能原谅我的话,我会跳过输入代码,因为1)它工作了,2)它有一些非英语短语,因为我不是地道的英语,因此我的变量命名也不是英语的,特别是如果这是要发送回老师,3)坦率地说,堆栈溢出中的内置代码采样器不是很好-来自netbeans-,但不管怎样。关键是,这里没有麻烦。)
我的麻烦开始了,当我看到,所有的数字都是一样的。我想“我们改变它怎么样”然后就彻底失败了。它不仅没有改变数字,而且赚得更多。如果你问我,那就是因果报应。
不管怎样,我说“好吧,让我们做一些研究。”而我的研究机器,谷歌,没有正确的,或者有完全相同的解决方案,因为我正在尝试。即:

for (i = 0; i < arr.length; i++) {
    for (j = i+1; j < arr.length; j++)
        if (arr[i] == arr[j]) {
            arr[i] = (int) (Math.random() * 100) + 1;
        }
    }
}

现在,在你说“把”>>arr[i]<<改成”>>arr[j]<<之前,也不要掷骰子。
在我思考的时候,我想“为什么我不能一个接一个地遍历所有的值,然后再遍历所有的值,并将它们与我最初选择的值进行比较?”但后来我意识到,这个算法就是这样做的,问题就在别处。但为了我一生的爱,我想不通。
另外,如果有人感兴趣,这里有完整的代码;但如果没有坏代码:

package searchformax;
public class SearchForMax {
    public static void main(String[] args) {
        //initialization
        int arr[] = new int[20]; //array, and it's set length

        //random number generation
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 100) + 1;
        }

        int maximum = arr[0]; 
        /*a random integer that's initialized after the line-up is made,
         *since initializing ahead of that would be problematic,
         *and then starts with the first value in the array
         */

        //the search for the actual maximum number
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > maximum) {
                maximum = arr[i];
            }
        }

        //userfriendliness
        System.out.println("The line-up of random numbers generated into the array:");
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                System.out.println(arr[i]);
            } else {
                System.out.print(arr[i] + ", "); 
            }
        }

        //presenting the largest nummber
        System.out.println("The largest number from these: " + maximum);
    }
}

再说一次,这不是严格必要的,只是为了满足我对更多知识的渴望——说真的,我们太慢了,特别是在我的国家,每一个高中生都被送回家上学;是的,你可以说“小心自我教育”,但是,别担心。而且,当要发送的时候,我不打算用额外的代码发送,当我发送的时候就把它删掉了。不过,我还是很好奇,我的问题出在哪里,或者它是不是由于java的特性而无法修复,或者是什么。

qmb5sa22

qmb5sa221#

误用math.random() arr[i] = (int) (Math.random() * 100) + 1 实际上,这不是正确的方法。问题是这样的:如果我给你一个标准的骰子(有6个边),我让你给我一个1到4之间的均匀分布的随机数,然后你做这个算法:“好吧,好吧,我会滚骰子,如果我得到5或6,我会把这些‘溢出’分为1和2,所以如果我滚1或5,我回答1,2/6,我回答2,对于3我回答3,对于4我回答4“。你可以清楚地看到,这是不统一的。完全相同的问题,除了那些大得多的数字,适用于你正在做的事情:最终,计算机是二进制的,有一个特定的非无限量的答案 Math.random() 可能会给。如果这个数不能被100整除(事实并非如此),那么一些数字出现的频率会比其他数字更高。虽然不会太多,但也不是随机的。

这就是为什么这是正确的方法: arr[i] = rnd.nextInt(100) + 1 哪里 rnd 是由于 Random rnd = new Random(); (您创建一个random示例,然后不断调用它以获得越来越多的随机数)。

真正的问题是随机选择,但唯一的数字

深入到你的故事中,我们得到了一个实际的问题,似乎是:
我想用从1到r(包括1和r)之间选择的随机数填充一个n大小的数组,但不要重复。
其中n=20,r=100。

算法的问题(重卷重复项)

你已经(糟糕地)实现的策略是不断检查你得到的数组是否唯一,如果不是,则重新对数字进行roll。不幸的是,您没有考虑到在20大小的数组中重新运行第15个数字可能会导致它等于第一个数字。从理论上讲,你可能要花很长时间才能得到一个独特的阵列。假设n=100,r=90,那么它将永远运行(当然,你不能在1到90之间生成100个唯一的数字)。像您编写的for循环不能永远运行,因此这必然是错误的:您的方法需要一些循环构造,理论上可以永远运行。
正确的方法不是先把20个数字加起来,然后开始检查。它是滚动1个数字,然后检查该数字是否可添加(唯一),这很简单:您还没有数字,它是可添加的。然后继续:滚动第二个插槽的编号,但继续滚动直到编号唯一。
请注意,当n等于r或仅小一点时,此算法效率非常低:如果n=1000且r=1000,那么对于最后一个数字,您实际上要滚动1000次(每次检查999个条目),直到您幸运地滚动到仍然可用的数字为止。当然,如果n小于r,它将永远运行。你也许应该写一封信 if 一个可以提前中止的地方。
还有其他更简单的方法:

枚举和洗牌

枚举“选择范围”中的每个数字(因此,对于您的问题,请列出 [1, 2, 3, 4, 5, .... 100] . 然后使用例如。 Collections.shuffle ,然后只需从中选取前n个数字,就可以得到n个均匀分布且唯一的随机数,介于1和r之间。
除非“pick range”非常大(比如说100k+),否则这种方法很有效,因为如果它非常大,则需要制作一个非常大的列表,并等待相对较长的时间来洗牌该列表。

构建和洗牌

另一种算法的效果正好相反:r大n小时效果很好:
其目的是以一种聪明的方式保持有效选项的实际“数字行”:让我们回到一个非常简单的r=6,n=2的情况。对于第一个数字,有效选项为 [1, 2, 3, 4, 5, 6] . 假设是4。那么对于第二个数字,有效选项只有 [1, 2, 3, 5, 6] . 换句话说, .nextInt(5) 是第二卷需要的,不是吗 nextInt(6) ,但是 nextInt(5) 需要Map。可能的输出是 [0, 1, 2, 3, 4] 这需要Map到 [1, 2, 3, 5, 6] .
为了有效地做到这一点,你需要按排序顺序存储你已经选择的数字(想象r=100,n=20,所以你有18和10。如果 nextInt() 然后产生16,你需要调整到19;您不知道,在已经选择的号码列表中出现18意味着您需要增加nextint调用的结果,直到您将其增加之后,因为已经选择了10,这就是为什么需要对其进行排序)。您可以有效地保持列表的“自排序”(插入到正确的位置),而不是插入排序-请参阅 TreeSet<Integer> 这是一个高效的数据结构。它涉及到树木,变得非常复杂。
然后,在最后,你有一个n个数字的排序列表,从1-r中随机选择,没有重复。要使其完全随机,只需将此列表洗牌。
这个算法比以前的算法复杂得多。
一个完全灵活的“总是有效的”实现在n小r大的情况下使用第二种算法,在r小的情况下使用第一种算法,或者n和r在同一个范围内,如果n>r则抛出一个异常。

相关问题