如何避免重复和使用java随机函数零?

时间:2016-04-19 09:13:48

标签: java random

我有一个包含100个元素的数组,我想打印20个随机元素而不重复或有零。

我已成功打印出1000个随机的20个数字,但我无法阻止它打印出重复和/或零。 有什么帮助吗?!

以下是代码: -

import java.util.Random;


public class MyClass {
    public static void main(String args[]) {
        int[] persons = new int[1000];
        int[] person  = new int[20];
        Random random = new Random();

        for (int i = 0; i < person.length; i++)
        {
            person[i] = random.nextInt(persons.length);
            System.out.println(person[i]);
        }
    }
}

7 个答案:

答案 0 :(得分:1)

此问题可能会破坏APH记录(每小时答案),在第一个小时内有8个答案。通常情况下,这是一个不好的迹象,但令人惊讶的是,我没有找到一个问题,我本来希望将这个问题视为副本。

不幸的是,大多数“简单”的答案在实践中可能有严重的缺点。最重要的是,所提出的解决方案要么对某些设置效率低下,要么采用无法证明Total Correctness的技术 - 即,算法可能无法证明终止。这指的是将随机数添加到Set并将此Set用于跟踪已选择的不同元素数量的方法。所以,例如,在这段代码中

Set<Integer> set = new HashSet<Integer>();
Random random = new Random(0);  
while (set.size() < sampleSize) {
    set.add(min + rand.nextInt(max));
}

循环可能永远不会终止。你根本无法证明将会选择20个不同的数字。 Random实例可能会在第一次调用中返回0。它可能会在第二次调用中返回0。在第三次电话会议中...... you can never be sure.

当然,“在实践中”,循环通常迟早终止,但这取决于参数:当要求在0到10之间挑选20个不同的随机数时,它将终止。这同样适用于类似技术,例如

int[] ints = new Random(0).ints(0, 10).distinct().limit(20).toArray();

因此,应仔细检查参数,以确保它们在这方面有效。

通常以各种形式建议的另一个选项是在预先填充了可供选择的项目的列表上使用Collections#shuffle。这可能适用于您的情况,此列表可能只有100或1000个元素。但填充一个列表,比如100000000个元素太耗费内存,并且 shuffling 这个列表太耗费时间。

一般来说,有一种相当通用的技术可以解决这个问题。它被称为Reservoir Sampling

(请注意,关于水库采样的实施存在一些问题,但似乎没有提出这个非常通用的任务的解决方案)

这是Java中的水库采样的实现。对于给定的样本大小和范围,它按升序返回所需范围内的(随机,唯一)整数的集合:

/**
 * Creates a collection with the given size, containing random values 
 * between the given minimum value (inclusive) and maximum value
 * (exclusive). The resulting collection will contain the values
 * in ascending order. 
 *  
 * @param size The size of the returned collection
 * @param min The minimum value (inclusive)
 * @param max The maximum value (exclusive)
 * @param random The random number generator
 * @return The collection
 * @throws IllegalArgumentException If the requested size is larger than
 * the difference between the maximum value and the minimum value
 */
public static Collection<Integer> randomSample(
    int size, int min, int max, Random random)
{
    if (size > max - min)
    {
        throw new IllegalArgumentException(
            "Can not create a sample of size "+size+
            " with values between "+min+" and "+max);

    }
    Set<Integer> set = new LinkedHashSet<Integer>(size);
    int n = size;
    for (int i = 0; i < max - min; i++)
    {
        double d = (double) size / ((max - min) - i);
        if (random.nextDouble() < d)
        {
            set.add(i + min);
            n--;
        }
        if (n <= 0)
        {
            break;
        }
    }
    return set;
}        

(如果此实现存在任何缺陷或缺点,请在评论中留言。)

这可以作为类似任务的构建块。例如,在您的情况下,您可以创建 indices 的随机样本,并使用它来选择所需的元素:

int persons[] = new int[1000];
int sample[]  = new int[20];
Collection<Integer> indices = randomSample(20, 0, 1000);
int n = 0;
for (Integer index : indices) 
{
    sample[n] = index;
    n++;
}

对于其他情况,您可能希望从返回的索引创建列表并随机播放此列表。但在这种情况下,只需要对(小)样本进行混洗,而不是包含所有可能输入的(可能很大)列表。

答案 1 :(得分:0)

意味着重复随机数。 For security reasons,如果你没有重复,可以通过简单的先前数字来猜测下一个数字。

总之,任何好的书面随机函数都会在生成少量数字后返回重复的数字。

因此,您实际上正在寻找一些方法来重新排列已选择的元素。 Collections.shuffle()可以做到这一点。

答案 2 :(得分:0)

import java.util.Random;


public class MyClass {
    public static void main(String args[]) {
        int[] persons = new int[1000];
        int[] person  = new int[20];
        Random random = new Random();

        for (int i = 0; i < person.length; i++)
        {
            int number = random.nextInt(persons.length);
            while(number == 0 || IntStream.of(person).anyMatch(x -> x == number))
                number = random.nextInt(persons.length);
            person[i] = number;
            System.out.println(person[i]);
        }
    }
}

这里要小心,如果你只剩下重复数或数组中的0,你可能会进入无限循环!

在随机运行之前,其他对数组的方式只包含唯一元素: How to get unique items from an array?

[编辑]

关于代码IntStream.of(person).anyMatch(x -> x == number)的一些解释。在这里,我们将获取结果数组并对其进行流处理,以便能够使用谓词对其进行过滤。我们正在检查数字的值是否在数组person之前出现过。如果是这样,我们不想使用该数字,因此我们只需从persons数组中执行另一个随机数getter。

答案 3 :(得分:0)

小想法

您可以每次洗牌并获取前20个项目。你将确保没有重复,也没有零。

您可以使用Collections.shuffle(yourList)

答案 4 :(得分:0)

您可以创建 N个不同的随机索引并使用它们来选择元素:

int[] indices = ThreadLocalRandom.current()
    .ints(0, persons.length)
    .distinct()
    .limit(N)
    .toArray();

答案 5 :(得分:0)

如何使用HashSet

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class NonRepeatRandom {
    public static void main(String[] args){
        final int low = 1;
        Set<Integer> set = new HashSet<Integer>();
        Random rand = new Random();  

        while (set.size() < 20) {
            set.add(rand.nextInt(1000) + low); // set bound between 1 and 1000
        }
        System.out.println(set);
    }
}

答案 6 :(得分:0)

//随机播放你的数组

int[] filteredPersons = Arrays.stream(persons).filter(x-> x!=0).distinct().limit(20).toArray();

//在这里打印

Arrays.stream(persons).filter(x -> x!=0).distinct().limit(20).forEach(System.out::println);

//如果你只想打印