Tworzenie liczb losowych bez duplikatów

W tym przypadku MAX wynosi tylko 5, więc mógłbym sprawdzić duplikaty jeden po drugim, ale jak mógłbym to zrobić w prostszy sposób? Na przykład, co jeśli MAX ma wartość 20? Dzięki.

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;
        }

}
 72
Author: Bobby, 2010-10-28

15 answers

Najprostszym sposobem byłoby utworzenie listy możliwych liczb (1..20 lub cokolwiek), a następnie przetasuj je Collections.shuffle. Następnie po prostu weź tyle elementów, ile chcesz. Jest to świetne rozwiązanie, jeśli twój zakres jest równy liczbie elementów potrzebnych na końcu (np. do tasowania talii kart).

To nie działa tak dobrze, jeśli chcesz (powiedzmy) 10 losowych elementów w zakresie 1..10,000 - skończyłbyś niepotrzebnie wykonując dużo pracy. W tym momencie, prawdopodobnie lepiej zachować zestaw wartości, które do tej pory wygenerowałeś, i po prostu Generuj liczby w pętli, aż następna nie będzie już obecna:

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);
}

Bądź jednak ostrożny z wyborem zestawu-celowo użyłem LinkedHashSet, ponieważ utrzymuje on kolejność wstawiania, na której nam tutaj zależy.

Jeszcze inną opcją jest zawsze robić postępy, zmniejszając Zakres za każdym razem i kompensując istniejące wartości. Załóżmy na przykład, że chcesz 3 wartości z zakresu 0..9. Na pierwszej iteracji można wygenerować dowolną liczbę z zakresu 0..9 - powiedzmy, że generujesz 4.

W drugiej iteracji wygenerowałbyś liczbę z zakresu 0..8. Jeśli wygenerowana liczba jest mniejsza niż 4, zachowasz ją tak, jak jest... w przeciwnym razie dodasz jeden do niego. To daje Ci zakres wyników 0..9 Bez 4. Załóżmy, że mamy 7 w ten sposób.

W trzeciej iteracji wygenerowałbyś liczbę z zakresu 0..7. Jeśli wygenerowana liczba jest mniejsza niż 4, zachowasz ją tak, jak jest. Jeśli jest 4 lub 5, dodasz jedną. Jeśli jest 6 lub 7, dodałbyś dwa. W ten sposób zakres wyniku wynosi 0..9 Bez 4 lub 6.

 133
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-18 09:50:38

Oto jak bym to zrobił

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));
        }
    }
}

Jak zauważył Szanowny Pan Skeet:
Jeśli n jest liczbą losowo wybranych liczb, które chcesz wybrać, a N jest całkowitą przestrzenią przykładową liczb dostępnych do wyboru:

  1. If n N , należy po prostu zapisać wybrane liczby i sprawdzić listę, aby sprawdzić, czy wybrana liczba jest na niej.
  2. If n ~= N , prawdopodobnie powinieneś użyć mojej metody, przez wypełnianie listy zawierającej całą przestrzeń próbki, a następnie usuwanie z niej liczb podczas ich wybierania.
 18
Author: Catchwa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 05:42:43
//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);
    }
}
 11
Author: Satheesh Guduri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-01 20:00:12

Istnieje inny sposób wykonywania" losowych " liczb uporządkowanych z LFSR, spójrz na:

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

Dzięki tej technice można uzyskać uporządkowaną liczbę losową według indeksu i upewnić się, że wartości nie są powielane.

Nie są to jednak prawdziwe liczby losowe, ponieważ generacja losowa jest deterministyczna.

Ale w zależności od przypadku możesz użyć tej techniki zmniejszając ilość przetwarzania przy generowaniu liczb losowych podczas tasowania.

Tutaj algorytm LFSR w Javie, (wziąłem go gdzieś, czego nie pamiętam):

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);
        }
    }
}
 3
Author: felipe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-24 14:54:49

