Зафиксируйте форматирование, замените \n
фактическими новыми строками и удаляют побочный 'n' вначале, затем он работает. Таким образом:
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r "$i" ]; then
. "$i"
fi
done
unset i
fi
if [ -n "$PS1" ]; then
if [ -n "$BASH" ]; then
PS1='u@h:w$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
umask 022
PT5HOME=/opt/pt
export PT5HOME
Предполагая, что у вас достаточно памяти для перебора файла, вы можете попробовать
perl -e 'use List::Util 'shuffle'; @k=shuffle(<>); print @k[0..999]' file.bed
Поскольку вы хотите сделать это 10000 раз, я бы рекомендовал интегрировать повторение в скрипт и перетасовать индексы вместо самого массива, чтобы ускорить работу:
$ time perl -e 'use List::Util 'shuffle';
@l=<>; for $i (1..10000){
open(my $fh, ">","file.$i.bed");
@r=shuffle(0..$#l);
print $fh @l[@r[0..999]]
}' file.bed
real 1m12.444s
user 1m8.536s
sys 0m3.244s
Вышеописанное создало 10000 файлов по 1000 строк каждый из файла, содержащего 37000 строк (ваш пример файла повторяется 1000 раз). Как вы можете видеть, на моей системе это заняло чуть больше трех минут.
use List::Util 'shuffle';
: это импортирует модуль Perl, который предоставляет функцию shuffle()
, которая рандомизирует массив. @l=<>;
: загрузите входной файл (<>
) в массив @l
. for $i (1..10000){}
: запустите это 10000 раз. @r=shuffle(0..$#l);
: $#l
- это количество элементов в @l
, поэтому @r
теперь является рандомизированным списком номеров индексов массива @l
(строк входного файла). open(my $fh, ">", "file.$i.bed");
: открыть файл с именем file.$i.bed
для записи. $i
будет принимать значения от 1 до 10000. print $fh @l[@r[0..999]]
: берем первые 1000 индексов в перетасованном массиве и печатаем соответствующие строки (элементы @l
). Другой подход - использовать shuf
(спасибо @frostschutz):
$ time for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.abed; done
real 1m9.743s
user 0m23.732s
sys 0m31.764s
Если вы хотите, чтобы эталонный тест показал, как быстро это может быть сделано, скопируйте его в 10kshuffle.cpp
и скомпилируйте g++ 10kshuffle.cpp -o 10kshuffle
. Затем вы можете запустить его:
10kshuffle filename < inputfile
Где имя файла
- базовый путь для использования выходных файлов; они будут иметь имена filename.0
, filename.1
и т.д., и каждая из них содержит первые 1000 строк тасовки. Имя каждого файла записывается по порядку.
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <unistd.h>
#include <vector>
using namespace std;
unsigned int randomSeed () {
int in = open("/dev/urandom", O_RDONLY);
if (!in) {
cerr << strerror(errno);
exit(1);
}
unsigned int x;
read(in, &x, sizeof(x));
close(in);
return x;
}
int main (int argc, const char *argv[]) {
char basepath[1024];
strcpy(basepath,argv[1]);
char *pathend = &basepath[strlen(basepath)];
// Read in.
vector<char*> data;
data.reserve(1<<16);
while (!cin.eof()) {
char *buf = new char[1024];
cin.getline(buf,1023);
data.push_back(buf);
}
srand(randomSeed());
for (int n = 0; n < 10000; n++) {
vector<char*> copy(data);
// Fisher-Yates shuffle.
int last = copy.size() - 1;
for (int i = last; i > 0; i--) {
int r = rand() % i;
if (r == i) continue;
char *t = copy[i];
copy[i] = copy[r];
copy[r] = t;
}
// Write out.
sprintf(pathend, ".%d", n);
ofstream file(basepath);
for (int j = 0; j < 1000; j++) file << copy[j] << endl;
cout << basepath << endl;
file.close();
}
return 0;
}
На одном ядре 3,5 ГГц это занимает ~20 секунд:
time ./10kshuffle tmp/test < data.txt
tmp/test.0
[...]
tmp/test.9999
real 19.95, user 9.46, sys 9.86, RSS 39408
data.txt
было 37000 строк, продублированных из вопроса. Если вы хотите, чтобы вся тасовка в выходном файле вместо первых 1000 строк, измените строку 54 на:
for (int j = 0; j < copy.size(); j++) file << copy[j] << endl;
Итак, в Вашем вопросе есть Unix аспект, но сначала стоит решить Вашу фундаментальную проблему, а затем попытаться найти Unix-y способ реализовать это решение.
Вам нужно создать 10,000 примеров размером 1000 каждый из файла с неизвестным, большим количеством строк. Это можно сделать в одном единственном проходе файла, если вы можете хранить в памяти 10,000 х 1000 строк. Если вы не можете удержать в памяти столько строк, вы все равно можете сделать это за один проход, если знаете, сколько строк содержит ваш файл. Если вы не знаете, сколько строк содержит ваш файл, вам потребуется один дополнительный проход для подсчета количества строк.
Алгоритм, в более сложном случае, когда Вы не знаете количество строк, должен сделать следующее для каждой выборки (параллельно, сохраняя выборки в памяти):
n > 1000
), включить с вероятностью 1000 / n
и отбросить случайную строку из уже выбранных Вами строк. (из-за вероятности отбрасывания некоторых строк нам нужно удерживать выборку в памяти до конца входа)Элегантным способом реализации второго шага является генерация случайного целого k
в [1, n]
. Если k <= 1000
, то включите эту строку и замените на нее существующую k
-ю строку. Вот более стандартное описание алгоритма: http://en.wikipedia.org/wiki/Reservoir_sampling
Если известно количество строк, R
, то:
s
из 0(1000 - с) / (R - n + 1)
и сразу же выводим ее (и увеличиваем размер выборки s
)Как это сделать на Unix? awk
кажется ответом на это сообщение в интернете (я не могу поручиться за его правильность, но код там) https://news.ycombinator.com/item? id=4840043