2010-02-07 29 views

Trả lời

18

Dưới đây là một giải pháp. Hãy xem phương thức select() thực hiện điều thực (phương thức main() lặp đi lặp lại các bài tập select(), để chỉ ra rằng phân phối thực sự khá đồng bộ).

Ý tưởng rất đơn giản: khi bạn đọc dòng đầu tiên, nó có 100% cơ hội được chọn là kết quả. Khi bạn đọc dòng thứ 2 nó có 50% cơ hội thay thế dòng đầu tiên là kết quả. Khi bạn đọc dòng thứ 3, nó có 33% cơ hội trở thành kết quả. Dòng thứ tư có 25%, v.v ...

import java.io.*; 
import java.util.*; 

public class B { 

    public static void main(String[] args) throws FileNotFoundException { 
    Map<String,Integer> map = new HashMap<String,Integer>(); 
    for(int i = 0; i < 1000; ++i) 
    { 
     String s = choose(new File("g:/temp/a.txt")); 
     if(!map.containsKey(s)) 
      map.put(s, 0); 
     map.put(s, map.get(s) + 1); 
    } 

    System.out.println(map); 
    } 

    public static String choose(File f) throws FileNotFoundException 
    { 
    String result = null; 
    Random rand = new Random(); 
    int n = 0; 
    for(Scanner sc = new Scanner(f); sc.hasNext();) 
    { 
     ++n; 
     String line = sc.nextLine(); 
     if(rand.nextInt(n) == 0) 
      result = line;   
    } 

    return result;  
    } 
} 
+4

Việc triển khai lấy mẫu hồ chứa – Will

+0

Tuyệt vời. Không bao giờ nghe nói về lấy mẫu hồ chứa. Điều gì xảy ra nếu tệp của tôi là MB? Có bất kỳ vấn đề về hiệu suất nào không? Nếu có, có lựa chọn thay thế nào để tránh quét toàn bộ tệp không? –

+1

Tôi có đúng và giả sử đây là một n = 1 cố định, trong đó n là số 'mẫu'? Có cách nào để chọn chọn nhiều hơn một lần không? vì nó là viết tắt của bạn 'trên băng' nhiều hơn một lần, hoặc ít nhất là cố gắng mà dường như không hiệu quả. – Pureferret

-1

Sử dụng bộ đệm BufferedReader và đọc dòng thông minh. Sử dụng các đối tượng java.util.Random để ngăn chặn một cách ngẫu nhiên;)

+0

Làm cách nào để đảm bảo tệp không kết thúc khi tôi muốn dừng? I E. làm thế nào để tôi biết số dòng nếu một tập tin? – Fluffy

+0

Ngoài ra, tôi muốn tính probalilities của nhận được mỗi dòng để được bằng nhau. – Fluffy

+0

@Dinuk, vì vậy nếu tệp nhỏ hơn những người khác, tôi sẽ nhận được dòng cuối cùng quá thường xuyên, nếu tệp lớn hơn - Tôi sẽ nhận được nó quá hiếm khi – Fluffy

9

Hoặc bạn

  1. đọc các tập tin hai lần - một lần để đếm số dòng, lần thứ hai để trích xuất một dòng ngẫu nhiên, hoặc

  2. sử dụng reservoir sampling

20

Đọc toàn bộ tệp nếu bạn muốn chỉ một dòng có vẻ hơi quá mức. Sau đây sẽ hiệu quả hơn:

  1. Sử dụng RandomAccessFile để tìm kiếm vị trí byte ngẫu nhiên trong tệp.
  2. Tìm kiếm trái và phải cho người kết thúc dòng tiếp theo. Hãy L dòng giữa chúng.
  3. Với xác suất (MIN_LINE_LENGTH/L.length) trở L. Nếu không, bắt đầu lại ở bước 1.

Đây là một biến thể của rejection sampling.

Độ dài dòng bao gồm (các) ký tự kết thúc dòng, do đó MIN_LINE_LENGTH> = 1. (Tất cả sẽ tốt hơn nếu bạn biết giới hạn chặt chẽ hơn về độ dài của đường). Cần lưu ý rằng thời gian chạy của thuật toán này không phụ thuộc vào kích thước tệp, chỉ trên độ dài dòng, tức là nó có quy mô tốt hơn nhiều so với việc đọc toàn bộ tệp.

+0

Tuyệt vời! Nếu tệp sẽ được lấy mẫu nhiều lần, hãy sử dụng một lần truyền để thu thập một 'Danh sách ' của các offset, sau đó có thể được ngẫu nhiên thông qua 'Collections.shuffle()'. – trashgod

+0

Đây phải là câu trả lời hay nhất. – akuz

6

Nhìn qua câu trả lời của Itay, có vẻ như nó đọc tệp hàng nghìn lần sau khi lấy mẫu một dòng mã, trong khi lấy mẫu hồ chứa thực chỉ nên đi qua 'băng' một lần. Tôi đã nghĩ ra một số mã để đi qua mã một lần với lấy mẫu hồ chứa thực, dựa trên this và các mô tả khác nhau trên web.

import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.util.List; 

public class reservoirSampling { 

    public static void main(String[] args) throws FileNotFoundException, IOException{ 
     Sampler mySampler = new Sampler(); 
     List<String> myList = mySampler.sampler(10); 
     for(int index = 0;index<myList.size();index++){ 
      System.out.println(myList.get(index)); 
     } 
    } 
} 

import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 
import java.util.Scanner; 

public class Sampler { 

    public Sampler(){} 
    public List<String> sampler (int reservoirSize) throws FileNotFoundException, IOException 
    { 
     String currentLine=null; 
     //reservoirList is where our selected lines stored 
     List <String> reservoirList= new ArrayList<String>(reservoirSize); 
     // we will use this counter to count the current line number while iterating 
     int count=0; 

     Random ra = new Random(); 
     int randomNumber = 0; 
     Scanner sc = new Scanner(new File("Open_source.html")).useDelimiter("\n"); 
     while (sc.hasNext()) 
     { 
      currentLine = sc.next(); 
      count ++; 
      if (count<=reservoirSize) 
      { 
       reservoirList.add(currentLine); 
      } 
      else if ((randomNumber = (int) ra.nextInt(count))<reservoirSize) 
      { 
       reservoirList.set(randomNumber, currentLine); 
      } 
     } 
     return reservoirList; 
    } 
} 

Tiền đề cơ bản là bạn điền vào hồ chứa và sau đó quay lại và điền vào các dòng ngẫu nhiên với cơ hội 1/ReservoirSize. Tôi hy vọng điều này cung cấp mã hiệu quả hơn. Xin vui lòng cho tôi biết nếu điều này không làm việc cho bạn, như tôi đã nghĩa đen gõ nó lên trong nửa giờ.

+0

Tôi đã thiết lập cho [xem xét] (http://codereview.stackexchange.com/q/16154/15461). – Pureferret

Các vấn đề liên quan