【问题标题】:Bug in random numbers in AndroidAndroid中随机数的错误
【发布时间】:2014-04-29 02:29:14
【问题描述】:
TreeSet myNumbers = new TreeSet();
Random randGen = new Random();

for (int i = 1; i <= 16; i++) {
    // number generation here
    int randNum = randGen.nextInt(16 - 1) + 1;
    for (;;) {
        if (myNumbers.add(randNum))
            break;
        else
            randNum = randGen.nextInt();

    }
    Toast.makeText(getApplicationContext(), "" + randNum, 100).show();
}

我想生成 1 到 16 之间的随机数,并且不应该重复相同的数字。

上面的代码给了我这样的输出:

2, 5, 7, 9, 1, 4, 10.4109446, -448831, 98824724, 11, 13, ...

我不知道为什么它给我的随机数不在 1-16 范围内,请帮帮我。

【问题讨论】:

  • 您的代码似乎给您非整数结果,这表明您可能没有真正正确地诊断它。如果你真的能说服 Android 将 10.4109446 存储在 int 中,我会感到惊讶。
  • @Vyger 无限循环
  • @blackbelt: 喜欢while(true) {?
  • @Vyger 关注此链接ehow.com/…
  • 我认为是 '.'在输出中只是一个错字,它实际上是一个','。有可能吗?

标签: java android math random


【解决方案1】:

如果您在 1-16 范围内工作,这不是一个大问题,但您的代码会导致拒绝某些随机抽取的数字,如果它们之前已经被选中的话。 在您的解决方案中,nextInt() 调用的预期值与 n log(n) 成正比,其中 n 是您想要洗牌的总元素数(16 in你的情况)——单次运行的实际值可能要高得多。您可以考虑使用更高效的实现。

始终只使用 n 个调用的解决方案:

ArrayList<Integer> originalNumbers = new ArrayList<Integer>();
Random randGen = new Random();
int max = 16;
for (int i = 1; i <= max; i++) {
    // initializing ordered list so it becomes 1, 2, ..., max
    originalNumbers.add(i);
}
for (int i = max; i >= 1; i--) {
    // picking a random number from the ordered list, and swapping it
    // with the last unpicked element which is placed closer to the
    // end of list, where the already picked numbers are stored
    int randNum = randGen.nextInt(i);
    Collections.swap(originalNumbers, i - 1, randNum);
    Toast.makeText(getApplicationContext(), "" + originalNumbers[i - 1], 100).show();
}

【讨论】:

  • 假设 TreeSet.add 为 O(n log n), HashSet.add ~ O(1), Random.nextInt ~ O(1);如您所说,对Set.add 的调用次数为O(n log n)。那么OP的方法的复杂度是O((n log n) ^ 2)。使用HashSet 或boolean[n] 会给我们带来O(n log n) 的复杂性。您的方法(naïve Fisher-Yates shuffle)每次调用ArrayList.remove ‒ O(n^2)n 次都需要 O(n) 时间i> 总计;比 O(n log n) 差。 Knuth shuffle,在移除之前将所选元素与最后一个元素交换,复杂度 O(n)。令人惊讶的是,这里没有一个答案使用它。
  • 你没有交换元素
  • 再次感谢@kyrill,希望现在一切都好。不幸的是,我目前没有设置任何开发环境,编码相当盲目。如果还不行,请告诉我。
  • Collections.swap(originalNumbers, i - 1, randNum); 所以你允许一个元素保持在同一个位置
【解决方案2】:

要生成一个范围内的随机数,是这样的:

int min = ...
int max = ...
int randNumber = min + new Random().nextInt(max - min + 1);

因此,在您想要从 [1, 16] 生成随机数的示例中,它看起来像:

int randNumber = 1 + new Random().nextInt(16 - 1 + 1);

或者如果您选择简化:

int randNumber = 1 + new Random().nextInt(16);

另外,你真的应该使用while 循环而不是无限的for 循环:

    final TreeSet<Integer> myNumbers = new TreeSet<>();
    final Random rand = new Random();
    for(int i = 0; i < 16; i++){
        int n = 1 + rand.nextInt(16);
        while(!myNumbers.add(n))
            n = 1 + rand.nextInt(16);
    }

【讨论】:

    【解决方案3】:

    您只生成 1-15 范围内的 一个 数字。然后,您将生成 后续 个数字,其中仅包含 nextInt:

    if (myNumbers.add(randNum))
        break;
    else
        randNum = randGen.nextInt();
    

    应该是:

    if (myNumbers.add(randNum))
        break;
    else
        randNum = randGen.nextInt(16) + 1;
    

    ... 并修复对nextInt 的初始调用以删除“-1”。 (您不需要 16 - 1,正如 Josh 的回答中所解释的那样。)

    【讨论】:

    • @VikrantAlekar 如果它解决了你的问题你为什么不接受它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2013-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多