Najbardziej efektywny, podstawowy sposób, aby mieć nie powtarzające się liczby losowe, jest wyjaśniony przez ten pseudo-kod. Nie ma potrzeby tworzenia zagnieżdżonych pętli ani haszowanych lookupów:

// 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
}

Załóżmy, że pierwsza iteracja wygeneruje losową liczbę 3 na początek (od 0 do 19). To daje wyniki[0] = mapowanie [3], tzn. wartość 3. Przypisalibyśmy wtedy mapowanie [3] do 19.

W następnej iteracji losowa liczba wynosiła 5 (od 0 do 18). Daje to Wyniki[1] = mapowanie [5], tzn. wartość 5. We ' d następnie przypisać mapowanie [5] do 18.

Przypuśćmy, że następna iteracja ponownie wybrała 3 (z 0 - 17). wyniki [2] byłyby przypisane do wartości mapowania [3], ale teraz ta wartość nie jest 3, ale 19.

Ta sama ochrona obowiązuje dla wszystkich liczb, nawet jeśli masz tę samą liczbę 5 razy z rzędu. Na przykład, jeśli generator liczb losowych daje 0 pięć razy z rzędu, wyniki będą: [ 0, 19, 18, 17, 16 ].

Nigdy nie dostaniesz tej samej liczby dwa razy.
 3
Author: blackcatweb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-01 20:28:14

Generowanie wszystkich indeksów sekwencji jest generalnie złym pomysłem, ponieważ może to zająć dużo czasu, zwłaszcza jeśli stosunek liczb do MAX jest niski (złożoność zostaje zdominowana przez O(MAX)). Sytuacja pogarsza się, gdy stosunek liczb do MAX zbliża się do jednej, ponieważ wtedy usunięcie wybranych indeksów z sekwencji wszystkich również staje się kosztowne (zbliżamy się do O(MAX^2/2)). Ale dla małych liczb, to na ogół działa dobrze i nie jest szczególnie podatne na błędy.

Filtrowanie wygenerowanych indeksów za pomocą zbioru jest również złym pomysłem, ponieważ trochę czasu poświęca się na wstawianie indeksów do sekwencji, a postęp nie jest gwarantowany, ponieważ ta sama liczba losowa może być narysowana kilka razy (ale dla wystarczająco dużych MAX jest to mało prawdopodobne). Może to być bliskie złożoności
O(k n log^2(n)/2), ignorując duplikaty i zakładając, że kolekcja używa drzewa do efektywnego wyszukiwania (ale ze znacznym stałym kosztem k alokacji drzewa węzły i ewentualnie konieczności rebalance ).

Inną opcją jest wygenerowanie losowych wartości od początku, gwarantując postęp. Oznacza to, że w pierwszej rundzie generowany jest losowy indeks w [0, MAX]:

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

W drugiej rundzie generowane jest tylko [0, MAX - 1] (jako że jeden element został już wybrany):

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)

Wartości indeksów należy następnie skorygować: jeśli drugi indeks przypada w drugiej połowie sekwencji (po pierwszy indeks), należy go zwiększyć, aby uwzględnić lukę. Możemy to zaimplementować jako pętlę, pozwalając nam wybrać dowolną liczbę unikalnych elementów.

Dla krótkich sekwencji jest to dość szybkie O(n^2/2) algorytm:

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
}

Gdzie n_select_num jest twoje 5 i n_number_num jest twoje MAX. n_Rand(x) zwraca losowe liczby całkowite w [0, x] (włącznie). Można to zrobić nieco szybciej, wybierając wiele elementów (np. nie 5, ale 500) za pomocą wyszukiwania binarnego, aby znaleźć punkt wstawiania. Aby to zrobić, musimy upewnić się, że spełniamy wymagania.

