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один раз.