कोई डुप्लिकेट के साथ यादृच्छिक संख्या बनाना


87

इस मामले में, MAX केवल 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 या जो कुछ भी "चक्र" था, लेकिन यह संभवतः इसके लायक होने की तुलना में अधिक परेशानी है।
हॉट ऑक्ट्स

1
इसके अलावा एक यादृच्छिक संख्या जनरेटर जो दोहराता नहीं है वह "कम" भी है: यदि MAX = 5 और आप 3 नंबर पढ़ते हैं, तो आप 50% संभावना के साथ अगले का अनुमान लगा सकते हैं, यदि आप 4 नंबर पढ़ते हैं, तो आप अगले को 100% जानते हैं ज़रूर!
आइकिया

डुप्लिकेट प्रश्न पर उत्तर यहाँ
एलेक्स - GlassEditor.com


जवाबों:


149

सबसे सरल तरीका संभव संख्याओं की सूची बनाना होगा (1..20 या जो भी) और फिर उनके साथ फेरबदल करें Collections.shuffle। फिर बस आप चाहते हैं कि कई तत्वों को ले लो। यह महान है यदि आपकी सीमा अंत में आपकी आवश्यकता वाले तत्वों की संख्या के बराबर है (उदाहरण के लिए कार्ड के डेक को फेरबदल करने के लिए)।

यदि आप (१०) श्रेणी के १० यादृच्छिक तत्वों को १.१०,००० में चाहते हैं तो यह इतनी अच्छी तरह से काम नहीं करता है - आप बहुत सारा काम अनावश्यक रूप से करेंगे। उस बिंदु पर, आपके द्वारा अब तक उत्पन्न किए गए मानों का एक सेट रखना बेहतर है, और बस एक लूप में संख्या उत्पन्न करना जारी रखें जब तक कि अगला मौजूद न हो:

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क्योंकि यह प्रविष्टि क्रम को बनाए रखता है, जिसे हम यहां ध्यान रखते हैं।

फिर भी एक और विकल्प हमेशा प्रगति करना है, प्रत्येक बार सीमा को कम करके और मौजूदा मूल्यों के लिए क्षतिपूर्ति करना। इसलिए, उदाहरण के लिए, मान लीजिए कि आप 0..9 की सीमा में 3 मान चाहते हैं। पहली पुनरावृत्ति पर आप 0..9 की सीमा में कोई भी संख्या उत्पन्न करेंगे - मान लें कि आप 4 उत्पन्न करते हैं।

दूसरे पुनरावृति पर आप 0..8 की सीमा में एक संख्या उत्पन्न करेंगे। यदि उत्पन्न संख्या 4 से कम है, तो आप इसे वैसे ही रखेंगे ... अन्यथा आप इसमें एक जोड़ देंगे। आपको बिना रिजल्ट के 0..9 का रिजल्ट मिलता है। मान लीजिए कि हमें इस तरह 7 मिलते हैं।

तीसरे पुनरावृत्ति पर आप 0..7 की सीमा में एक संख्या उत्पन्न करेंगे। यदि उत्पन्न संख्या 4 से कम है, तो आप इसे वैसे ही रखेंगे। यदि यह 4 या 5 है, तो आप एक जोड़ देंगे। यदि यह 6 या 7 है, तो आप दो जोड़ेंगे। इस तरह रिजल्ट रेंज 4 या 6 के बिना 0..9 है।


संभावित मानों का एक सरणी उत्पन्न करें, यादृच्छिक रूप से एक (यादृच्छिक संख्या मॉड सरणी आकार) का चयन करें, चयनित संख्या को हटाएं (और सहेजें), फिर दोहराएं।
हॉट लिक्स

या एक पूर्ण चक्र के साथ एक यादृच्छिक जनरेटर का उपयोग करें (जो कि प्राइम्स पर आधारित छोटे प्राइमर - संबंधित छोटे चक्रों के साथ) का उपयोग कर सकते हैं और मानों को सीमा से बाहर छोड़ सकते हैं।
पॉल डी व्रीज़ 19

