Как сделать выборку без замены из скрипта, который случайным образом извлекает 200 символов с помощью shuf?

Чтобы проверить, выполняется ли скрипт в интерактивной или -неинтерактивной оболочке, Я проверяю в своих скриптах наличие подсказки, хранящейся в переменной $PS1:

if [ -z $PS1 ] # no prompt?
### if [ -v PS1 ]   # On Bash 4.2+...
then
  # non-interactive
 ...
else
  # interactive
 ...
fi

Это я узнал здесь:https://www.tldp.org/LDP/abs/html/intandnonint.html

0
20.12.2020, 21:30
2 ответа

Это реализация решения в awk. Данные представляют собой 8 ГБ псевдо-случайных шестнадцатеричных цифр (, на самом деле шестнадцатеричное преобразование около 12 справочных страниц, продублированных 3300 раз ). Это около 11 миллионов строк, в среднем 725 байт на строку.

Это синхронизированное выполнение.

Paul--) ls -l tdHuge.txt
-rw-r--r-- 1 paul paul 8006529300 Dec 24 22:38 tdHuge.txt
Paul--)./rndSelect
inFile./tdHuge.txt; Size 8006529300; Count 10000; Lth 200; maxIter 50; Db 1;
Iteration   1 needs  10000
Iteration   2 needs   2712
Overlap   9561: 7663038508 to 7663038657
Iteration   3 needs    728
Iteration   4 needs    195
Iteration   5 needs     50
Iteration   6 needs     11
Iteration   7 needs      2
Required 7 iterations
Reporting 10000 samples

real    2m3.326s
user    0m3.496s
sys 0m10.340s
Paul--) wc Result.txt
  20000   20000 2068894 Result.txt
Paul--) head -n 8 Result.txt | cut -c 1-40
>1
5706C69636174656420696E666F726D6174696F6
>2
20737472696E672028696E207768696368206361
>3
20646F6573206E6F742067657420612068617264
>4
647320616E642073746F7265732E204966207468
Paul--) tail -n 8 Result.txt | cut -c 1-40
>9997
6F7374207369676E69666963616E7420646F7562
>9998
7472696E676F702D73747261746567793D616C67
>9999
865726520736F6D652066696C6573206D7573742
>10000
5726E65642E205768656E20746865202D66206F7
Paul--) 

Это требует итераций, потому что делает случайные проверки в файле. Если проба перекрывает соседнюю или новую строку, то она отбрасывается и создается меньшая партия новых проб. При средней длине строки 725 строк и требуемом количестве выборки 200 почти 30% зондов будут располагаться слишком близко к концу строки, чтобы их можно было принять. Мы не знаем среднюю длину строки реальных данных --, более длинные строки улучшат коэффициент успеха.

Мы также не знаем, присутствуют ли в файле строки заголовка (, как указано в предыдущем связанном вопросе от 04 -декабря -2020 ).Но при условии, что каждая строка заголовка меньше, чем длина выборки 200, строки заголовка будут отброшены (по счастливой случайности в лучшем случае ).

Код в основном представляет собой GNU/awk (минимальный bash )и содержит некоторые комментарии. Существует много остаточной отладки, которую можно скрыть, установив Db=0 в параметрах.

#! /bin/bash

#.. Select random non-overlapping character groups from a file.

export LC_ALL="C"

#.. These are the optional values that will need to be edited.
#.. Command options could be used to set these from scripts arguments.

inFile="./tdHuge.txt"
outFile="./Result.txt"
Count=10000     #.. Number of samples.
Lth=200         #.. Length of each sample.
maxIter=50      #.. Prevents excessive attempts.

Size="$( stat -c "%s" "${inFile}" )"
Seed="$( date '+%N' )"
Db=1

#.. Extracts random non-overlapping character groups from a file.

