2009-05-13 5 views
6

this प्रश्न के बाद, मुझे फ़ाइल के यादृच्छिक रूप से n लाइनों को ठीक से प्राप्त करने की आवश्यकता है (या stdin)। यह head या tail के समान होगा, सिवाय इसके कि मैं कुछ मध्य से चाहता हूं।पर्ल के साथ फाइल से मुझे बिल्कुल यादृच्छिक रेखाएं कैसे मिल सकती हैं?

अब, लिंक किए गए प्रश्न के समाधान के साथ फ़ाइल पर लूप करने के अलावा, एक रन में n लाइनों को पाने का सबसे अच्छा तरीका क्या है?

संदर्भ के लिए, मैंने कोशिश की यह:

#!/usr/bin/perl -w 
use strict; 
my $ratio = shift; 
print $ratio, "\n"; 
while() { 
    print if ((int rand $ratio) == 1); 
} 

जहां $ratio लाइनों मैं चाहता हूँ के किसी न किसी प्रतिशत है।

aaa> foreach i (0 1 2 3 4 5 6 7 8 9) 
foreach? random_select 10 a.list | wc -l 
foreach? end 
4739 
4865 
4739 
4889 
4934 
4809 
4712 
4842 
4814 
4817 

अन्य विचार मैं था इनपुट फ़ाइल slurping गया था और तब चुनने:

random_select 10 a.list 

बहरहाल, यह मुझे एक सटीक राशि नहीं दे करता है: उदाहरण के लिए, अगर मैं 10 में 1 लाइनों चाहते हैं n सरणी से यादृच्छिक रूप से, लेकिन यह एक समस्या है यदि मेरे पास वास्तव में बड़ी फ़ाइल है।

कोई भी विचार?

संपादित करें: यह this प्रश्न का एक सटीक डुप्लिकेट है।

+1

इस http://stackoverflow.com/questions/692312/randomly-pick-lines-from-a-file-without-slurping-it-with-unix –

+0

की एक सटीक डुप्लिकेट नहीं है यह हाँ है। माफ़ कीजिये। मैं दोनों को जोड़ूंगा और इसे बंद करने के लिए वोट दूंगा। –

+2

नहीं, नमूना के लिए अन्य प्रश्नों को बंद करने की अनुमति है - यह एक सटीक संख्या चाहता है। – Alnitak

उत्तर

4

यहां एक अच्छा एक-पास एल्गोरिदम है जो मैं अभी आया था, जिसमें ओ (एन) समय जटिलता और ओ (एम) स्पेस जटिलता है, एन-लाइन फ़ाइल से एम लाइन पढ़ने के लिए।

मान लें एम < = एन

  1. Let चुना लाइनों के सेट हो S। फाइल के पहले M लाइनों में S आरंभ करें। यदि अंतिम परिणाम का ऑर्डर करना महत्वपूर्ण है, तो अब S को घुमाएं।
  2. अगली पंक्ति l में पढ़ें। अब तक, हमने n = M + 1 कुल लाइनें पढ़ी हैं। संभावना है कि हम l चुनना चाहते हैं क्योंकि हमारी अंतिम लाइनों में से एक M/n है।
  3. संभाव्यता M/n के साथ l स्वीकार करें; l को स्वीकार या अस्वीकार करने के लिए यह तय करने के लिए एक आरएनजी का उपयोग करें।
  4. यदि l स्वीकार कर लिया गया है, तो S में यादृच्छिक रूप से किसी एक पंक्ति का चयन करें और इसे l के साथ बदलें।
  5. दोहराएँ चरण 2-4 तक फ़ाइल लाइनों के समाप्त हो गया है, हर नई लाइन पढ़ने के साथ n incrementing।
  6. चयनित लाइनों के सेट S पर लौटें।
+0

अच्छा, लेकिन मुझे लगता है कि आपका मतलब एम <= एन – Alnitak

+0

फ़्लिप किया गया चिन्ह गणितज्ञों का शाश्वत दुश्मन है। एक श्वास के साथ फिक्स्ड। – kquinn

+0

भी, मूल एम लाइनों की ओर पूर्वाग्रह नहीं है जब तक कि एन >> एम? – Alnitak

1

संभव समाधान:

  1. स्कैन फिर
  2. स्कैन लाइनों की संख्या की गिनती करने के
  3. तय लाइन नंबर बेतरतीब ढंग से लेने के लिए एक बार, लाइन
+2

stdin पर, स्कैनिंग दो बार एक समस्या हो सकती है। – Eyal

0

लेने छद्म में कोड:

use List::Util qw[shuffle]; 

# read and shuffle the whole file 
@list = shuffle(<>); 

# take the first 'n' from the list 
splice(@list, ...); 

यह सबसे छोटा कार्यान्वयन है, लेकिन आपको पहले पूरी फ़ाइल को पढ़ना होगा, जिसके लिए आपको पर्याप्त मेमोरी उपलब्ध होगी।

+1

यह काम नहीं करेगा अगर फ़ाइल वास्तव में बड़ी है – kcwu

+0

यह वास्तव में मेरा मुद्दा था। जिस फ़ाइल पर मैं काम कर रहा हूं वह 63 एमबी है और यह हमेशा के लिए लेता है। –

+0

