Створення випадкових чисел без дублікатів


87

У цьому випадку МАКС становить лише 5, тому я міг би перевірити дублікати по одному, але як я міг зробити це простіше? Наприклад, що, якщо MAX має значення 20? Дякую.

int MAX = 5;

for (i = 1 , i <= MAX; i++)
{
        drawNum[1] = (int)(Math.random()*MAX)+1;

        while (drawNum[2] == drawNum[1])
        {
             drawNum[2] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
        {
             drawNum[3] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
        {
             drawNum[4] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[5] == drawNum[1]) ||
               (drawNum[5] == drawNum[2]) ||
               (drawNum[5] == drawNum[3]) ||
               (drawNum[5] == drawNum[4]) )
        {
             drawNum[5] = (int)(Math.random()*MAX)+1;
        }

}

3
Багато генераторів випадкових чисел (псевдо) не повторюються протягом повного "циклу". Проблема, звичайно, полягає в тому, що їх повний «цикл» становить мільярди або трильйони цінностей, і цінності, які вони виробляють, можуть бути будь-якими з цих мільярдів або трильйонів цінностей. Теоретично ви могли б створити генератор випадкових чисел, який мав би "цикл" 5 або 10 або що завгодно, але це, мабуть, більше проблем, ніж це варте.
Hot Licks

1
Також генератор випадкових чисел, який не повторюється, є навіть "менш" випадковим: якщо MAX = 5 і ви читаєте 3 числа, ви можете вгадати наступне з 50% ймовірністю, якщо ви прочитаєте 4 числа, ви знаєте наступне на 100% звичайно!
icza

Відповів на дублікат запитання тут
Alex - GlassEditor.com


Відповіді:


149

Найпростішим способом було б створити список можливих чисел (1..20 або що завгодно), а потім перетасувати їх за допомогою Collections.shuffle. Тоді просто візьміть скільки завгодно елементів. Це чудово, якщо ваш діапазон дорівнює кількості елементів, які вам потрібні в кінці (наприклад, для перемішування колоди карт).

Це не працює так добре, якщо ви хочете (скажімо) 10 випадкових елементів в діапазоні 1..10 000 - ви в кінцевому підсумку зробите багато роботи без потреби. На той момент, мабуть, краще зберегти набір значень, які ви створили до цього часу, і просто продовжуйте генерувати числа в циклі, поки наступне ще не буде:

if (max < numbersNeeded)
{
    throw new IllegalArgumentException("Can't ask for more numbers than are available");
}
Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < numbersNeeded)
{
    Integer next = rng.nextInt(max) + 1;
    // As we're adding to a set, this will automatically do a containment check
    generated.add(next);
}

Будьте обережні з вибором набору - я дуже навмисно використовував, LinkedHashSetоскільки він підтримує порядок вставки, про який ми тут дбаємо.

Ще один варіант - це завжди прогресувати, щоразу зменшуючи діапазон і компенсуючи існуючі значення. Наприклад, припустимо, ви хотіли 3 значення в діапазоні 0..9. На першій ітерації ви генеруєте будь-яке число в діапазоні 0..9 - припустимо, ви генеруєте 4.

На другій ітерації ви потім створили б число в діапазоні 0..8. Якщо сформоване число менше 4, ви збережете його як є ... інакше додасте до нього одне. Це дає вам діапазон результатів 0..9 без 4. Припустимо, ми отримаємо 7 таким чином.

На третій ітерації ви створили б число в діапазоні 0..7. Якщо сформоване число менше 4, ви збережете його як є. Якщо це 4 або 5, ви додасте один. Якщо це 6 або 7, ви додасте два. Таким чином діапазон результатів становить 0..9 без 4 або 6.


Створіть масив з можливих значень, випадковим чином виберіть його (розмір масиву мод з випадковим числом), видаліть (і збережіть) вибране число, а потім повторіть.
Hot Licks