Selector () {

    local Awk='
#.. Seed the random number generation, and show the args being used.
BEGIN {
    NIL = ""; NL = "\n"; SQ = "\047";
    srand (Seed % PROCINFO["pid"]);
    if (Db) printf ("inFile %s; Size %d; Count %d; Lth %d; maxIter %s; Db %s;\n",
        inFile, Size, Count, Lth, maxIter, Db);
    fmtCmd = "dd bs=%d count=1 if=%s iflag=skip_bytes skip=%d status=none";
}
#.. Constructs an array of random file offsets, replacing overlaps.
#.. Existing offsets are indexed from 1 to Count, deleting overlaps.
#.. Additions are indexed from -1 down to -N to avoid clashes.

function Offsets (N, Local, Iter, nSeek, Seek, Text, j) {

    while (N > 0 && Iter < maxIter) {
        ++Iter;
        if (Db) printf ("Iteration %3d needs %6d\n", Iter, N);

        for (j = 1; j <= N; ++j) {
            Seek[-j] = int ((Size - Lth) * rand());
            Text[Seek[-j]] = getSample( Seek[-j], Lth);
            if (Db7) printf ("Added %10d: \n", Seek[-j], Text[Seek[-j]]);
        }
        #.. Reindex in numeric order for overlap checking.
        nSeek = asort (Seek);
        if (Db7) for (j in Seek) printf ("%6d: %10d\n", j, Seek[j]);

        #.. Discard offsets that overlap the next selection.
        N = 0; for (j = 1; j < nSeek; ++j) {
            if (Seek[j] + Lth > Seek[j+1]) {
                if (Db) printf ("Overlap %6d: %10d to %10d\n",
                    j, Seek[j], Seek[j+1]);
                ++N; delete Text[Seek[j]]; delete Seek[j];
            } else if (length (Text[Seek[j]]) < Lth) {
                if (Db7) printf ("Short   %6d: %10d\n",
                    j, Seek[j]);
                ++N; delete Text[Seek[j]]; delete Seek[j];
            }
        }
    }
    if (Iter >= maxIter) {
        printf ("Failed with overlaps after %d iterations\n", Iter);
    } else {
        printf ("Required %d iterations\n", Iter);
        Samples( nSeek, Seek, Text);
    }
}
#.. Returns n bytes from the input file from position p.
function getSample (p, n, Local, cmd, tx) {

    cmd = sprintf (fmtCmd, n, SQ inFile SQ, p);
    if (Db7) printf ("cmd :%s:\n", cmd);
    cmd | getline tx; close (cmd);
    return (tx);
}
#.. Send samples to the output file.
function Samples (nSeek, Seek, Text, Local, j) {

    printf ("Reporting %d samples\n", nSeek);
    for (j = 1; j <= nSeek; ++j) {
        printf (">%d\n%s\n", j, Text[Seek[j]]) > outFile;
    }
    close (outFile);
}
END { Offsets( Count); }
'
    echo | awk -v Size="${Size}" -v inFile="${inFile}" \
        -v outFile="${outFile}" -v Count="${Count}" -v Lth="${Lth}" \
        -v maxIter="${maxIter}" \
        -v Db="${Db}" -v Seed="${Seed}" -f <( printf '%s' "${Awk}" )
}

#.. Test.

    time Selector
0
18.03.2021, 22:41

РЕДАКТИРОВАТЬ :Я добавлю еще один ответ с реализацией (, которая может нуждаться в модификации ). Этот текущий ответ частично заменен, и я, вероятно, скоро захочу его удалить. Или, возможно, обновить его, как примечание к дизайну для фактической реализации.

Суть алгоритма. Он может не выдавать строго случайных результатов статистически, но, похоже, он удовлетворяет требованию. Вероятно, я бы прототипировал это в awk (, используя RNG Cliff, или, возможно, чтение из shuf ).

  1. Установите N как количество смещений, которые будут добавлены к массиву X (, например. 10000 ).

  2. Пока N > 0, итерации от 2a до 2d.
    2а. Добавьте блок из N случайных смещений к X (, используя диапазон в вашем существующем скрипте ).
    2б. Сортировка X по возрастанию.
    2в. Перейдите X, сравнивая каждое смещение со следующим и помечая для удаления все, что ближе 200 символов от его преемника.
    2д. Удалите отмеченные смещения и установите N равным количеству сделанных удалений.

  3. На данный момент у нас есть 10000 случайных смещений (в порядке возрастания ), которые не перекрываются. (Нужна проверка того, что диапазон достаточно разрежен, чтобы этап 2 в конечном итоге вышел --, например, как минимум 3 *10000 *200 символов.)
    3а. Выпустить серию 10000 хвост | команды головы (по одной на смещение ).
    3б. Передайте эти команды через bash.

Сначала необходимо проверить, что диапазон достаточно разреженный, чтобы этап 2 в конечном итоге вышел из --, например, как минимум 3 *10000 *200 символов. Некоторые исследования по этому вопросу могут быть необходимы.

РЕДАКТИРОВАТЬ :Для проверки перекрытия между выборками требуются только смещения, а для проверки перекрытия с концом строки нужны данные. Итак, в (2a )прочитайте сам образец,и сохраните его в массиве, индексированном по смещению первого байта. Проверьте и удалите короткие сэмплы в (2c ).

Это делает весь (3 )избыточным, так как мы уже сохранили каждый допустимый образец в памяти (и у нас есть отсортированный список допустимых смещений для их индексации из ). Таким образом, мы можем просто перечислить все образцы в порядке смещения.

0
18.03.2021, 22:41

Теги

Похожие вопросы