फ़ाइल का आकार 63 एमबी? आपके पास कितने एमबी रैम हैं? मुझे लगता है कि यह आकार एक समस्या नहीं होनी चाहिए। – kcwu

1
@result =(); 

$k = 0; 
while(<>) { 
    $k++; 
    if (scalar @result < $n) { 
     push @result, $_; 
    } else { 
     if (rand <= $n/$k) { 
      $result[int rand $n] = $_; 
     } 
    } 
} 

print for @result; 
+0

आपकी रैंड टेस्ट गलत है - यह $ n/$ k होना चाहिए, 1.0/$ k नहीं; – Alnitak

+0

धन्यवाद। ठीक कर दिया। – kcwu

2

यह एक एकल कमांड लाइन तर्क है, जो पहली एन लाइनों आयोजित कर रहे हैं आप चाहते हैं लाइन की संख्या, एन है, जैसा कि आप किसी भी अधिक नहीं देख सकता है लेता है। इसके बाद, आप यादृच्छिक रूप से तय करते हैं कि अगली पंक्ति लेना है या नहीं। और यदि आप करते हैं, तो आप यादृच्छिक रूप से तय करते हैं कि मौजूदा सूची-एन-एन में ओवरराइट करने के लिए कौन सी रेखा है।

#!/usr/bin/perl 
my $bufsize = shift; 
my @list =(); 

srand(); 
while (<>) 
{ 
    push(@list, $_), next if (@list < $bufsize); 
    $list[ rand(@list) ] = $_ if (rand($./$bufsize) < 1); 
} 
print foreach @list; 
0

यहां कुछ वर्बोज़ पर्ल कोड है जो बड़ी फ़ाइलों के साथ काम करना चाहिए।

इस कोड का दिल यह है कि यह पूरी फ़ाइल को स्मृति में संग्रहीत नहीं करता है, लेकिन केवल फ़ाइल में ऑफसेट स्टोर करता है।

उपयोग tell ऑफसेट मिलता है। फिर लाइनों को पुनर्प्राप्त करने के लिए उपयुक्त स्थानों पर seek

लक्ष्य फ़ाइल का बेहतर विनिर्देश और प्राप्त करने के लिए लाइनों की संख्या I से कम आलसी लोगों के लिए एक अभ्यास के रूप में छोड़ी गई है। उन समस्याओं को हल किया गया है।

#!/usr/bin/perl 

use strict; 
use warnings; 

use List::Util qw(shuffle); 

my $GET_LINES = 10; 

my @line_starts; 
open(my $fh, '<', 'big_text_file') 
    or die "Oh, fudge: $!\n"; 

do { 
    push @line_starts, tell $fh 
} while (<$fh>); 

my $count = @line_starts; 
print "Got $count lines\n"; 

my @shuffled_starts = (shuffle @line_starts)[0..$GET_LINES-1]; 

for my $start (@shuffled_starts) { 

    seek $fh, $start, 0 
     or die "Unable to seek to line - $!\n"; 

    print scalar <$fh>; 
} 
1

फ़ाइल में वास्तविक पंक्ति संख्या जानने की कोई आवश्यकता नहीं है। बस एक यादृच्छिक जगह की तलाश करें और अगले लाइन रखें। (वर्तमान लाइन सबसे अधिक आंशिक रेखा होगी।)

यह दृष्टिकोण बड़ी फ़ाइलों के लिए बहुत तेज़ होना चाहिए, लेकिन यह एसटीडीआईएन के लिए काम नहीं करेगा। बिल्ली, स्मृति में पूरी फाइल को कैशिंग करने के लिए कुछ भी नहीं एसटीडीआईएन के लिए काम करेगा। इसलिए, यदि आपके पास STDIN होना चाहिए, तो मुझे नहीं लगता कि आप बड़ी फ़ाइलों के लिए तेज़/सस्ते कैसे हो सकते हैं।

आप एसटीडीआईएन का पता लगा सकते हैं और एक कैश किए गए दृष्टिकोण पर स्विच कर सकते हैं, अन्यथा तेज़ हो सकते हैं।

 
#!perl 
use strict; 

my $file='file.txt'; 
my $count=shift || 10; 
my $size=-s $file; 

open(FILE,$file) || die "Can't open $file\n"; 

while ($count--) { 
    seek(FILE,int(rand($size)),0); 
    $_=readline(FILE);       # ignore partial line 
    redo unless defined ($_ = readline(FILE)); # catch EOF 
    print $_; 
} 
+2

ध्यान दें कि यह दृष्टिकोण * फ़ाइल से समान रूप से लाइनों को नहीं उठाएगा। चुनी जा रही रेखा की संभावना पिछले रेखा की लंबाई से भारित की जाएगी; अगर सभी लाइनों की लंबाई समान है, तो यह कोई समस्या नहीं है। लेकिन यदि आपको अलग-अलग लंबाई की रेखा वाली फ़ाइल से लाइनों की कड़ाई से समान वितरण की आवश्यकता है, तो आपको एक अलग दृष्टिकोण की आवश्यकता होगी। – kquinn

+0

grrrr आप सही हैं ... ओह ठीक है .. यह * तेज़ है :) लेकिन उपयोगी है अगर रिकॉर्ड की लंबाई स्थिर है .. या बहुत करीब है। – rmeden

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

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