Або використовуйте генератор випадкових випадків із повним циклом (ті, що базуються на простих числах, можуть використовувати малі простих числа - з відповідними малими циклами) і викидати значення за межі діапазону.
Paul de Vrieze,

"Ще один варіант - завжди досягати успіху" - WAAAAY краще рішення. Будь ласка, відредагуйте для роздумів. І дякую за цю чудову відповідь.
user123321

1
@musselwhizzle: Незабаром спробує знайти час. Я не впевнений у питанні "WAAAY краще", хоча - це буде значно менше "очевидно правильним", хоча це буде більш ефективно. Досить часто я радий жертвувати виставою заради читабельності.
Джон Скіт,

@Deepthi: Якого б максимуму не хотів ОП - відповідно до питання.
Джон Скіт,

19

Ось як я це зробив

import java.util.ArrayList;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        int size = 20;

        ArrayList<Integer> list = new ArrayList<Integer>(size);
        for(int i = 1; i <= size; i++) {
            list.add(i);
        }

        Random rand = new Random();
        while(list.size() > 0) {
            int index = rand.nextInt(list.size());
            System.out.println("Selected: "+list.remove(index));
        }
    }
}

Як зазначив шановний пан Скіт:
Якщо n - кількість випадково вибраних чисел, які ви хочете вибрати, а N - загальний простір вибірки чисел, доступних для вибору:

  1. Якщо n << N , вам слід просто зберегти вибрані вами номери та перевірити список, щоб перевірити, чи є в ньому вибраний номер.
  2. Якщо n ~ = N , вам, мабуть, слід скористатися моїм методом, заповнивши список, що містить весь пробір, а потім видаливши з нього числа, коли ви їх вибираєте.

список повинен бути LinkedList, видаляти випадкові індекси з списку списків дуже неефективно
Ріккардо Касатта

@RiccardoCasatta у вас є джерело для вашого твердження? Я не можу уявити, що об’їзд пов’язаного списку буде дуже ефективним. Дивіться також: stackoverflow.com/a/6103075/79450
Catchwa

Я перевірив це, і ви маєте рацію, чи слід видаляти свій коментар?
Ріккардо Касатта,

@RiccardoCasatta Інші можуть вважати наші туди-сюди корисними
Catchwa,

13
//random numbers are 0,1,2,3 
ArrayList<Integer> numbers = new ArrayList<Integer>();   
Random randomGenerator = new Random();
while (numbers.size() < 4) {

    int random = randomGenerator .nextInt(4);
    if (!numbers.contains(random)) {
        numbers.add(random);
    }
}

Це мало б жахливу продуктивність для великої кількості. ArrayList.contains перебирає список. Набагато чистішим було б замість цього мати набір - вам не потрібно перевіряти, чи він містить, просто додайте і продуктивність буде кращою.
kfox

5

Існує ще один спосіб зробити "випадкові" впорядковані числа з LFSR, погляньте на:

http://en.wikipedia.org/wiki/Linear_feedback_shift_register

за допомогою цієї техніки ви можете досягти упорядкованого випадкового числа за допомогою індексу та переконавшись, що значення не дублюються.

Але це не ІСТИНІ випадкові числа, оскільки випадкове породження є детермінованим.

Але залежно від вашого випадку ви можете використовувати цю техніку, зменшуючи обсяг обробки при генерації випадкових чисел при використанні перетасовки.

Ось алгоритм LFSR в Java, (я взяв його десь, не пам’ятаю):

public final class LFSR {
    private static final int M = 15;

    // hard-coded for 15-bits
    private static final int[] TAPS = {14, 15};

    private final boolean[] bits = new boolean[M + 1];

    public LFSR() {
        this((int)System.currentTimeMillis());
    }

    public LFSR(int seed) {
        for(int i = 0; i < M; i++) {
            bits[i] = (((1 << i) & seed) >>> i) == 1;
        }
    }

