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

时间:2018-11-04 23:10:30

标签: java random formula reverse-engineering random-seed

我使用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个值和所使用的公式,我们就可以很容易地知道第三个值,而知道要使用的公式。

2 个答案:

答案 0 :(得分:2)

这是不可能的。许多上限可以产生相同的输出。例如,在Ideone上的quick test显示了1000000以下的9个可能的边界,它们会用种子532产生235的输出(而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的幂,并且循环将持续到第一次迭代之后

以2的幂数bound的情况很容易-只需尝试适合bound的所有以2的幂数int即可。只有31个。您可以对此进行优化,但是没有太多意义。

可以通过计算next(31)本来可以得到的值来处理第一个迭代的非二次方幂情况(可以通过播种Random实例并调用{{1 }}),然后查看next(31)的值将同时给出bound的正确值并终止do-while。

要给出正确的值valval必须比boundbits - val倍。 (有时val将为0,并且任何大于bits - val的整数都将通过。)要终止执行,val不得溢出。因此,在这种情况下,可能的界线是bits - val + (bound-1)在一定范围内的因数,而不是2的幂。

对于最后一种情况,我不想经历,所以将“留给读者练习”。 (这是最困难的情况,遇到一些困难,例如弄清楚bits - val的值会在您不知道bound时引起溢出,这将花费比我更多的时间。)

答案 1 :(得分:0)

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

请参阅下表。在最上面,我们用对随机值进行采样的迭代为列命名。在一侧,我们有具有相同种子的序列,这些序列具有不同的绑定值。中间单元格中的值表示为给定的Random实例和迭代检索的随机值。

在第一次遍历所有可能的整数之后,我们剩下4个可能的候选项。我们将继续与权威机构进行比较,直到仅剩下一名可能的候选人。如果您没有从权威机构提供足够的样本,则可能会留下多个匹配的上限值候选。

enter image description here

Random

这将产生输出:

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);
}