如何获得用于生成随机数的范围?
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】:这是一种寻找隐藏边界的实验方法。获取随机对象的副本并记录其中的输出。创建Random
的n
实例,其中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
输出供应的限制。以上是关于如何获得用于生成随机数的范围?的主要内容,如果未能解决你的问题,请参考以下文章