    /* generate a random int uniformly on the interval [-2^31 + 1, 2^31 - 1] */
    public short nextShort() {
        //printBits();

        // calculate the integer value from the registers
        short next = 0;
        for(int i = 0; i < M; i++) {
            next |= (bits[i] ? 1 : 0) << i;
        }

        // allow for zero without allowing for -2^31
        if (next < 0) next++;

        // calculate the last register from all the preceding
        bits[M] = false;
        for(int i = 0; i < TAPS.length; i++) {
            bits[M] ^= bits[M - TAPS[i]];
        }

        // shift all the registers
        for(int i = 0; i < M; i++) {
            bits[i] = bits[i + 1];
        }

        return next;
    }

    /** returns random double uniformly over [0, 1) */
    public double nextDouble() {
        return ((nextShort() / (Integer.MAX_VALUE + 1.0)) + 1.0) / 2.0;
    }

    /** returns random boolean */
    public boolean nextBoolean() {
        return nextShort() >= 0;
    }

    public void printBits() {
        System.out.print(bits[M] ? 1 : 0);
        System.out.print(" -> ");
        for(int i = M - 1; i >= 0; i--) {
            System.out.print(bits[i] ? 1 : 0);
        }
        System.out.println();
    }


    public static void main(String[] args) {
        LFSR rng = new LFSR();
        Vector<Short> vec = new Vector<Short>();
        for(int i = 0; i <= 32766; i++) {
            short next = rng.nextShort();
            // just testing/asserting to make 
            // sure the number doesn't repeat on a given list
            if (vec.contains(next))
                throw new RuntimeException("Index repeat: " + i);
            vec.add(next);
            System.out.println(next);
        }
    }
}

4

Інший підхід , який дозволяє визначити , скільки номерів ви хочете з sizeі minі maxзначенням повертаються чисел

public static int getRandomInt(int min, int max) {
    Random random = new Random();

    return random.nextInt((max - min) + 1) + min;
}

public static ArrayList<Integer> getRandomNonRepeatingIntegers(int size, int min,
        int max) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    while (numbers.size() < size) {
        int random = getRandomInt(min, max);

        if (!numbers.contains(random)) {
            numbers.add(random);
        }
    }

    return numbers;
}

Щоб використовувати його, повертаючи 7 чисел від 0 до 25.

    ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
    for (int i = 0; i < list.size(); i++) {
        System.out.println("" + list.get(i));
    }

4

Це було б набагато простіше у java-8:

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);

3

Цей псевдокод пояснює найефективніший, основний спосіб отримання не повторюваних випадкових чисел. Немає необхідності мати вкладені цикли або хешовані пошуки:

// get 5 unique random numbers, possible values 0 - 19
// (assume desired number of selections < number of choices)

const int POOL_SIZE = 20;
const int VAL_COUNT = 5;

declare Array mapping[POOL_SIZE];
declare Array results[VAL_COUNT];

declare i int;
declare r int;
declare max_rand int;

// create mapping array
for (i=0; i<POOL_SIZE; i++) {
   mapping[i] = i;
}

max_rand = POOL_SIZE-1;  // start loop searching for maximum value (19)

for (i=0; i<VAL_COUNT; i++) {
    r = Random(0, max_rand); // get random number
    results[i] = mapping[r]; // grab number from map array
    mapping[r] = max_rand;  // place item past range at selected location

    max_rand = max_rand - 1;  // reduce random scope by 1
}

Припустимо, перша ітерація генерує випадкове число 3 для початку (з 0 - 19). Це призведе до результатів [0] = відображення [3], тобто значення 3. Потім ми призначимо відображення [3] до 19.

У наступній ітерації випадкове число було 5 (від 0 - 18). Це призведе до результатів [1] = відображення [5], тобто значення 5. Потім ми призначимо відображення [5] до 18.