Wykonamy wyszukiwanie binarne z porównaniem n + j < rand_num[j], które jest takie samo jak
n < rand_num[j] - j. Musimy pokazać, że rand_num[j] - j jest nadal uporządkowaną sekwencją dla uporządkowanej sekwencji rand_num[j]. Jest to na szczęście łatwo pokazane, ponieważ najniższa odległość między dwoma elementami oryginału rand_num jest jedna (wygenerowane liczby są unikalne, więc zawsze jest różnica co najmniej 1). Jednocześnie, jeśli odejmujemy indeksy j od wszystkich elementy
rand_num[j], różnice w indeksie wynoszą dokładnie 1. Tak więc w "najgorszym" przypadku otrzymujemy ciąg stały - ale nigdy nie malejący. Można więc użyć wyszukiwania binarnego, uzyskując algorytm 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
    }
};

I wreszcie:

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
}

Przetestowałem to na trzech benchmarkach. W 2007 roku, po raz pierwszy w historii serii, wybrano 3 liczby z 7 pozycji, a histogram wybranych pozycji został zgromadzony ponad 10 000 przebiegów.]}

4265 4229 4351 4267 4267 4364 4257

To pokazuje, że każdy z 7 elementów został wybrany w przybliżeniu ta sama liczba razy i nie ma widocznego błędu spowodowanego przez algorytm. Wszystkie sekwencje zostały również sprawdzone pod kątem poprawności (unikalności treści).

Drugi benchmark polegał na wybraniu 7 liczb z 5000 pozycji. Czas kilku wersji algorytmu został skumulowany ponad 10 000 000 uruchomień. Wyniki są oznaczone w komentarzach w kodzie jako b1. Prosta wersja algorytmu jest nieco szybsza.

Trzeci benchmark polegał na wybraniu 700 liczb z 5000 pozycji. Ponownie zgromadzono czas kilku wersji algorytmu, tym razem ponad 10 000 uruchomień. Wyniki są oznaczone w komentarzach w kodzie jako b2. Binarna wersja algorytmu wyszukiwania jest teraz ponad dwa razy szybsza niż prosta.

Druga metoda zaczyna być szybsza dla wybrania więcej niż cca 75 pozycji na mojej maszynie(zauważ, że złożoność każdego z algorytmów nie zależy od liczby pozycji, MAX).

Warto wspominając, że powyższe algorytmy generują liczby losowe w porządku rosnącym. Ale łatwo byłoby dodać kolejną tablicę, do której liczby byłyby zapisywane w kolejności, w jakiej zostały wygenerowane ,i zwrócić ją zamiast tego (przy znikomym dodatkowym koszcie O(n)). Nie ma potrzeby przetasowywania wyjścia: byłoby to znacznie wolniejsze.

Zauważ, że źródła są w C++, nie mam Javy na moim komputerze, ale koncepcja powinna być czysto.

EDIT :

Dla rozrywki zaimplementowałem również podejście, które generuje listę ze wszystkimi indeksami
0 .. MAX, wybiera je losowo i usuwa z listy, aby zagwarantować wyjątkowość. Ponieważ wybrałem dość wysoki MAX (5000), wydajność jest katastrofalna: {]}

// 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

Zaimplementowałem również podejście z set (zbiór C++), który w rzeczywistości zajmuje drugie miejsce na benchmarku b2, będąc tylko około 50% wolniejszym od podejście z wyszukiwania binarnego. Jest to zrozumiałe, ponieważ set używa drzewa binarnego, gdzie koszt wstawiania jest podobny do wyszukiwania binarnego. Jedyną różnicą jest szansa na uzyskanie duplikatów przedmiotów, co spowalnia postęp.

// 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

Pełny kod źródłowy to tutaj .

 3
Author: the swine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-17 11:59:50

Inne podejście, które pozwala określić, ile liczb chcesz z size i min i max wartości zwracanych liczb

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;
}

Aby użyć go zwracając 7 liczb z zakresu od 0 do 25.

    ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
    for (int i = 0; i < list.size(); i++) {
        System.out.println("" + list.get(i));
    }
 3
