Я рекомендую сделать это так. Если размер и диапазон значительно отличаются, тогда можно использовать sets
или streams
с distinct
. Но если размер и диапазон близки, выполнение этих методов может занять относительно много времени.
Недостатком этого алгоритма является то, что он сначала инициализирует внутренний массив. Я считаю, что это незначительно в большинстве случаев, и увеличение скорости для больших значений как размера, так и диапазона очень значительно.
Это работает следующим образом:
- предполагает массив целых чисел от
0
до N
. - генерирует случайное значение int
k
между 0
и N
- , присваивает это значение массиву возврата.
- замените k-е значение на N-е значение
- , затем уменьшите
N
на 1
- и повторяйте до
N == 0
С k
был фактически удален из списка доступных значений и заменен на неназначенное значение Nth
, это значение больше никогда не будет выбрано.
public static int [] getRandom(int size, int range) {
Random r = new Random();
int[] ret = new int[size];
// initialize number pool
int[] nums = IntStream.range(0,range).toArray();
for (int i = 0; i < size; i++) {
int k = r.nextInt(range);
ret[i] = nums[k];
range--;
nums[k] = nums[range];
}
return ret;
}
Чтобы проверить это, я рекомендую следующее. Набор заполнен только для того, чтобы показать, что нет дубликатов. По иронии судьбы, заполнение набора для подтверждения точки занимает больше времени, чем фактическое создание значений.
int[] v = getRandom(10_000_000, 10_000_000);
System.out.println("Done! Filling set");
Set<Integer> set = Arrays.stream(v)
.boxed()
.collect(Collectors.toCollection(LinkedHashSet::new));
System.out.printf("%,d%n",set.size());
System.out.printf("%,d%n",v.length);
Печать
Done! Filling set.
10,000,000
10,000,000