"फिर भी एक और विकल्प हमेशा प्रगति करना है" एक समाधान के लिए WAAAAY बेहतर है। कृपया प्रतिबिंबित करने के लिए संपादित करें। और इस भयानक जवाब के लिए धन्यवाद।
user123321

1
@musselwhizzle: जल्द ही कुछ समय निकालने की कोशिश करेंगे। मैं "WAAAY बेहतर" के बारे में निश्चित नहीं हूं, हालांकि - यह बहुत कम "स्पष्ट रूप से सही" होने जा रहा है, हालांकि यह अधिक कुशल होगा। काफी अक्सर मैं पठनीयता के लिए प्रदर्शन का बलिदान करने के लिए खुश हूँ।
जॉन स्कीट

@ दीप्ति: ओपी जो चाहे अधिकतम - सवाल के अनुसार।
जॉन स्कीट

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 , तो आपको संभवतः मेरे तरीके का उपयोग करना चाहिए, संपूर्ण नमूना स्थान वाली सूची को पॉप्युलेट करके और फिर उसमें से संख्याओं को हटाते हुए उन्हें चुनें।

सूची एक लिंक्डलिस्ट होनी चाहिए,
सरणी

@RiccardoCasatta क्या आपके पास अपने दावे के लिए एक स्रोत है? मैं कल्पना नहीं कर सकता कि एक लिंक की गई सूची को ट्रेस करना बहुत अच्छा होगा या तो। इसे भी देखें: stackoverflow.com/a/6103075/79450
कैचवा

मैंने इसका परीक्षण किया और आप सही हैं, क्या मुझे अपनी टिप्पणी हटा देनी चाहिए?
रिकार्डो कैसट्टा

@RiccardoCasatta अन्य हमारे पीछे और उपयोगी उपयोगी पा सकते हैं
कैचवा

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

इस तकनीक के साथ आप अनुक्रमित द्वारा क्रमबद्ध यादृच्छिक संख्या प्राप्त कर सकते हैं और यह सुनिश्चित कर सकते हैं कि मान डुप्लिकेट नहीं हैं।

लेकिन ये TRUE यादृच्छिक संख्याएँ नहीं हैं क्योंकि यादृच्छिक पीढ़ी नियतात्मक है।

लेकिन अपने मामले के आधार पर आप फेरबदल का उपयोग करते समय यादृच्छिक संख्या पीढ़ी पर प्रसंस्करण की मात्रा को कम करने वाली इस तकनीक का उपयोग कर सकते हैं।

यहाँ जावा में एक LFSR एल्गोरिथ्म, (मैं इसे कहीं ले गया जो मुझे याद नहीं है):

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

इसका उपयोग करने के लिए 0 और 25 के बीच 7 नंबर लौटाते हैं।

    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, 19, 18, 17, 16]।

आपको कभी भी एक ही नंबर दो बार नहीं मिलेगा।


मुझे संदेह है कि यह उतना ही यादृच्छिक है जितना कि आप इसे ध्वनि बनाते हैं। क्या यह मानक यादृच्छिकता परीक्षण पास करता है ?; यह स्पेक्ट्रम के अंत के पास संख्याओं को केंद्रित करता प्रतीत होगा।
टक्सीक्सी

यहाँ एक आधार मामला है। ताल {ए, बी, सी} है। हमें 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आपका है MAXn_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
}

मैंने तीन बेंचमार्क पर इसका परीक्षण किया है। सबसे पहले, 7 आइटमों में से 3 नंबर चुने गए थे, और चुनी गई वस्तुओं का एक हिस्टोग्राम 10,000 रन से अधिक जमा हुआ था:

4265 4229 4351 4267 4267 4364 4257

इससे पता चलता है कि 7 वस्तुओं में से प्रत्येक को लगभग एक ही समय में चुना गया था, और एल्गोरिथ्म के कारण कोई स्पष्ट पूर्वाग्रह नहीं है। सभी अनुक्रमों की शुद्धता (सामग्री की विशिष्टता) के लिए भी जाँच की गई थी।

