2012-01-22 9 views
8

मेरे पास P आकार के साथ एक सरणी है, A = [a1,a2,a3,...aP]। मुझे q सरणी एमैं एनएल तत्वों को एक पर्ल सरणी से यादृच्छिक रूप से कैसे ले सकता हूं?

पर नमूना देना है I q पुनरावृत्तियों के साथ एक लूप का उपयोग करने की योजना है, और यादृच्छिक रूप से प्रत्येक पुनरावृत्ति पर ए से एक elment चुनें। लेकिन मैं यह कैसे सुनिश्चित कर सकता हूं कि प्रत्येक पुनरावृत्ति पर उठाई गई संख्या अलग होगी?

+0

शफलिंग से तेज़ी से एक दृष्टिकोण के लिए, प्रतिस्थापन के बिना यादृच्छिक नमूनाकरण के कार्यान्वयन की खोज करें (मुझे उदाहरण के लिए पाइथन कुकबुक से कुछ याद है)। डोनाल्ड नूथ * द आर्ट ऑफ कंप्यूटर प्रोग्रामिंग *, सेक्शन 3.4.2 देखें। – FMc

उत्तर

16

अन्य उत्तर सभी सरणी, जो O(n) है फेरबदल शामिल है। इसका मतलब मूल सरणी (विनाशकारी) को संशोधित करना या मूल सरणी (स्मृति गहन) की प्रतिलिपि बनाना है।

इसे और अधिक मेमोरी कुशल बनाने का पहला तरीका मूल सरणी को घुमाने के लिए नहीं बल्कि इंडेक्स की सरणी को घुमाने के लिए है।