Тепер припустимо, що наступна ітерація знову обрала 3 (від 0 - 17). результатам [2] буде присвоєно значення відображення [3], але зараз це значення не 3, а 19.

Цей самий захист зберігається для всіх чисел, навіть якщо ви отримували одне і те ж число 5 разів поспіль. Наприклад, якби генератор випадкових чисел давав вам 0 п’ять разів поспіль, результати були б такими: [0, 19, 18, 17, 16].

Ви ніколи не отримаєте одне і те ж число двічі.


Я сумніваюся, що це настільки випадково, наскільки це звучить. Чи проходить він стандартні тести на випадковість ?; здавалося б, зосередити числа біля кінця спектра.
tucuxi

Ось базовий випадок. Пул - {a, b, c}. Нам потрібні 2 неповторювані елементи. За алгоритмом, ось комбінації, які ми могли б намалювати, та їх результати: 0,0: a, c 0,1: a, b 1,0: b, a 1,1: b, c 2,0: c, a 2, 1: c, b Оцінка: a-4, b-4, c-4
blackcatweb

3

Генерація всіх індексів послідовності, як правило, погана ідея, оскільки це може зайняти багато часу, особливо якщо співвідношення чисел, які слід обрати, MAXє низьким (складність стає переважною O(MAX)). Це погіршується, якщо співвідношення вибраних чисел до MAXнаближення до одиниці, оскільки тоді вилучення вибраних індексів із послідовності всіх також стає дорогим (ми підходимо O(MAX^2/2)). Але для невеликих чисел це, як правило, працює добре і не особливо схильне до помилок.

Фільтрування згенерованих індексів за допомогою колекції також є поганою ідеєю, оскільки витрачається деякий час на вставку індексів у послідовність, і прогрес не гарантується, оскільки одне і те ж випадкове число можна намалювати кілька разів (але для досить великого MAXце навряд чи ). Це може бути близьким до складності
O(k n log^2(n)/2), ігноруючи дублікати і припускаючи, що колекція використовує дерево для ефективного пошуку (але зі значними постійними витратами kна розподіл вузлів дерева і, можливо, необхідність перебалансування ).

Інший варіант - генерувати випадкові значення однозначно з самого початку, гарантуючи прогрес. Це означає, що в першому раунді [0, MAX]генерується випадковий індекс в :

items i0 i1 i2 i3 i4 i5 i6 (total 7 items)
idx 0       ^^             (index 2)

У другому раунді [0, MAX - 1]генерується лише (оскільки вже вибрано один елемент):

items i0 i1    i3 i4 i5 i6 (total 6 items)
idx 1          ^^          (index 2 out of these 6, but 3 out of the original 7)

Потім значення індексів потрібно скорегувати: якщо другий індекс потрапляє у другу половину послідовності (після першого індексу), його потрібно збільшити, щоб врахувати розрив. Ми можемо реалізувати це як цикл, що дозволяє нам вибрати довільну кількість унікальних елементів.

Для коротких послідовностей це досить швидко O(n^2/2) алгоритм:

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3187.000 msec (the fastest)
    // b2: 3734.000 msec
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        size_t n_where = i;
        for(size_t j = 0; j < i; ++ j) {
            if(n + j < rand_num[j]) {
                n_where = j;
                break;
            }
        }
        // see where it should be inserted

        rand_num.insert(rand_num.begin() + n_where, 1, n + n_where);
        // insert it in the list, maintain a sorted sequence
    }
    // tier 1 - use comparison with offset instead of increment
}

Де n_select_numтвій 5, а n_number_numтвійMAX . У n_Rand(x)повертає випадкові числа в [0, x](включно). Це можна зробити трохи швидше, якщо вибрати багато елементів (наприклад, не 5, а 500), використовуючи двійковий пошук для пошуку точки вставки. Для цього нам потрібно переконатися, що ми відповідаємо вимогам.

