Java 从1-50的生成器生成1-100的随机数
在最近的一次采访中,我被问到以下问题: 使用给定的Java 从1-50的生成器生成1-100的随机数,java,optimization,random,Java,Optimization,Random,在最近的一次采访中,我被问到以下问题: 使用给定的getrnd50()方法打印1-100之间的随机数 它生成1-50之间的随机数。每个随机数 只能按随机顺序打印一次。不使用其他随机数生成器 是允许的,我不允许改变 getrnd50() 我想出了下面的代码,它给出了正确的输出 import java.util.Random; public class Test { public static void main(String[] args) { int[] rs = new int[1
getrnd50()
方法打印1-100之间的随机数
它生成1-50之间的随机数。每个随机数
只能按随机顺序打印一次。不使用其他随机数生成器
是允许的,我不允许改变
getrnd50()
我想出了下面的代码,它给出了正确的输出
import java.util.Random;
public class Test {
public static void main(String[] args) {
int[] rs = new int[100];
int count = 0;
int k;
while (count != 100) {
// I decided to simply multiply the result of `getrnd50()` by 2.
// But since that would generate only even numbers,
k = getrnd50() * 2;
// I decided to randomly subtract 1 from the numbers.
// Which i accomlished as follows.
if (getrnd50() <= 25) // 25 is to half the possibilities.
k--;
// Every number is to be stored in its own unique location
// in the array `rs`, the location is `(number-1)`.
// Every time a number is generated it is checked whether it has
// already been generated. If not, it is saved in its position, printed and
// `count` is incremented.
if (rs[k-1] == 0) {
rs[k-1] = k;
count++;
System.out.print(k + " ");
}
}
}
// This is the method and i am not supposed to touch it.
static int getrnd50() {
Random rand = new Random();
return (1 + rand.nextInt(50));
}
}
import java.util.Random;
公开课考试{
公共静态void main(字符串[]args){
int[]rs=新的int[100];
整数计数=0;
int k;
而(计数!=100){
//我决定简单地将'getrnd50()'的结果乘以2。
//但由于这只会产生偶数,
k=getrnd50()*2;
//我决定从数字中随机减去1。
//我的解释如下。
如果(getrnd50()因为100/50是一个整数,这很容易。因为50/(100/50)是一个整数,这就更容易了
如果您没有完全理解,下面是一些示例代码:
int rnd1 = getrnd50();
int rnd2 = getrnd50();
if (rnd1 % 2 == 0)
{
rnd2 += 50;
}
return rnd2;
以下是一个概要:
- 在1和50之间随机选择的两个数字称为a和b
- 如果a为偶数,则将50加到b
- 返回b
如果需要,您可以将其制作为一行:
return getrnd50() + getrnd50() % 2 * 50;
这有点太模糊了
编辑:我知道问题其实是要求一个无序列表,而不是一个随机整数序列
这可以通过创建一个从1到100的列表,并进行100次随机交换来实现,就像Fisher-Yates shuffle一样。我认为Fisher-Yates shuffle的绝对最小调用数是93次(使用公式ceil(log50(100!))
),但使用更简单的算法,可以使用200次
简单的算法是将100个元素中的每一个替换为100个元素中的一个随机元素。要选择的数字将由上述生成器从1-100生成
例如:
for (int i = 0; i < 100; i++)
{
swap(i, getrnd100() - 1); // - 1 for zero base!
}
for(int i=0;i<100;i++)
{
交换(i,getrnd100()-1);//-1表示零基数!
}
下面是一些完整的代码:
int[] result = new int[100];
for (int i = 0; i < 100; i++)
{
result[i] = i + 1;
}
for (int i = 0; i < 100; i++)
{
int j = (getrnd50() + getrnd50() % 2 * 50) - 1;
int tmp = result[i];
result[i] = result[j];
result[j] = tmp;
}
return result;
int[]结果=新的int[100];
对于(int i=0;i<100;i++)
{
结果[i]=i+1;
}
对于(int i=0;i<100;i++)
{
int j=(getrnd50()+getrnd50()%2*50)-1;
int tmp=结果[i];
结果[i]=结果[j];
结果[j]=tmp;
}
返回结果;
(免责声明:我不懂Java,也没有测试过。)
最佳情况200,最坏情况200,平均情况200。关键是不要检查您以前是否生成过数字,如果只查找剩余的1个数字,这将非常昂贵,而是按顺序生成1-100个数字,然后洗牌
在您的代码中,当您在100个数字中生成99个时,您将循环生成随机数,直到找到剩余的1个数字。这就是为什么您的版本中的平均情况如此糟糕的原因
如果只是对数组进行洗牌,则只需要随机数的数量与洗牌操作的数量相同,洗牌操作的数量与输出数字的数量相同
(有关洗牌的详细信息,请查阅洗牌,特别是可以就地生成洗牌数组的由内而外的变体)
要生成随机数,您需要一个变量生成器,而不是一个固定的1-50生成器。您可以通过多种方式来实现这一点,但如果您确实希望输出在可能的状态之间具有良好的分布,请小心在结果中引入歪斜
例如,我建议使用整数位数的位进行移位,而不是尝试使用模。如果值超出所需的范围,这确实会涉及一定数量的循环,但如果无法修改原始随机数生成,您的手会有些束缚
static int bits = 0;
static int r100 = 0;
static int randomInt(int range)
{
int ret;
int bitsneeded = 32 - Integer.numberOfLeadingZeros(range - 1);
do {
while(bits < bitsneeded)
{
int r = (getrnd50()-1) * 50 + getrnd50()-1;
if(r < 2048)
{
r100 <<= 11;
r100 |= r;
bits += 11;
}
}
ret = r100 & ((1 << bitsneeded) - 1);
bits -= bitsneeded;
r100 >>= bitsneeded;
} while(ret >= range);
return ret + 1;
}
静态整数位=0;
静态int r100=0;
静态int randomInt(int范围)
{
int ret;
int bitsneed=32-整数。前导零的数量(范围-1);
做{
while(位
此实现将为100值随机数组使用150个随机数的区域。这比模版本差,但比输入范围的2倍要好,这是原始版本的最佳情况。如果随机生成是真正随机的,那么最坏的情况仍然是无穷大,但随机生成ion通常不是这样工作的。如果是这样的话,考虑到约束条件,我不确定不符合实际的结果
为了举例说明,由于结果很微妙,下面是我建议的随机例程与模版本的对比图:
总之,我认为虽然你的随机生成有点低效,而且可以改进,但面试官所寻求的真正的大赢家,是在一开始就不需要那么多随机数,通过洗牌而不是以不断降低的概率重复搜索。代码的性能损失他在那条线上
if (getrnd50() <= 25)
if(getrnd50()Ok,那么允许您打印最后一组丢失的n个数字,而不必由随机数生成器生成
如果是这样的话,您可以利用递归并在每次调用时减小集合的大小,直到只有n=2,然后调用getrnd50()一次。当您递归返回时,只需在每个集合上打印缺少的数字。List lint;
List<Integer> lint ;
public void init(){
random = new Random();
lint = new LinkedList<>();
for(int i = 1 ; i < 101; ++i) {
lint.add(i); // for truly random results, this needs to be randomized.
}
}
Random random ;
public int getRnd50() {
return random.nextInt(50) + 1;
}
public int getRnd100(){
int value = 0;
if (lint.size() > 1) {
int index = getRnd50()%lint.size();
value = lint.remove(index);
} else if (lint.size() == 1 ) {
value = lint.remove(0);
}
return value;
}
公共void init(){
随机=新随机();
lint=newlinkedlist();
对于(int i=1;i<101;++i){
lint.add(i);//对于tr
import java.util.*;
import java.lang.*;
class Main {
public static void main(String... args) {
int samples = 100;
// all the numbers [1, 100]
int[] nums = new int[samples];
for (int i = 0; i < samples; i++) nums[i] = i + 1;
for (int i = samples - 1; i > 0; i--) {
int swapWith = nextInt(i + 1);
// swap nums[i] and nums[swapWith]
if (swapWith == i) continue;
int tmp = nums[swapWith];
nums[swapWith] = nums[i];
nums[i] = tmp;
}
System.out.println("calls/sample " + (double) calls / samples);
System.out.println(Arrays.toString(nums));
int[] count49 = new int[49];
for (int i = 0; i < 49 * 10000; i++)
count49[nextInt(49) - 1]++;
int[] count54 = new int[54];
for (int i = 0; i < 54 * 10000; i++)
count54[nextInt(54) - 1]++;
System.out.println("Histogram check (49): " + Arrays.toString(count49));
System.out.println("Histogram check (54): " + Arrays.toString(count54));
}
// keep track of the range of values.
static int maxRandom = 1;
// some random value [0, maxRandom)
static int rand100 = 0;
static int nextInt(int n) {
while (maxRandom < 10 * n * n) {
maxRandom *= 50;
rand100 = rand100 * 50 + getrnd50() - 1;
}
int ret = rand100 % n;
maxRandom = (maxRandom + n - 1) / n;
rand100 /= n;
return ret + 1;
}
static final Random rand = new Random();
static int calls = 0;
static int getrnd50() {
calls++;
return (1 + rand.nextInt(50));
}
}
calls/sample 1.509