# Shuffled list of indexes into @deck 
my @shuffled_indexes = shuffle(0..$#deck); 

# Get just N of them. 
my @pick_indexes = @shuffled_indexes[ 0 .. $num_picks - 1 ]; 

# Pick cards from @deck 
my @picks = @deck[ @pick_indexes ]; 

यह @deck की सामग्री के कम से कम स्वतंत्र है, लेकिन इसके अभी भी ओ (nlogn) प्रदर्शन और हे (एन) स्मृति।

एक अधिक कुशल एल्गोरिदम (आवश्यक रूप से तेज़ नहीं है, अब आपके सरणी पर बड़ा निर्भर करता है) सरणी के प्रत्येक तत्व को देखना है और यह तय करना है कि यह इसे सरणी में बनाने जा रहा है या नहीं। यह how you select a random line from a file without reading the whole file into memory के समान है, प्रत्येक पंक्ति में 1/एन मौका होने का मौका है जहां एन लाइन संख्या है। तो पहली पंक्ति में 1/1 मौका है (इसे हमेशा चुना जाता है)। अगले में 1/2 है। फिर 1/3 और इतने पर। प्रत्येक पिक पिछले पिक ओवरराइट करेगा। इसके परिणामस्वरूप प्रत्येक पंक्ति में 1/total_lines मौका होता है।

आप इसे अपने लिए बाहर कर सकते हैं। एक लाइन फ़ाइल में 1/1 मौका होता है ताकि पहले व्यक्ति को हमेशा चुना जा सके। एक दो पंक्ति फ़ाइल ... पहली पंक्ति में 1/1 है और जीवित रहने का 1/2 मौका है, जो 1/2 है, और दूसरी पंक्ति में 1/2 मौका है। एक तीन पंक्ति फ़ाइल के लिए ... पहली पंक्ति में 1/1 मौका लेने का मौका है, फिर 1/2 * 2/3 जीवित रहने का मौका 2/6 या 1/3 है। और इसी तरह।

एल्गोरिदम गति के लिए ओ (एन) है, यह एक बार एक अनियंत्रित सरणी के माध्यम से फिर से चलाता है, और चुनौतियों को स्टोर करने के लिए आवश्यक स्मृति से अधिक स्मृति का उपभोग नहीं करता है।

थोड़ा संशोधन के साथ, यह कई चुनौतियों के लिए काम करता है। 1/$position मौका के बजाय, यह $picks_left/$position है। प्रत्येक बार जब कोई पिक सफल होता है, तो आप $ picks_left कमी करते हैं। आप उच्च पद से निम्न तक काम करते हैं। पहले के विपरीत, आप ओवरराइट नहीं करते हैं।

my $picks_left = $picks; 
my $num_left = @$deck; 
my @picks; 
my $idx = 0; 
while($picks_left > 0) { # when we have all our picks, stop 
    # random number from 0..$num_left-1 
    my $rand = int(rand($num_left)); 

    # pick successful 
    if($rand < $picks_left) { 
     push @result, $deck->[$idx]; 
     $picks_left--; 
    } 

    $num_left--; 
    $idx++; 
} 

यह how perl5i implements its pick method (अगली रिलीज आने वाला) है।

यह समझने के लिए कि यह क्यों काम करता है, 4 तत्व सूची से 2 चुनने का उदाहरण लें। प्रत्येक को उठाए जाने का 1/2 मौका होना चाहिए।

1. (2 picks, 4 items):   2/4 = 1/2 

काफी सरल। अगले तत्व में 1/2 मौका है कि एक तत्व पहले से ही चुना जाएगा, जिस स्थिति में इसकी संभावना 1/3 है। अन्यथा इसकी संभावना 2/3 है। गणित हो रहा है ...

2. (1 or 2 picks, 3 items): (1/3 * 1/2) + (2/3 * 1/2) = 3/6 = 1/2 

अगला एक 1/4 मौका दोनों तत्वों को पहले ही उठाया जाएगा कि (1/2 * 1/2) है, तो यह कोई मौका नहीं है; 1/2 मौका है कि केवल एक ही चुना जाएगा, फिर इसमें 1/2 होगा; और शेष 1/4 कि किसी भी आइटम को नहीं चुना जाएगा, जिस स्थिति में यह 2/2 है।

3. (0, 1 or 2 picks, 2 items): (0/2 * 1/4) + (1/2 * 2/4) + (2/2 * 1/4) = 2/8 + 1/4 = 1/2 

आखिरकार, आखिरी वस्तु के लिए, पिछले 1/2 में पिछली पिक ली गई थी।

4. (0 or 1 pick, 1 items):  (0/1 * 2/4) + (1/1 * 2/4) = 1/2 

बिल्कुल एक सबूत नहीं है, लेकिन स्वयं को समझाने के लिए अच्छा यह काम करता है।

+0

'सूची :: जनरल' के पास 'perl5i' की तुलना में अलग-अलग डिज़ाइन लक्ष्यों हैं, जिसमें संख्याओं की अनंत श्रेणियों के साथ काम करने की क्षमता शामिल है। यह तत्वों को चुनने के लिए पूरी सरणी को कॉपी और शफल नहीं करता है, जो पूरी तरह से गलत है।यदि ऐसा होता है, तो एक अनंत स्रोत से चयन करें जैसे '<1..*> -> चुनें (5) -> कहें 'काम नहीं कर सका (लेकिन यह करता है)। –

+0

@EricStrom आलस्य के बारे में सब कुछ के बारे में स्पष्टीकरण के लिए धन्यवाद। सूची :: जनरल पर स्लैग करने का मतलब नहीं था, लेकिन मुझे लगा कि प्रदर्शन समस्या इतनी तीव्र थी कि लोगों को चेतावनी दी जाए जब तक उन्हें आलसी पहलू की आवश्यकता न हो। – Schwern

+0

फिर भी आपने अपना जवाब संपादित नहीं किया है ... साथ ही, क्या आप जानते हैं कि 'pick' का आपका 'perl5i' संस्करण ** क्रमबद्ध ** है? (कम से कम जब तक आप सभी तत्वों के लिए नहीं पूछते, जहां यह 'सूची :: उपयोग :: शफल' पर वापस आ जाता है और ठीक से काम करता है) उम्मीद है कि अगली रिलीज से पहले तय किया जाएगा। –

-1

आप आकार पी के साथ दूसरा सरणी, बूलियन बना सकते हैं और सही चुनिंदा संख्याओं के लिए स्टोर कर सकते हैं। और जब अंक चुना जाता है, तो दूसरी तालिका की जांच करें; यदि "सत्य" में आपको अगला चुनना होगा।

+1

यदि "क्यू" "पी" के नजदीक है तो यह बहुत धीमा होगा, खासकर अगर दो संख्याएं बड़ी हों। यदि q> पी, यह एक अनंत लूप में जाएगा। इस समस्या को हल करने के लिए मानक एल्गोरिदम मेरे उत्तर में वर्णित है। –

4

आप अपने सरणी को यादृच्छिक रूप से अनुमति देने के लिए Fisher-Yates shuffle algorithm पर शक कर सकते हैं और फिर पहले q तत्वों का एक टुकड़ा उपयोग कर सकते हैं। यहाँ PerlMonks से कोड है:

# randomly permutate @array in place 
sub fisher_yates_shuffle 
{ 
    my $array = shift; 
    my $i = @$array; 
    while (--$i) 
    { 
     my $j = int rand($i+1); 
     @$array[$i,$j] = @$array[$j,$i]; 
    } 
} 

fisher_yates_shuffle(\@array); # permutes @array in place 

आप शायद के बाद यह q यादृच्छिक चयनित तत्व है फेरबदल बंद होने से इस अनुकूलन कर सकते हैं। (वैसे यह लिखा है, आप पिछले क्ष तत्वों चाहेंगे।)

+0

यह एल्गोरिदम ['सूची :: उपयोग]] के रूप में उपलब्ध है (http://search.cpan.org/perldoc?List::Util)' :: shuffle' – ikegami

+0

@ikegami - दरअसल। हालांकि, यदि आपने 'सूची :: उपयोग :: शफल' का उपयोग किया है, तो मैंने जो ऑप्टिमाइज़ेशन उल्लेख किया है वह उपलब्ध नहीं है; अगर पी बहुत बड़ा है और क्यू बहुत छोटा है, तो यह एक कारक हो सकता है। –

7

perldoc perlfaq4 से:

मैं कैसे एक सरणी बेतरतीब ढंग से शफ़ल करते हैं?

पर्ल 5.8.0 या बाद में स्थापित आप या तो किया है, या अगर आपके पास अदिश-सूची-Utils 1.03 या बाद में स्थापित है, तो आप कह सकते हैं:

use List::Util 'shuffle'; 
@shuffled = shuffle(@list); 

यदि नहीं, तो आप एक का उपयोग कर सकते फिशर-येट्स शफल।

sub fisher_yates_shuffle { 

    my $deck = shift; # $deck is a reference to an array 
    return unless @$deck; # must not be empty! 

    my $i = @$deck; 
    while (--$i) { 
     my $j = int rand ($i+1); 
     @$deck[$i,$j] = @$deck[$j,$i]; 
    } 
} 


# shuffle my mpeg collection 
# 

my @mpeg = <audio/*/*.mp3>; 
fisher_yates_shuffle(\@mpeg); # randomize @mpeg in place 
print @mpeg; 

तुम भी List::Gen इस्तेमाल कर सकते हैं:

my $gen = <1..10>; 
print "$_\n" for $gen->pick(5); # prints five random numbers 
+1

सूची :: जनरल बेहद धीमी है, इसकी गति अक्षम अजीब शफल होने के कारण, कुछ हजारों के साथ पोकिंग एक सेकंड चुनती है। सूची :: उपयोग :: शफल तीव्रता के तीन आदेश तेज है, कुछ सौ हजार एक सेकंड चुनते हैं। मैंने सूची :: जनरल लेखक को अधिसूचित किया है। – Schwern

+0

@Schwern => सूची का बिंदु :: जनरल का '-> pick' यह आलसी है। यह यादृच्छिक तत्वों को बहुत बड़े, या बदलने, या पहुंचने में धीमा, या अनंत डेटा स्रोतों को चुनने की अनुमति देता है। यह अनिवार्य रूप से एक प्रदर्शन लागत पर आता है। मैं एक उत्सुक एल्गोरिदम का उपयोग करने पर विचार करूंगा जिसे अधिक प्रासंगिक रूप से अनुकूलित किया जा सकता है जब सूची संदर्भ में '-> pick ($ n) 'कहा जाता है, क्योंकि उस उपयोग के लिए सभी तत्वों की गणना एक बार में की जानी चाहिए। –

संबंधित मुद्दे

 संबंधित मुद्दे