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))
виконанні, як зазначено в документах:
Сортує вказаний масив вкладень у порядку зростання. Алгоритм сортування - це налаштований квакспорт, адаптований з Джона Л. Бентлі та М. Дугласа Макілроя «Інженерія функції сортування», Програмне забезпечення та практика, Vol. 23 (11) С. 1249-1265 (листопад 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) Список відмінностей уже збережений у a 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
; цикл у той час повинен бути доданий до тих пір, поки ми не знайдемо випадкове значення, якого ще немає у списку. Оскільки перевірка на нуль зараз робиться .contains(0)
за допомогою набору (що перевіряється лише один раз), то, швидше за все, для продуктивності краще перевірити його в той момент, порівняно з додаванням циклу .contains
в списку, який перевірятиметься хоча б n
раз , але, швидше за все, більше.
Що стосується перевірки унікальності, то у нас є лише наша n
кількість випадкових цілих чисел, яка дорівнює n squared
після кроку 1 програми, тому лише тоді ми можемо перевірити, чи всі вони унікальні чи ні. Можливо, можливо, зберегти сортований Список замість масиву та перевірити відмінності між ними, але я серйозно сумніваюся, що це покращить ефективність, ніж просто поставити їх у Set
та перевірити, чи розмір цього набору є n
один раз.