假设有一个文件太大而无法存入内存。如何从中获取随机线?感谢。
更新: 我想要让每一条线相等的概率。
答案 0 :(得分:23)
如果只想要一行,则读取整个文件似乎有点过分。以下应该更有效:
这是rejection sampling的变体。
行长度包括行终止符(s),因此MIN_LINE_LENGTH> = 1.(如果您知道行长度更紧密,那就更好了。)
值得注意的是,此算法的运行时间不依赖于文件大小,仅取决于行长度,即它比读取整个文件要好得多。
答案 1 :(得分:19)
这是一个解决方案。看一下执行真实事物的choose()方法(main()方法反复练习choose(),以显示分布确实非常均匀)。
这个想法很简单:当你阅读第一行时,它有100%的机会被选为结果。当您阅读第2行时,它有50%的机会替换第一行作为结果。当你阅读第3行时,它有33%的机会成为结果。第四行有25%,依旧......
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;
}
}
答案 2 :(得分:9)
你要么
读取文件两次 - 一次计算行数,第二次提取随机行,或
答案 3 :(得分:6)
查看Itay的答案,看起来它在采样一行代码后读取文件一千次,而真正的水库采样应该只过了一次“磁带”。我已经设计了一些代码,根据this以及网络上的各种描述,通过真实的水库采样来验证代码。
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;
}
}
基本前提是你填满水库,然后回到它并填充1 / ReservoirSize机会的随机线。我希望这能提供更高效的代码。如果这对你不起作用,请告诉我,因为我已经在半小时内把它搞砸了。
答案 4 :(得分:1)
在java中从文件中随机读取一行:
public String getRandomLineFromTheFile(String filePathWithFileName) 抛出异常 {
File file = new File(filePathWithFileName);
final RandomAccessFile f = new RandomAccessFile(file, "r");
final long randomLocation = (long) (Math.random() * f.length());
f.seek(randomLocation);
f.readLine();
String randomLine = f.readLine();
f.close();
return randomLine;
}
答案 5 :(得分:0)
使用 RandomAccessFile :
使用这种方法,我一直在从布朗语料库中随机采样行,并且可以在几秒钟内轻松地从随机选择的文件中检索1000个随机样本。如果我尝试逐行阅读每个文件来做同样的事情,那将花费我更长的时间。
可以使用相同的原理从列表中选择随机元素。如果您生成一个介于0和列表长度之间的随机数,则无需阅读列表并停在一个随机的位置,而是可以直接索引到列表中。
答案 6 :(得分:-1)
使用BufferedReader并逐行读取。使用java.util.Random对象随机停止;)