Ми виконаємо двійковий пошук із порівнянням, n + j < rand_num[j]яке є таким самим, як
n < rand_num[j] - j. Нам потрібно показати, що rand_num[j] - jце все ще відсортована послідовність для відсортованої послідовності rand_num[j]. На щастя, це легко показати, оскільки найменша відстань між двома елементами оригіналу rand_numодна (генеровані числа є унікальними, тому завжди є різниця щонайменше 1). У той же час, якщо відняти індекси jз усіх елементів
rand_num[j], різниці в індексі дорівнюють рівно 1. Отже, у «гіршому» випадку ми отримуємо постійну послідовність - але ніколи не зменшуючись. Тому можна використовувати двійковий пошук, отримуючи O(n log(n))алгоритм:

struct TNeedle { // in the comparison operator we need to make clear which argument is the needle and which is already in the list; we do that using the type system.
    int n;

    TNeedle(int _n)
        :n(_n)
    {}
};

class CCompareWithOffset { // custom comparison "n < rand_num[j] - j"
protected:
    std::vector<int>::iterator m_p_begin_it;

public:
    CCompareWithOffset(std::vector<int>::iterator p_begin_it)
        :m_p_begin_it(p_begin_it)
    {}

    bool operator ()(const int &r_value, TNeedle n) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return r_value < n.n + n_index; // or r_value - n_index < n.n
    }

    bool operator ()(TNeedle n, const int &r_value) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return n.n + n_index < r_value; // or n.n < r_value - n_index
    }
};

І, нарешті:

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3578.000 msec
    // b2: 1703.000 msec (the fastest)
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        std::vector<int>::iterator p_where_it = std::upper_bound(rand_num.begin(), rand_num.end(),
            TNeedle(n), CCompareWithOffset(rand_num.begin()));
        // see where it should be inserted

        rand_num.insert(p_where_it, 1, n + p_where_it - rand_num.begin());
        // insert it in the list, maintain a sorted sequence
    }
    // tier 4 - use binary search
}

Я перевірив це на трьох еталонах. Спочатку було обрано 3 числа з 7 елементів, а гістограма вибраних предметів була накопичена за 10000 прогонів:

4265 4229 4351 4267 4267 4364 4257

Це показує, що кожен із 7 пунктів був обраний приблизно однаковим числом разів, і немає явного упередження, спричиненого алгоритмом. Усі послідовності також перевірялись на правильність (унікальність змісту).

Другий орієнтир передбачав вибір 7 чисел із 5000 предметів. Час декількох версій алгоритму було накопичено понад 10 000 000 прогонів. Результати позначаються в коментарях у коді як b1. Проста версія алгоритму трохи швидша.

Третій орієнтир передбачав вибір 700 чисел із 5000 предметів. Час кількох версій алгоритму знову накопичився, цього разу понад 10000 прогонів. Результати позначаються в коментарях у коді як b2. Бінарний варіант пошуку алгоритму зараз більш ніж у два рази швидший, ніж простий.

Другий метод починає бути швидшим для вибору понад cca 75 елементів на моїй машині (зауважте, що складність будь-якого алгоритму не залежить від кількості елементів, MAX).

Варто згадати, що вищезазначені алгоритми генерують випадкові числа у порядку зростання. Але було б просто додати ще один масив, до якого числа будуть зберігатися в тому порядку, в якому вони були згенеровані, і повертати його натомість (за незначно додаткових витрат O(n)). Не потрібно перетасовувати результати: це було б набагато повільніше.

Зверніть увагу, що джерела знаходяться на C ++, у мене немає машини на моїй машині, але концепція повинна бути чіткою.

РЕДАГУВАТИ :

Для розваги я також застосував підхід, який формує список з усіма індексами
0 .. MAX, вибирає їх випадковим чином і вилучає зі списку, щоб гарантувати унікальність. Оскільки я вибрав досить високу MAX(5000), продуктивність катастрофічна:

// b1: 519515.000 msec
// b2: 20312.000 msec
std::vector<int> all_numbers(n_item_num);
std::iota(all_numbers.begin(), all_numbers.end(), 0);
// generate all the numbers

for(size_t i = 0; i < n_number_num; ++ i) {
    assert(all_numbers.size() == n_item_num - i);
    int n = n_Rand(n_item_num - i - 1);
    // get a random number

    rand_num.push_back(all_numbers[n]); // put it in the output list
    all_numbers.erase(all_numbers.begin() + n); // erase it from the input
}
// generate random numbers

Я також застосував підхід із set(колекцією C ++), яка насправді посідає друге місце за тестом b2, будучи лише приблизно на 50% повільнішою за підхід з бінарним пошуком. Це зрозуміло, оскільки setвикористовує двійкове дерево, де вартість вставки схожа на двійковий пошук. Єдина відмінність - це можливість отримати дублікати предметів, що уповільнює прогрес.

// b1: 20250.000 msec
// b2: 2296.000 msec
std::set<int> numbers;
while(numbers.size() < n_number_num)
    numbers.insert(n_Rand(n_item_num - 1)); // might have duplicates here
// generate unique random numbers

rand_num.resize(numbers.size());
std::copy(numbers.begin(), numbers.end(), rand_num.begin());
// copy the numbers from a set to a vector

Повний вихідний код тут .


2

Ви можете скористатися одним із класів, що реалізують інтерфейс Set ( API ), а потім кожне генероване число використовуйте Set.add (), щоб вставити його.

Якщо значення, що повертається, хибне, ви знаєте, що число вже було створено раніше.


2

Замість того, щоб робити все це, створіть LinkedHashSetоб'єкт і випадкові числа до нього за допомогою Math.random()функції .... якщо виникає якийсь дубльований запис, LinkedHashSetоб'єкт не додасть це число до свого Списку ... Оскільки в цьому класі колекції не допускаються повторювані значення .. зрештою u отримуємо список випадкових чисел, що не мають дубльованих значень ....: D


2

Здається, ваша проблема зводиться до випадкового вибору k елементів із колекції з n елементів. Таким чином, відповідь Collections.shuffle є правильною, але як зазначено неефективно: її O (n).

Вікіпедія: Перетасовка Фішер – Єйтс має версію O (k), коли масив уже існує. У вашому випадку немає масиву елементів, і створення масиву елементів може бути дуже дорогим, скажімо, якби макс. 10000000 замість 20.

Алгоритм перетасовки передбачає ініціалізацію масиву розміром n, де кожен елемент дорівнює своєму індексу, вибір k випадкових чисел кожного числа в діапазоні з макс. Одиницею, меншим за попередній діапазон, а потім обмін елементами до кінця масиву.

Ви можете зробити ту саму операцію за O (k) разів з хеш-картою, хоча я визнаю, що це певна проблема. Зверніть увагу, що це варто лише в тому випадку, якщо k набагато менше n. (тобто k ~ lg (n) або близько того), інакше слід використовувати перетасовку безпосередньо.

Ви будете використовувати свою хеш-карту як ефективне представлення резервного масиву в алгоритмі перетасовки. Будь-який елемент масиву, який дорівнює його індексу, не повинен відображатися на карті. Це дозволяє представляти масив розміром n у постійний час, не витрачається часу на його ініціалізацію.

  1. Виберіть k випадкових чисел: перше знаходиться в діапазоні від 0 до n-1, друге від 0 до n-2, третє від 0 до n-3 і так далі, через nk.

  2. Ставтесь до своїх випадкових чисел як до набору обмінів. Перший випадковий індекс переходить у кінцеву позицію. Другий випадковий індекс переходить на другу з останньої позиції. Однак, замість того, щоб працювати проти резервного масиву, працюйте проти своєї хеш-карти. У вашій хеш-карті буде зберігатися кожен предмет, який не знаходиться в позиції.