दूसरे बेंचमार्क में 5000 वस्तुओं में से 7 नंबर चुनना शामिल था। एल्गोरिथ्म के कई संस्करणों का समय 10,000,000 रनों से अधिक जमा हुआ था। परिणामों को कोड में टिप्पणियों के रूप में दर्शाया जाता है b1। एल्गोरिथ्म का सरल संस्करण थोड़ा तेज है।

तीसरे बेंचमार्क में 5000 वस्तुओं में से 700 नंबर को चुनना शामिल था। एल्गोरिथ्म के कई संस्करणों का समय फिर से जमा हुआ, इस बार 10,000 से अधिक रन। परिणामों को कोड में टिप्पणियों के रूप में दर्शाया जाता है 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(सी + + संग्रह) के साथ दृष्टिकोण को भी लागू किया है , जो वास्तव में बेंचमार्क पर दूसरे स्थान पर आता है, जो 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.add () का उपयोग करें।

यदि रिटर्न मान गलत है, तो आप जानते हैं कि संख्या पहले ही उत्पन्न हो चुकी है।


2

यह सब करने के बजाय फ़ंक्शन LinkedHashSetद्वारा ऑब्जेक्ट और रैंडम नंबर बनाएं Math.random()। .... यदि कोई डुप्लिकेट प्रविष्टि होती है, तो LinkedHashSetऑब्जेक्ट उस नंबर को उसकी सूची में नहीं जोड़ेगा ... क्योंकि इस संग्रह कक्षा में कोई डुप्लिकेट मान की अनुमति नहीं है .. अंत में, बिना डुप्लिकेट मान वाले यादृच्छिक संख्याओं की एक सूची प्राप्त करें ....: D


2

N तत्वों के संग्रह से यादृच्छिक पर k तत्वों को चुनने के लिए आपकी समस्या कम होती दिख रही है। इस प्रकार, संग्रह.शफल उत्तर इस प्रकार सही है, लेकिन जैसा कि उल्लेखनीय है: इसका O (n)।

फिशर-येट्स फेरबदल में एक ओ (के) संस्करण होता है जब सरणी पहले से मौजूद होती है। आपके मामले में, तत्वों की कोई सरणी नहीं है और तत्वों की सरणी बनाना बहुत महंगा हो सकता है, मान लें कि अधिकतम 20 के बजाय 10000000 थे।

फेरबदल एल्गोरिथ्म में आकार n की एक सरणी को शामिल करना शामिल है जहां हर तत्व अपने सूचकांक के बराबर होता है, प्रत्येक श्रेणी में k संख्या को पिछले सीमा से कम अधिकतम के साथ उठाता है, फिर तत्वों को सरणी के अंत की ओर स्वैप करता है।

आप हेम के साथ ओ (के) समय में एक ही ऑपरेशन कर सकते हैं, हालांकि मैं इसके प्रकार का दर्द मानता हूं। ध्यान दें कि यह केवल तभी सार्थक है यदि k n से बहुत कम है। (अर्थात k ~ lg (n) या तो), अन्यथा आपको सीधे फेरबदल का उपयोग करना चाहिए।

आप फेरबदल एल्गोरिथ्म में बैकिंग सरणी के एक कुशल प्रतिनिधित्व के रूप में अपने हैशमैप का उपयोग करेंगे। सरणी का कोई भी तत्व जो इसके सूचकांक के बराबर है, मानचित्र में दिखाई देने की आवश्यकता नहीं है। यह आपको निरंतर समय में आकार n की एक सरणी का प्रतिनिधित्व करने की अनुमति देता है, इसे आरंभीकृत करने में कोई समय खर्च नहीं होता है।

  1. K 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। इस समाधान में एक यादृच्छिक संख्या प्राप्त करने के लिए ओ (1) है और आरंभीकरण के लिए ओ (एन) है, लेकिन ट्रेडऑफ़ के रूप में यदि एन काफी बड़ी हो जाती है तो अच्छी मात्रा में मेमोरी की आवश्यकता होती है।


