如何获得用于生成随机数的范围?

Posted

技术标签:

【中文标题】如何获得用于生成随机数的范围?【英文标题】:How can I get the range used in generating random number? 【发布时间】:2019-04-08 08:11:03 【问题描述】:

我使用 Java 中的种子生成随机数。知道最终输出是 235 和种子编号 532,我如何在 java 中获得 intBound 编号? 例如

int randomNumber
int seed=532;
int intBound=800;
Random rand = Random(seed); 
randomNumber=rand.nextInt(intBound);

System.out.println("The generated Random number using the above seed and int bound is:- "+randomNumber);
//Results is: The generated Random number using the above seed and int bound is: 235

这个问题的简化数学版本是:只知道一个数学公式的两个值,如何产生第三个值?例如 1+2=3 这也意味着如果我们只知道 2 个值和使用的公式,我们可以很容易地得到第三个值,知道用于获取结果的公式。

【问题讨论】:

你为什么认为这是可能的? @user2357112 有可能。 See here Random.java "如果 @code Random 的两个实例使用相同的种子创建,并且为每个实例进行相同的方法调用序列,它们将生成并返回相同的数字序列。" @flakes:这些都不意味着您可以恢复nextInt 调用的上限。 @user2357112 但有趣的是,您可以减少可能值的集合。在同一范围内再调用几次nextInt 可能会将集合限制为 1 只是好奇:如果你可以做到这一点(你做不到),你为什么要想要做到这一点?谁会受益?如何受益? 【参考方案1】:

这是不可能的。许多上限可以产生相同的输出。例如,Ideone 上的quick test 显示 1000000 以下的 9 个可能边界,这些边界将产生 235 的输出,种子为 532(而 800 不是其中之一):237、369、711、3239、9717、29151、50549、151647 , 和 454941。

import java.util.*;

class Test

    public static void main (String[] args) throws java.lang.Exception
    
        List<Integer> bounds = new ArrayList<Integer>();
        for (int i = 1; i < 1000000; i++) 
            Random rng = new Random(532);
            if (rng.nextInt(i) == 235) 
                bounds.add(i);
            
        
        System.out.println(bounds);
    


您能做的最好的事情就是确定可能的界限。 nextInt(int) 的实现是required 相当于

 public int nextInt(int bound) 
   if (bound <= 0)
     throw new IllegalArgumentException("bound must be positive");

   if ((bound & -bound) == bound)  // i.e., bound is a power of 2
     return (int)((bound * (long)next(31)) >> 31);

   int bits, val;
   do 
       bits = next(31);
       val = bits % bound;
    while (bits - val + (bound-1) < 0);
   return val;
 

该算法给出特定输出的方式可以分为三种可能性:

bound 是二的幂 bound 不是 2 的幂,循环在第一次迭代时终止 bound 不是 2 的幂,循环在第一次迭代之后继续

bound 的二次幂案例很简单 - 只需尝试适合 int 的每一个二次幂bound。其中只有 31 个。你可以优化这个,但是没有多大意义。

可以通过计算 next(31) 的值来处理第一次迭代的非二次幂情况(这可以通过播种 Random 实例并调用 next(31) 来完成),然后查看bound 的哪些值既会给出正确的 val 值,也会终止 do-while。

要给出正确的 val 值,bound 必须是大于 val 的因子 bits - val。 (有时bits - val 为0,任何大于val 的整数都会通过。)要终止do-while,bits - val + (bound-1) 不能溢出。因此,属于这种情况的可能范围是bits - val 在一定范围内的因数,而不是二的幂。

至于最后一个案例,我不想经历它,所以“留给读者作为练习”。 (这是最困难的情况,当你不知道val 时,很难弄清楚bound 的哪些值会导致溢出,这比我花的时间要多。)

【讨论】:

【参考方案2】:

这是一种寻找隐藏边界的实验方法。获取随机对象的副本并记录其中的输出。创建Randomn 实例,其中n 是最大整数。对于每个Random 实例,使用它们的索引作为nextInt 的参数。仅存储遵循原始序列的Random 实例。继续淘汰候选人,直到只剩下一个。

考虑下表。在顶部,我们使用随机值被采样的迭代来命名列。另一方面,我们有具有相同种子的序列,它们应用了不同的边界值。中间单元格中的值表示为给定 Random 实例和迭代检索到的随机值。

在第一次遍历所有可能的整数后,我们剩下 4 个可能的候选者。我们不断与当局进行比较,直到只剩下一个可能的候选人。如果您没有从权威机构提供足够的样本,您可能会留下多个匹配的候选上限。

public static void main(String [] args) throws Exception 
    final Scanner scanner = new Scanner(System.in);

    System.out.print("Please enter the seed number: ");
    final long seed = scanner.nextLong();
    System.out.print("Please enter the hidden bound: ");
    final int bound = scanner.nextInt();

    final long start = System.nanoTime();

    final IntSupplier original = rand(seed, bound);
    final int firstRandom = original.getAsInt();

    Map<Integer, IntSupplier> candidates = new HashMap<>();
    for (int i = 1; i < Integer.MAX_VALUE; i++) 
        final IntSupplier candidate = rand(seed, i);
        if (firstRandom == candidate.getAsInt()) 
            candidates.put(i, candidate);
        
    

    int iterations = 1;
    while (candidates.size() > 1) 
        iterations += 1;
        final int current = original.getAsInt();

        Map<Integer, IntSupplier> survivors = new HashMap<>();
        for (Map.Entry<Integer, IntSupplier> entry : candidates.entrySet()) 
            if (entry.getValue().getAsInt() == current) 
                survivors.put(entry.getKey(), entry.getValue());
            
        
        candidates = survivors;
    

    if (candidates.size() == 1) 
        System.out.println("Upper bound is " + candidates.keySet().iterator().next());
     else 
        System.out.println("No upper bound found");
    
    System.out.println("Completed in " + iterations +  " iterations");

    final long end = System.nanoTime();
    System.out.println("Completed in " + (end - start) / Math.pow(10,9) + "seconds");


static IntSupplier rand(long seed, int bound) 
    final Random rand = new Random(seed);
    return () -> rand.nextInt(bound);

这会产生输出:

Please enter the seed number: 532
Please enter the hidden bound: 800
Upper bound is 800
Completed in 4 iterations
Completed in 46.778499624 seconds

【讨论】:

如果您可以根据需要生成任意数量的randInt 返回值,并且您可以承受大部分时间来处理数字,那么这是一个选择。如问题所示,只有一个 randInt 返回值,这是行不通的。 另外值得注意的是,取决于界限,特别是如果它是一个非常大的界限,这可能需要很多 randInt 返回值。如果您没有能力随意生成它们(并且您依赖外部源或预先录制的输出),您最终可能会受到randInt 输出供应的限制。

以上是关于如何获得用于生成随机数的范围?的主要内容,如果未能解决你的问题,请参考以下文章

如何获得mysql随机整数范围?

C语言中获得随机数的方法

js如何生成指定范围内的随机数

如何在电话号码格式的范围内生成随机数 Postman [关闭]

random 模块

js如何生成指定范围内的随机数