Author: Carlo Rodríguez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-18 20:38:00

Możesz użyć jednej z klas implementujących interfejs Set (API), a następnie każdą wygenerowaną liczbę użyj Set.add () to insert it.

Jeśli zwracana wartość jest false, to wiesz, że liczba została już wcześniej wygenerowana.

 2
Author: SSTwinrova,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 05:39:42

Zamiast robić to wszystko, Utwórz obiekt LinkedHashSet i losowe liczby do niego za pomocą funkcji Math.random().... jeśli wystąpi jakakolwiek zduplikowana pozycja, obiekt LinkedHashSet nie doda tej liczby do swojej listy ... Ponieważ w tej klasie Collection nie są dozwolone zduplikowane wartości .. w końcu otrzymujemy listę liczb losowych, które nie mają zduplikowanych wartości .... : D

 2
Author: abdeali chandan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-11 15:09:13

Twój problem wydaje się zmniejszać, aby wybrać elementy K losowo z zbioru N elementów. Kolekcje.odpowiedź shuffle jest więc poprawna, ale jak zaznaczono nieefektywna: jej O (n).

Wikipedia: Fisher-Yates shuffle ma wersję O (k), gdy tablica już istnieje. W Twoim przypadku, nie ma tablicy elementów i tworzenie tablicy elementów może być bardzo kosztowne, powiedzmy, gdyby Max były 10000000 zamiast 20.

Algorytm shuffle polega na inicjalizacja tablicy o rozmiarze n, gdzie każdy element jest równy swojemu indeksowi, wybieranie K liczb losowych każdej liczby w zakresie o maksymalnie jeden mniejszy od poprzedniego zakresu, a następnie Zamiana elementów na koniec tablicy.

Można zrobić tę samą operację w O (k) czasie z hashmap chociaż przyznaję, że to rodzaj bólu. Zauważ, że jest to opłacalne tylko wtedy, gdy k jest znacznie mniejsza niż N.(tj. k ~ lg (N) lub tak), w przeciwnym razie powinieneś użyć shuffle bezpośrednio.

Użyjesz swojego hashmap jako efektywna reprezentacja tablicy pomocniczej w algorytmie shuffle. Żaden element tablicy, który jest równy jej indeksowi, nie musi pojawiać się na mapie. Pozwala to na reprezentowanie tablicy o rozmiarze n w stałym czasie, nie ma czasu poświęconego na jej inicjalizację.

  1. Wybierz K liczb losowych: pierwsza jest w zakresie od 0 do n-1, Druga 0 do n-2, trzecia 0 do n-3 i tak dalej, przez n-K.

  2. Traktuj swoje losowe liczby jako zestaw swapów. Na pierwszy losowy indeks zamienia się na ostateczną pozycję. Drugi indeks losowy zamienia się na przedostatnią pozycję. Jednak zamiast działać przeciwko tablicy pomocniczej, pracuj przeciwko swojej hashmapie. Twoja hashmap przechowa każdy element, który jest poza pozycją.


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;
}

 2
Author: dhakim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-15 14:15:17

Istnieje algorytm partii kart: tworzysz uporządkowaną tablicę liczb ("partia kart") i w każdej iteracji wybierasz z niej liczbę w losowej pozycji (oczywiście usuwając wybraną liczbę z "partii kart").

 0
Author: Lavir the Whiolet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 05:30:34

Istnieje bardziej wydajne i mniej uciążliwe rozwiązanie dla liczb całkowitych niż zbiór.przetasuj.

Problem jest taki sam, jak sukcesywne wybieranie elementów tylko z niezbranych elementów w zestawie i ustawianie ich w kolejności gdzie indziej. Jest to dokładnie tak, jak losowanie kart lub losowanie zwycięskich kuponów z kapelusza lub kosza.

Ten algorytm działa do ładowania dowolnej tablicy i osiągnięcia losowej kolejności na końcu obciążenia. Działa również do dodawania do listy zbiór (lub dowolny inny zbiór indeksowany) i uzyskanie losowej sekwencji w zbiorze na końcu dodawania.

