Java 7+, n = 50 через ~ 30 секунд на TIO
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Random;
class Main{
public static void main(String[] a){
int n=50;
Random randomGenerator = new Random();
int i = n+1;
int squaredN = n*n;
int[]randomIntegers = new int[i];
randomIntegers[n] = squaredN;
while(true){
for(i=n; i-->1; ){
randomIntegers[i] = randomGenerator.nextInt(squaredN);
}
Set<Integer> result = new HashSet<>();
Arrays.sort(randomIntegers);
for(i=n; i-->0; ){
result.add(randomIntegers[i+1] - randomIntegers[i]);
}
if(!result.contains(0) && result.size()==n){
System.out.println(result);
return;
}
}
}
}
Версия без ответа моего ответа для кода-версии этой задачи на данный момент, только с одним незначительным изменением: java.util.Random#nextInt(limit)
используется вместо (int)(Math.random()*limit)
целого числа в диапазоне [0, n)
, поскольку это примерно в два раза быстрее .
Попробуйте онлайн.
Объяснение:
Используемый подход:
Код разбит на две части:
- Создайте список
n
количества случайных целых чисел, в которых n squared
.
- Затем он проверяет, все ли значения уникальны и не равны ли они нулю, и если любое из них ложно, он снова попытается выполнить шаг 1, промывая и повторяя, пока мы не получим результат.
Шаг 1 выполняется с помощью следующих подэтапов:
1) Создайте массив n-1
количества случайных целых чисел в диапазоне [0, n squared)
. И добавить 0
и n squared
в этот список. Это сделано в O(n+1)
исполнении.
2) Затем он будет сортировать массив с помощью встроенного модуля. java.util.Arrays.sort(int[])
Это будет сделано с точки зрения O(n*log(n))
производительности, как указано в документации:
Сортирует указанный массив целых чисел по возрастанию. Алгоритм сортировки - это настроенная быстрая сортировка, адаптированная из работ Джона Л. Бентли и М. Дугласа Макилроя «Разработка функции сортировки», Software-Practice and Experience, Vol. 23 (11) P. 1249-1265 (November 1993). Этот алгоритм обеспечивает производительность n * log (n) для многих наборов данных, которые приводят к ухудшению других быстрых сортировок до квадратичной производительности.
3) Рассчитайте разницу между каждой парой. Этот результирующий список различий будет содержать n
целые числа, которые суммируются с n squared
. Это сделано в O(n)
исполнении.
Вот пример:
// n = 4, nSquared = 16
// n-1 amount of random integers in the range [0, nSquared):
[11, 2, 5]
// Add 0 and nSquared to it, and sort:
[0, 2, 5, 11, 16]
// Calculate differences:
[2, 3, 6, 5]
// The sum of these differences will always be equal to nSquared
sum([2, 3, 6, 5]) = 16
Таким образом, эти три шага выше довольно хороши для производительности, в отличие от шага 2 и цикла вокруг всего, что является основной грубой силой. Шаг 2 разделен на следующие подэтапы:
1) Список различий уже сохранен в java.util.Set
. Он проверит, равен ли размер этого набора n
. Если это так, это означает, что все случайные значения, которые мы сгенерировали, являются уникальными.
2) И он будет также проверить , что она не содержит 0
в наборе, так как вызов просит случайные величины в диапазоне [1, X]
, где X
есть n squared
минус суммы [1, ..., n-1]
, как заявлено @Skidsdev в комментариях ниже.
Если любой из двух указанных выше вариантов (не все значения уникальны или присутствует ноль), он сгенерирует новый массив и снова установит его, сбросив на шаг 1. Это продолжается до тех пор, пока мы не получим результат. Из-за этого время может сильно отличаться. Я видел, как он закончился за 3 секунды один раз на TIO n=50
, но также и за 55 секунд один раз n=50
.
Доказательство единообразия:
Я не совсем уверен, как доказать это, чтобы быть полностью честным. Это java.util.Random#nextInt
точно, как описано в документации:
Возвращает следующее псевдослучайное, равномерно распределенное int
значение из последовательности этого генератора случайных чисел. Общий контракт nextInt
заключается в том, что одно int
значение генерируется и возвращается псевдослучайно. Все 2 32 возможных int
значения производятся с (приблизительно) равной вероятностью.
Различия между этими (отсортированными) случайными значениями, конечно, не являются одинаковыми, но наборы в целом являются однородными. Опять же, я не уверен, как это доказать математически, но вот сценарий, который поместит 10,000
сгенерированные наборы (для n=10
) в карту со счетчиком , где большинство наборов являются уникальными; некоторые повторяются дважды; и максимальное повторное вхождение обычно находится в диапазоне [4,8]
.
Инструкции по установке:
Поскольку Java является довольно известным языком с большим количеством информации о том, как создавать и запускать код Java, я буду кратким.
Все инструменты, используемые в моем коде, доступны в Java 7 (возможно, даже уже в Java 5 или 6, но давайте использовать 7 на всякий случай). Я почти уверен, что Java 7 уже заархивирована, поэтому я бы предложила загрузить Java 8 для запуска моего кода.
Мысли об улучшениях:
Я хотел бы найти улучшение для проверки на нули и проверить, что все значения уникальны. Я мог бы проверить это 0
раньше, убедившись, что случайного значения, которое мы добавляем в массив, еще нет, но это будет означать пару вещей: массив должен быть таким, ArrayList
чтобы мы могли использовать встроенный метод .contains
; цикл while следует добавлять до тех пор, пока мы не найдем случайное значение, которого еще нет в списке. Поскольку проверка на ноль теперь выполняется с .contains(0)
помощью набора (который проверяется только один раз), для производительности, скорее всего, лучше проверить его в этот момент, чем для добавления цикла с .contains
в список, который будет проверяться как минимум n
раз , но скорее всего больше.
Что касается проверки уникальности, у нас есть только n
количество случайных целых чисел, которое суммируется n squared
после шага 1 программы, поэтому только тогда мы можем проверить, все ли уникальны или нет. Может быть возможно сохранить сортируемый список вместо массива и проверить различия между ними, но я серьезно сомневаюсь, что это улучшит производительность, чем просто помещает их в a Set
и проверяет, не равен ли размер этого набора n
один раз.