0

एक संग्रह से अधिक पूर्णांक के लिए एक अधिक कुशल और कम बोझिल समाधान है। शफल।

समस्या समान रूप से केवल अन-चुनी वस्तुओं को एक सेट में से आइटम लेने और उन्हें कहीं और क्रम में स्थापित करने के समान है। यह बिलकुल बेतरतीब ढंग से निपटने वाले कार्ड की तरह है या हैट या बिन से रफ़ल टिकट जीतना।

यह एल्गोरिथम किसी भी सरणी को लोड करने और लोड के अंत में एक यादृच्छिक क्रम को प्राप्त करने के लिए काम करता है। यह एक सूची संग्रह (या किसी अन्य अनुक्रमित संग्रह) में जोड़ने और संग्रह के अंत में संग्रह में एक यादृच्छिक अनुक्रम प्राप्त करने के लिए भी काम करता है।

यह एक ही सरणी के साथ किया जा सकता है, एक बार बनाया गया, या एक संख्यात्मक रूप से आदेश दिया गया संग्रह, जैसे कि सूची में, जगह में। किसी सरणी के लिए, प्रारंभिक सरणी आकार में सभी इच्छित मानों को शामिल करने के लिए सटीक आकार होना चाहिए। यदि आप नहीं जानते कि कितने क्रम में अग्रिम रूप से हो सकता है, तो एक क्रमबद्ध संग्रह का उपयोग करके, जैसे कि ArrayList या List, जहाँ आकार अपरिवर्तनीय नहीं है, भी काम करेगा। यह Integer.MAX_VALUE तक किसी भी आकार की एक सरणी के लिए सार्वभौमिक रूप से काम करेगा जो अभी 2,000,000,000 से अधिक है। सूची वस्तुओं में समान सूचकांक सीमाएँ होंगी। इससे पहले कि आप उस आकार की एक सरणी के लिए अपनी मशीन स्मृति से बाहर चला सकते हैं। यह ऑब्जेक्ट प्रकारों के लिए टाइप किए गए एरे को लोड करने और एरे को लोड करने के बाद इसे कुछ संग्रह में बदलने के लिए अधिक कुशल हो सकता है। यह विशेष रूप से सच है अगर लक्ष्य संग्रह संख्यात्मक रूप से अनुक्रमित नहीं है।

यह एल्गोरिथम, जैसा कि लिखा गया है, बहुत समान वितरण पैदा करेगा जहां कोई डुप्लिकेट नहीं हैं। एक पहलू जो बहुत महत्वपूर्ण है, वह यह है कि अगले आइटम के सम्मिलन के लिए वर्तमान आकार + 1 तक होना संभव है। इस प्रकार, दूसरे आइटम के लिए, इसे 0 या स्थान 1 में संग्रहीत करना संभव हो सकता है। । 20 वीं आइटम के लिए, इसे किसी भी स्थान पर स्टोर करना संभव हो सकता है, 19 के माध्यम से 0। यह स्थान 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);

यदि आपके पास सत्यापित करने के लिए लंबी सूची है, तो एक अधिक जटिल विधि कोड की स्पष्टता और प्रसंस्करण संसाधनों दोनों में बेहतर परिणाम देगी।

उम्मीद है की यह मदद करेगा। इस साइट ने मुझे बहुत मदद की है, मैंने कम से कम TRY के लिए भी मदद करने के लिए बाध्य महसूस किया है।


0

मैंने एक ऐसा स्निपेट बनाया, जिसमें कोई डुप्लिकेट रैंडम पूर्णांक नहीं है। इस स्निपेट का लाभ यह है कि आप इसके लिए एक सरणी की सूची निर्दिष्ट कर सकते हैं और यादृच्छिक आइटम भी उत्पन्न कर सकते हैं।

कोई दोहराव यादृच्छिक जनरेटर वर्ग नहीं


0

सबसे आसान तरीका नैनो डेटाइम को लंबे प्रारूप के रूप में उपयोग करना है। System.nanoTime ();

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.