Można to zrobić z pojedynczą tablicą, utworzoną raz, lub uporządkowaną numerycznie kolekcją, taką jak lista, w miejscu. Dla tablicy, początkowy rozmiar tablicy musi być dokładnym rozmiarem, aby zawierał wszystkie zamierzone wartości. Jeśli nie wiesz, ile wartości może wystąpić z góry, użyj kolekcji numerycznie uporządkowanej, takiej jak ArrayList lub List, gdzie rozmiar nie jest niezmienny, będzie również działać. Będzie działać uniwersalnie dla tablicy o dowolnym rozmiarze do liczby całkowitej.MAX_VALUE czyli nieco ponad 2.000.000.000. Obiekty listy będą miały takie same limity indeksów. Na twoim komputerze może zabraknąć pamięci, zanim dotrzesz do tablicy o takim rozmiarze. Bardziej wydajne może być załadowanie tablicy wpisanej do typów obiektów i przekonwertowanie jej do pewnej kolekcji po załadowaniu tablicy. Jest to szczególnie ważne, jeśli zbiór docelowy nie jest indeksowany numerycznie.

Ten algorytm, dokładnie tak jak napisano, stworzy bardzo równą dystrybucję, w której nie ma duplikatów. Jednym z aspektów, który jest bardzo ważny, jest to, że musi być możliwe, aby wstawianie następnego elementu miało miejsce do bieżącego rozmiaru + 1. Tak więc, dla drugiego elementu, możliwe jest przechowywanie go w lokalizacji 0 lub Lokalizacji 1. W przypadku 20 pozycji można go przechowywać w dowolnym miejscu, od 0 do 19. Jest to tak samo możliwe, że pierwszy element pozostanie w lokalizacji 0, Jak to jest, aby skończyć w każdej innej lokalizacji. Jest tak samo możliwe, aby następny nowy przedmiot poszedł w dowolnym miejscu, w tym w następnej nowej lokalizacji.

Losowość sekwencji będzie tak samo losowa jak losowość generatora liczb losowych.

Ten algorytm może być również używany do ładowania typów referencyjnych w losowych lokalizacjach w tablicy. Ponieważ działa z tablicą, może również pracować z kolekcjami. Oznacza to, że nie musisz tworzyć kolekcji, a następnie tasować ją lub zamawiać na dowolne zamówienia obiektów włożenie. Kolekcja musi mieć tylko możliwość wstawiania elementu w dowolnym miejscu kolekcji lub dołączania go.

// 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
Author: Jim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-01 02:14:03

Naprawdę wszystko zależy od tego, do czego potrzebujesz losowego pokolenia, ale oto moje zdanie.

Najpierw Utwórz samodzielną metodę generowania liczby losowej. Pamiętaj, aby dopuszczać limity.

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

Następnie będziesz chciał stworzyć bardzo prostą strukturę decyzyjną, która porównuje wartości. Można to zrobić na dwa sposoby. Jeśli masz bardzo ograniczoną liczbę liczb do sprawdzenia, wystarczy proste polecenie 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;    }

Powyższe porównuje int1 do int2 poprzez int5, a także upewniając się, że w randomach nie ma zer.

Z tymi dwiema metodami możemy wykonać następujące czynności:

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

, Po Którym Następuje:

        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);

Jeśli masz dłuższą listę do sprawdzenia, bardziej złożona metoda przyniesie lepsze wyniki zarówno w przejrzystości kodu, jak i w przetwarzaniu zasobów.

Mam nadzieję, że to pomoże. Ta strona bardzo mi pomogła, czułem się zobowiązany przynajmniej spróbować pomóc.
 0
Author: AzerDraco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-18 23:46:18

Byłoby to o wiele prostsze w java-8:

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);
 0
Author: Eugene,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-09 20:39:38