Скажем, файл слишком большой, чтобы его можно было записать в память. Как я могу получить от него случайную строку? Спасибо.
Обновление: Я хочу, чтобы вероятность того, что каждая строка будет равна.
Скажем, файл слишком большой, чтобы его можно было записать в память. Как я могу получить от него случайную строку? Спасибо.
Обновление: Я хочу, чтобы вероятность того, что каждая строка будет равна.
Вот решение. Взгляните на метод select(), который делает реальную вещь (метод main() многократно выполняет функцию select(), чтобы показать, что распределение действительно довольно равномерное).
Идея проста: когда вы читаете первую строчку, у нее есть 100% шанс быть выбранным в качестве результата. Когда вы читаете вторую строчку, у нее есть 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;
}
}
Чтение всего файла, если вы хотите, чтобы только одна строка казалась немного чрезмерной. Следующее должно быть более эффективным:
Это вариант выборки отклонения.
Длина линии включает символ (-ы) терминатора линии, поэтому MIN_LINE_LENGTH >= 1. (Все лучше, если вы знаете более жесткую привязку длины строки).
Стоит отметить, что время выполнения этого алгоритма не зависит от размера файла, а только от длины строки, то есть оно масштабируется намного лучше, чем чтение всего файла.
Либо вы
дважды читайте файл - один раз, чтобы подсчитать количество строк, второй раз, чтобы извлечь случайную строку, или
использовать выборки коллектора
Оглядываясь на ответ Itay, похоже, что он читает файл тысячу раз после выборки одной строки кода, тогда как истинная выборка коллектора должна переходить только через "ленту" один раз. Я разработал код для перебора кода один раз с реальной выборкой коллектора на основе этого и различных описаний в Интернете.
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. Надеюсь, это обеспечит более эффективный код. Пожалуйста, дайте мне знать, если это не сработает для вас, поскольку я буквально выбил его через полчаса.
Использовать RandomAccessFile:
Используя этот метод, я случайно выбрал строки из Brown Corpus и могу легко получить 1000 случайных выборок из случайно выбранных файлов за несколько секунд. Если бы я попытался сделать то же самое, прочитав каждый файл по очереди, мне потребовалось бы гораздо больше времени.
Этот же принцип можно использовать для выбора случайных элементов из списка. Вместо того, чтобы читать список и останавливаться в случайном месте, если вы генерируете случайное число между 0 и длиной списка, вы можете индексировать его непосредственно в списке.
Используйте BufferedReader и прочитайте строку. Используйте объект java.util.Random для случайного остановки;)