int getValue(i) { if (map.contains(i)) return map[i]; return i; } void setValue(i, val) { if (i == val) map.remove(i); else map[i] = val; } int[] chooseK(int n, int k) { for (int i = 0; i < k; i++) { int randomIndex = nextRandom(0, n - i); //(n - i is exclusive) int desiredIndex = n-i-1; int valAtRandom = getValue(randomIndex); int valAtDesired = getValue(desiredIndex); setValue(desiredIndex, valAtRandom); setValue(randomIndex, valAtDesired); } int[] output = new int[k]; for (int i = 0; i < k; i++) { output[i] = (getValue(n-i-1)); } return output; }


creating the array of elements could be very expensive- чому створення масиву має бути дорожчим за перетасовку? Я думаю, що в цьому пункті немає абсолютно ніяких причин для песимізму :-)
Вовк,

1

Наступний код створює послідовність випадкових чисел між [1, m], які раніше не створювались.

public class NewClass {

    public List<Integer> keys = new ArrayList<Integer>();

    public int rand(int m) {
        int n = (int) (Math.random() * m + 1);
        if (!keys.contains(n)) {
            keys.add(n);
            return n;
        } else {
            return rand(m);
        }
    }

    public static void main(String[] args) {
        int m = 4;
        NewClass ne = new NewClass();
        for (int i = 0; i < 4; i++) {
            System.out.println(ne.rand(m));
        }
        System.out.println("list: " + ne.keys);
    }
}

0

Існує алгоритм партійних карток: ви створюєте впорядкований масив чисел ("партія карт") і на кожній ітерації вибираєте з нього число у випадковому положенні (звичайно, видаляючи вибраний номер із "партії карт").


0

Ось ефективне рішення для швидкого створення рандомізованого масиву. Після рандомізації ви можете просто вибрати n-й елемент eмасиву, збільшити nі повернути e. Це рішення має O (1) для отримання випадкового числа та O (n) для ініціалізації, але оскільки компроміс вимагає великого обсягу пам'яті, якщо n стає достатньо великим.


0

Існує більш ефективне та менш громіздке рішення для цілих чисел, ніж Collections.shuffle.

Проблема така ж, як послідовний вибір предметів лише з невідібраних предметів у наборі та впорядкування їх де-небудь ще. Це точно так само, як випадкові роздачі карт або розіграш виграшних лотерейних квитків з капелюха чи смітника.

Цей алгоритм працює для завантаження будь-якого масиву та досягнення випадкового порядку в кінці завантаження. Він також працює для додавання до колекції списків (або будь-якої іншої індексованої колекції) та досягнення випадкової послідовності в колекції в кінці додавання.

Це можна зробити за допомогою одного масиву, створеного один раз, або на місці чисельно упорядкованого колекціонування, такого як Список. Для масиву початковий розмір масиву повинен бути точним, щоб містити всі передбачені значення. Якщо ви не знаєте, скільки значень може виникнути заздалегідь, також працюватиме впорядкована за чисельністю колекція, наприклад ArrayList або List, де розмір не є незмінним, також буде працювати. Це буде працювати універсально для масиву будь-якого розміру до Integer.MAX_VALUE, який становить трохи більше 2 000 000 000. Об'єкти списку матимуть однакові обмеження індексу. У вашої машини може закінчитися пам’ять, перш ніж ви потрапите до масиву такого розміру. Можливо, буде ефективніше завантажити масив, набраний до типів об’єктів, і перетворити його в якусь колекцію після завантаження масиву. Це особливо вірно, якщо цільова колекція не є числовим індексом.

Цей алгоритм, точно як написано, створить дуже рівномірний розподіл там, де немає дублікатів. ДУЖЕ ВАЖЛИВИЙ аспект полягає в тому, що вставка наступного елемента повинна мати місце до поточного розміру + 1. Таким чином, для другого елемента можна зберігати його в місці 0 або розташуванні 1 Для 20-го предмета його можна зберігати в будь-якому місці, від 0 до 19. Це точно так само, як перший предмет залишається в місці 0, як і для того, щоб він опинився в будь-якому іншому місці. Можливо, наступний новий предмет може перейти куди завгодно, включаючи наступне нове місце.

Випадковість послідовності буде такою самою випадковою, як і випадковість генератора випадкових чисел.

Цей алгоритм також може бути використаний для завантаження посилальних типів у випадкові розташування масиву. Оскільки це працює з масивом, воно може працювати і з колекціями. Це означає, що вам не потрібно створювати колекцію, а потім перетасовувати її або замовляти за будь-якими замовленнями для вставлених об’єктів. Колекція має лише можливість вставити предмет у будь-яке місце колекції або додати його.

// RandomSequence.java
import java.util.Random;
public class RandomSequence {

    public static void main(String[] args) {
        // create an array of the size and type for which
        // you want a random sequence
        int[] randomSequence = new int[20];
        Random randomNumbers = new Random();

        for (int i = 0; i < randomSequence.length; i++ ) {
            if (i == 0) { // seed first entry in array with item 0
                randomSequence[i] = 0; 
            } else { // for all other items...
                // choose a random pointer to the segment of the
                // array already containing items
                int pointer = randomNumbers.nextInt(i + 1);
                randomSequence[i] = randomSequence[pointer]; 
                randomSequence[pointer] = i;
                // note that if pointer & i are equal
                // the new value will just go into location i and possibly stay there
                // this is VERY IMPORTANT to ensure the sequence is really random
                // and not biased
            } // end if...else
        } // end for
        for (int number: randomSequence) {
                System.out.printf("%2d ", number);
        } // end for
    } // end main
} // end class RandomSequence

0

Насправді все залежить від того, ЩО саме для чого вам потрібно випадкове покоління, але ось моя думка.

Спочатку створіть самостійний метод генерації випадкового числа. Обов’язково враховуйте обмеження.

public static int newRandom(int limit){
    return generatedRandom.nextInt(limit);  }

Далі ви захочете створити дуже просту структуру прийняття рішень, яка порівнює значення. Це можна зробити одним із двох способів. Якщо у вас є дуже обмежена кількість чисел для перевірки, буде достатньо простого твердження IF:

public static int testDuplicates(int int1, int int2, int int3, int int4, int int5){
    boolean loopFlag = true;
    while(loopFlag == true){
        if(int1 == int2 || int1 == int3 || int1 == int4 || int1 == int5 || int1 == 0){
            int1 = newRandom(75);
            loopFlag = true;    }
        else{
            loopFlag = false;   }}
    return int1;    }

Вище порівнюється int1 з int2 через int5, а також переконуються, що в рандомах немає нулів.

За допомогою цих двох методів ми можемо зробити наступне:

    num1 = newRandom(limit1);
    num2 = newRandom(limit1);
    num3 = newRandom(limit1);
    num4 = newRandom(limit1);
    num5 = newRandom(limit1);

Дотримується:

        num1 = testDuplicates(num1, num2, num3, num4, num5);
        num2 = testDuplicates(num2, num1, num3, num4, num5);
        num3 = testDuplicates(num3, num1, num2, num4, num5);
        num4 = testDuplicates(num4, num1, num2, num3, num5);
        num5 = testDuplicates(num5, num1, num2, num3, num5);

Якщо у вас є довший список для перевірки, то більш складний метод дасть кращі результати як у чіткості коду, так і в обробці ресурсів.

Сподіваюся, це допомагає. Цей сайт мені настільки допоміг, що я відчув себе зобов’язаним принаймні Спробувати також допомогти.



0

Найпростіший спосіб - використовувати nano DateTime як довгий формат. System.nanoTime ();

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.