web-dev-qa-db-ja.com

ランダムな数字を含む1 GBのテキストファイルを生成する最も速い方法は何ですか?

Bashスクリプトを試しましたが、単純な1 MBファイルを作成するには時間がかかりすぎました。答えは/dev/randomまたは/dev/urandomを使用することにあると思いますが、ここの他の投稿では、これらのものを使用してすべての種類のデータをファイルに追加する方法のみを示していますが、数値のみを追加します。

それで、0から9までの数字のみを含むサイズ1 GBのランダムファイルを作成するために使用できるコマンドはありますか?

編集:私は出力をこのようなものにしたい

0 1 4 7 ..... 9
8 7 5 8 ..... 8
....
....
8 7 5 3 ..... 3

範囲は0〜9で、0、1、2、3、4、5、6、7、8、9の数字のみを意味します。また、スペースで区切って、1行あたり100まで、nまでにする必要があります。行数。このnは気にしないものです。最終サイズを1 GBにしたいと思います。

編集:私はUbuntu 16.04 LTSを使用しています

53
posixKing

これは、質問のタイトルのため、部分的には口の内の答えです。

"...への最速の方法"を探す場合、答えはほとんどの場合特殊なツールです。この「答え」は、そのようなツールの1つを示しているので、実験することができます。

これは深刻な答えではありません。一度だけ、またはごくまれにしか実行しないジョブに特化したツールを調べてはならないからです。なるほど、実際に何かを行うよりも、ツールを探してそれらについて学ぶことに多くの時間を費やすことになります。 bashawkのようなシェルやユーティリティは最速ではありませんが、通常は次のように記述できますワンライナーほんの数秒を費やして、仕事を達成します。 Perlのようなより優れたスクリプト言語も使用できますが、Perlの学習曲線は急であり、私はひどいPerlプロジェクトに悩まされてきたので、そのような目的にはお勧めしません。一方、pythonは、I/Oがかなり遅いため、わずかに障害があります。ただし、ギガバイトのデータをフィルタリングまたは生成する場合にのみ問題になります。

いずれの場合でも、次のC89サンプルプログラム(POSIX.1を使用してより精度の高いクロックを使用できる場合のみ)は約100 MB/sの生成速度を達成する必要があります(Intel i5-4200Uプロセッサを搭載したラップトップ上のLinuxでテストし、出力をパイプします) _/dev/null_)に、かなり良い疑似乱数ジェネレータを使用します。 (コードは xorshift64 * と除外メソッドを使用して桁の偏りを避けるため、出力はMatrixRankテストを除くすべてのBigCrunchテストに合格する必要があります。)

decimal-digits.c:

_#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

/* This program is licensed under the CC0 license,
       https://creativecommons.org/publicdomain/zero/1.0/
   In other words, this is dedicated to the public domain.
   There are no warranties either, so if something breaks,
   you only have yourself to blame.
*/

#if _POSIX_C_SOURCE-199309 >= 0
static uint64_t time_seed(void)
{
    struct timespec  ts;

    if (clock_gettime(CLOCK_REALTIME, &ts))
        return (uint64_t)time(NULL);

    return (uint64_t)ts.tv_sec
         ^ (((uint64_t)ts.tv_nsec) << 32);
}
#else
static uint64_t time_seed(void)
{
    return (uint64_t)time(NULL);
}
#endif

/* Preferred output I/O block size.
 * Currently, about 128k blocks yield
 * maximum I/O throughput on most devices.
 * Note that this is a heuristic value,
 * and may be increased in the future.
*/
#ifndef  IO_BLOCK_SIZE
#define  IO_BLOCK_SIZE  262144
#endif

/* This is the Xorshift* pseudo-random number generator.
 * See https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
 * for details. This is an incredibly fast generator that
 * passes all but the MatrixRank test of the BigCrush
 * randomness test suite, with a period of 2^64-1.
 * Note that neither xorshift_state, nor the result of
 * this function, will ever be zero.
*/
static uint64_t xorshift_state;

static uint64_t xorshift_u64(void)
{
    xorshift_state ^= xorshift_state >> 12;
    xorshift_state ^= xorshift_state << 25;
    xorshift_state ^= xorshift_state >> 27;
    return xorshift_state * UINT64_C(2685821657736338717);
}

/* This function returns a number between (inclusive)
 * 0 and 999,999,999,999,999,999 using xorshift_u64()
 * above, using the exclusion method. Thus, there is
 * no bias in the results, and each digit should be
 * uniformly distributed in 0-9.
*/
static uint64_t quintillion(void)
{
    uint64_t result;

    do {
        result = xorshift_u64() & UINT64_C(1152921504606846975);
    } while (!result || result > UINT64_C(1000000000000000000));

    return result - UINT64_C(1);
}

/* This function returns a single uniformly random digit.
*/
static unsigned char digit(void)
{
    static uint64_t       digits_cache = 0;
    static unsigned char  digits_cached = 0;
    unsigned char         retval;

    if (!digits_cached) {
        digits_cache = quintillion();
        digits_cached = 17; /* We steal the first one! */
    } else
        digits_cached--;

    retval = digits_cache % (uint64_t)(10);
    digits_cache /= (uint64_t)(10);

    return retval;
}

static int parse_ulong(const char *src, unsigned long *to)
{
    const char   *end = src;
    unsigned long value;

    if (!src)
        return errno = EINVAL;

    errno = 0;
    value = strtoul(src, (char **)&end, 0);
    if (errno)
        return errno;

    if (end == src)
        return errno = EINVAL;
    while (*end)
        if (isspace(*end))
            end++;
        else
            return errno = EINVAL;

    if (to)
        *to = value;
    return 0;
}

int main(int argc, char *argv[])
{
    unsigned long lines, cols, line, col, seed;

    /* When parsing the command-line parameters,
     * use locale conventions. */
    setlocale(LC_ALL, "");

    /* Standard output should be fully buffered, if possible.
     * This only affects output speed, so we're not too worried
     * if this happens to fail. */
    (void)setvbuf(stdout, NULL, _IOFBF, (size_t)IO_BLOCK_SIZE);

    if (argc < 3 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s COLS LINES [ SEED ]\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program generates random decimal digits\n");
        fprintf(stderr, "0 - 9, separated by spaces, COLS per line,\n");
        fprintf(stderr, "LINES lines.  In total, COLS*LINES*2 bytes\n");
        fprintf(stderr, "will be used.\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "SEED is the optional seed for the Xorshift64*\n");
        fprintf(stderr, "pseudo-random number generator used in this program.\n");
        fprintf(stderr, "If omitted, current time is used as the seed.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    if (parse_ulong(argv[1], &cols) || cols < 1UL) {
        fprintf(stderr, "%s: Invalid number of digits per line.\n", argv[1]);
        return EXIT_FAILURE;
    }
    if (parse_ulong(argv[2], &lines) || lines < 1UL) {
        fprintf(stderr, "%s: Invalid number of lines.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (argc > 3) {
        if (parse_ulong(argv[3], &seed)) {
            fprintf(stderr, "%s: Invalid Xorshift64* seed.\n", argv[3]);
            return EXIT_FAILURE;
        }
    } else
        seed = time_seed();

    /* Since zero seed is invalid, we map it to ~0. */
    xorshift_state = seed;
    if (!xorshift_state)
        xorshift_state = ~(uint64_t)0;

    /* Discard first 1000 values to make the initial values unpredictable. */
    for (col = 0; col < 1000; col++)
        xorshift_u64();

    for (line = 0UL; line < lines; line++) {
        fputc('0' + digit(), stdout);
        for (col = 1UL; col < cols; col++) {
            fputc(' ', stdout);
            fputc('0' + digit(), stdout);
        }
        fputc('\n', stdout);

        /* Check for write errors. */
        if (ferror(stdout))
            return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
_

ラインバッファに切り替えると、より高速に処理でき、一度に各桁を出力する代わりに、fwrite()を1回実行します。出力がブロックデバイスである場合、部分的な(2の累乗ではない)書き込みを回避するために、ストリームを完全にバッファリングしたままにすることに注意してください。

_#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

#if _POSIX_C_SOURCE-199309 >= 0
static uint64_t time_seed(void)
{
    struct timespec  ts;

    if (clock_gettime(CLOCK_REALTIME, &ts))
        return (uint64_t)time(NULL);

    return (uint64_t)ts.tv_sec
         ^ (((uint64_t)ts.tv_nsec) << 32);
}
#else
static uint64_t time_seed(void)
{
    return (uint64_t)time(NULL);
}
#endif

/* Preferred output I/O block size.
 * Currently, about 128k blocks yield
 * maximum I/O throughput on most devices.
 * Note that this is a heuristic value,
 * and may be increased in the future.
*/
#ifndef  IO_BLOCK_SIZE
#define  IO_BLOCK_SIZE  262144
#endif

/* This is the Xorshift* pseudo-random number generator.
 * See https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
 * for details. This is an incredibly fast generator that
 * passes all but the MatrixRank test of the BigCrush
 * randomness test suite, with a period of 2^64-1.
 * Note that neither xorshift_state, nor the result of
 * this function, will ever be zero.
*/
static uint64_t xorshift_state;

static uint64_t xorshift_u64(void)
{
    xorshift_state ^= xorshift_state >> 12;
    xorshift_state ^= xorshift_state << 25;
    xorshift_state ^= xorshift_state >> 27;
    return xorshift_state * UINT64_C(2685821657736338717);
}

/* This function returns a number between (inclusive)
 * 0 and 999,999,999,999,999,999 using xorshift_u64()
 * above, using the exclusion method. Thus, there is
 * no bias in the results, and each digit should be
 * uniformly distributed in 0-9.
*/
static uint64_t quintillion(void)
{
    uint64_t result;

    do {
        result = xorshift_u64() & UINT64_C(1152921504606846975);
    } while (!result || result > UINT64_C(1000000000000000000));

    return result - UINT64_C(1);
}

/* This function returns a single uniformly random digit.
*/
static unsigned char digit(void)
{
    static uint64_t       digits_cache = 0;
    static unsigned char  digits_cached = 0;
    unsigned char         retval;

    if (!digits_cached) {
        digits_cache = quintillion();
        digits_cached = 17; /* We steal the first one! */
    } else
        digits_cached--;

    retval = digits_cache % (uint64_t)(10);
    digits_cache /= (uint64_t)(10);

    return retval;
}

static int parse_ulong(const char *src, unsigned long *to)
{
    const char   *end = src;
    unsigned long value;

    if (!src)
        return errno = EINVAL;

    errno = 0;
    value = strtoul(src, (char **)&end, 0);
    if (errno)
        return errno;

    if (end == src)
        return errno = EINVAL;
    while (*end)
        if (isspace(*end))
            end++;
        else
            return errno = EINVAL;

    if (to)
        *to = value;
    return 0;
}

int main(int argc, char *argv[])
{
    unsigned long lines, cols, line, col, seed;
    char         *oneline;

    /* When parsing the command-line parameters,
     * use locale conventions. */
    setlocale(LC_ALL, "");

    /* Standard output should be fully buffered, if possible.
     * This only affects output speed, so we're not too worried
     * if this happens to fail. */
    (void)setvbuf(stdout, NULL, _IOFBF, (size_t)IO_BLOCK_SIZE);

    if (argc < 3 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s COLS LINES [ SEED ]\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program generates random decimal digits\n");
        fprintf(stderr, "0 - 9, separated by spaces, COLS per line,\n");
        fprintf(stderr, "LINES lines.  In total, COLS*LINES*2 bytes\n");
        fprintf(stderr, "will be used.\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "SEED is the optional seed for the Xorshift64*\n");
        fprintf(stderr, "pseudo-random number generator used in this program.\n");
        fprintf(stderr, "If omitted, current time is used as the seed.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    if (parse_ulong(argv[1], &cols) || cols < 1UL) {
        fprintf(stderr, "%s: Invalid number of digits per line.\n", argv[1]);
        return EXIT_FAILURE;
    }
    if (parse_ulong(argv[2], &lines) || lines < 1UL) {
        fprintf(stderr, "%s: Invalid number of lines.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (argc > 3) {
        if (parse_ulong(argv[3], &seed)) {
            fprintf(stderr, "%s: Invalid Xorshift64* seed.\n", argv[3]);
            return EXIT_FAILURE;
        }
    } else
        seed = time_seed();

    /* Since zero seed is invalid, we map it to ~0. */
    xorshift_state = seed;
    if (!xorshift_state)
        xorshift_state = ~(uint64_t)0;

    /* Discard first 1000 values to make the initial values unpredictable. */
    for (col = 0; col < 1000; col++)
        xorshift_u64();

    /* Allocate memory for a full line. */
    oneline = malloc((size_t)(2 * cols + 1));
    if (!oneline) {
        fprintf(stderr, "Not enough memory for %lu column buffer.\n", cols);
        return EXIT_FAILURE;
    }

    /* Set spaces and terminating newline. */
    for (col = 0; col < cols; col++)
        oneline[2*col + 1] = ' ';
    oneline[2*cols-1] = '\n';

    /* Not needed, but in case a code modification treats it as a string. */
    oneline[2*cols] = '\0';

    for (line = 0UL; line < lines; line++) {
        for (col = 0UL; col < cols; col++)
            oneline[2*col] = digit();

        if (fwrite(oneline, 2*cols, 1, stdout) != 1)
            return EXIT_FAILURE; 
    }

    /* Check for write errors. */
    if (ferror(stdout))
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}
_

注:2016-11-18で編集された両方の例はensure数字の均一な分布(ゼロは除外されます。例 こちら を参照)さまざまな疑似乱数ジェネレータの比較と詳細については)。

例を使用してコンパイル

_gcc -Wall -O2 decimal-digits.c -o decimal-digits
_

オプションで、システム全体に_/usr/bin_を使用してインストールします

_Sudo install -o root -g root -m 0755 decimal-digits /usr/bin
_

1行あたりの桁数と行数が必要です。 _1000000000 / 100 / 2 = 5000000_(500万;合計バイトを列で割って2で割る)なので、次のように使用できます。

_./decimal-digits 100 5000000 > digits.txt
_

oPの要求に応じて、ギガバイトサイズの_digits.txt_を生成します。

プログラム自体は、効率よりも可読性を重視して記述されていることに注意してください。ここでの私の意図は、コードの効率性を示すことではなく、汎用のCインターフェイスではなく、POSIX.1と低レベルI/Oを使用することですが、費やした労力とのバランスの種類を簡単に確認できます専用ツールの開発とパフォーマンスの比較において、ワンライナーまたは短いシェルまたはawkスクリプトレットと比較して。

GNU Cライブラリを使用して、すべての文字出力に対してfputc()関数を呼び出すと、(間接的な関数呼び出しまたは条件付き-FILEインターフェースは実際にはかなり複雑で用途が広いと思います。この特定のIntel Core i5-4200Uラップトップでは、出力を_/dev/null_にリダイレクトします。最初の(fputc)バージョンは約11秒かかりますが、 -a-timeバージョンは1.3秒しかかかりません。

たまたまそういうプログラムやジェネレーターを書くのは、巨大なデータセットで遊ぶのが好きだからです。私はそのように変です。たとえば、私はかつて、すべての有限の正のIEEE-754浮動小数点値をテキストファイルに出力するプログラムを書いたが、解析時に正確に同じ値を生成するのに十分な精度であった。ファイルのサイズは数ギガバイト(おそらく4G程度)でした。有限の肯定的なfloatsはそれほど多くありません。これを使用して、そのようなデータを読み取って解析する実装を比較しました。

OPのような通常の使用例では、シェルスクリプトとスクリプトレット、ワンライナーがより良いアプローチです。タスク全体を完了するために費やされる時間が短縮されます。 (毎日別のファイルが必要な場合や、別のファイルを必要とする人がたくさんいる場合を除きます。まれに、上記のような専用のツールを使用すれば、労力を費やすことができます。)

38
Nominal Animal

この:

_ LC_ALL=C tr '\0-\377' \
             '[0*25][1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][x*]' \
    < /dev/urandom |
    tr -d x |
    fold -w 1 |
    paste -sd "$(printf '%99s\\n')" - |
    head -c1G
_

head実装が_-c_をサポートすることを前提としています)は、私のシステムではかなり高速に見えます。

trは、バイト範囲全体(0から255、8進数で0から0377)を変換します。最初の25バイトを0、次の25バイトを1、次に25 9残りを(250から255)に変換します。 "x"は(_tr -d x_自体が均一分布であることを前提として)均一分布が必要なため、破棄し(_/dev/urandom_を使用)、一部の桁にバイアスをかけません。

これにより、_/dev/urandom_のバイトの97%に1桁が生成されます。 _fold -w 1_は、1行あたり1桁にします。 _paste -s_は、99のスペース文字と1つの改行文字で構成されるセパレーターのリストを使用して呼び出されるため、各行に100のスペースで区切られた数字があります。

_head -c1G_は最初のGiB(230) その。最後の行は切り捨てられ、区切られないことに注意してください。あなたは2に切り捨てることができます30-1、不足している改行を手動で追加するか、10に切り捨てます9 代わりに、これらの200バイト行の5,000万バイト(_head -n 50000000_も標準/ポータブルコマンドになります)。

これらのタイミング(クアッドコアシステムでzshによって取得)は、CPU時間の消費場所を示します。

_LC_ALL=C tr '\0-\377'  < /dev/urandom  0.61s user 31.28s system 99% cpu 31.904 total
tr -d x  1.00s user 0.27s system 3% cpu 31.903 total
fold -w 1  14.93s user 0.48s system 48% cpu 31.902 total
paste -sd "$(printf '%99s\\n')" -  7.23s user 0.08s system 22% cpu 31.899 total
head -c1G > /dev/null  0.49s user 1.21s system 5% cpu 31.898 total
_

最初のtrはボトルネックであり、ほとんどがカーネルで費やされた時間です(乱数の生成を想定しています)。タイミングは、_/dev/uramdom_からバイトを取得できる速度とほぼ一致します(約19MiB /秒、ここでは、32MiB /秒の速度で/ dev/urandomの0.97バイトごとに2バイトを生成します)。 foldは、各バイトの後に改行文字を挿入するためだけに不合理な量のCPU時間(15秒)を費やしているようですが、私の場合は別のCPUで動作するため、全体の時間には影響しません( _-b_オプションを使用すると、効率がわずかに向上します。_dd cbs=1 conv=unblock_は、より良い代替手段のようです)。

_head -c1G_を廃止し、ファイルサイズに制限を設定することで数秒を節約できます(zshで_limit filesize 1024m_または他のほとんどのシェルでulimit -f "$((1024*1024))") (zshを含む))代わりにサブシェルで。

バイトごとに2桁を抽出すれば改善できるかもしれませんが、そのためには別のアプローチが必要になります。 trは256バイト配列の各バイトを検索するだけなので、上記は非常に効率的です。一度に2バイトではこれを行うことはできません。より複雑なアルゴリズムを使用してバイトのテキスト表現を計算する_hexdump -e '1/1 "%02u"'_などを使用すると、乱数の生成自体よりもコストが高くなります。それでも、私の場合のように、時間に余裕のあるCPUコアがある場合でも、数秒で削り落とすことができます。

と:

_< /dev/urandom LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -n250000000 -ve '500/1 "%02u" "\n"' |
  fold -w1 |
  paste -sd "$(printf '%99s\\n')" - > /dev/null
_

私は得ます(ただし、ここでは1,073,741,824ではなく1,000,000,000バイトです):

_LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' < /dev/urandom  0.32s user 18.83s system 70% cpu 27.001 total
tr -d x  2.17s user 0.09s system 8% cpu 27.000 total
hexdump -n250000000 -ve '500/1 "%02u" "\n"'  26.79s user 0.17s system 99% cpu 27.000 total
fold -w1  14.42s user 0.67s system 55% cpu 27.000 total
paste -sd "$(printf '%99s\\n')" - > /dev/null  8.00s user 0.23s system 30% cpu 26.998 total
_

全体的にはより多くのCPU時間ですが、私の4つのCPUコア間でより適切に分散されているため、実際にかかる時間は少なくなっています。現在、ボトルネックはhexdumpです。

行ベースのddの代わりにfoldを使用すると、実際にhexdumpが実行する必要がある作業量を削減し、CPU間の作業のバランスを改善できます。

_< /dev/urandom LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -ve '"%02u"' |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -
_

(ここでは、GNU ddをその_iflag=fullblock_および_status=none_に対して想定))とすると、次のようになります。

_LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' < /dev/urandom  0.32s user 15.58s system 99% cpu 15.915 total
tr -d x  1.62s user 0.16s system 11% cpu 15.914 total
hexdump -ve '"%02u"'  10.90s user 0.32s system 70% cpu 15.911 total
dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock  5.44s user 0.19s system 35% cpu 15.909 total
paste -sd "$(printf '%99s\\n')" - > /dev/null  5.50s user 0.30s system 36% cpu 15.905 total
_

ボトルネックとなっている乱数生成に戻ります。

@OleTangeで指摘されているように、opensslユーティリティを使用している場合は、それを使用してバイトの高速ランダムジェネレーター(特にAES命令を持つプロセッサー)を取得できます。

_</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom
_

私のシステムでは、_/dev/urandom_の1秒あたりのバイト数が15倍です。 (それが暗号的に安全なランダム性のソースの点で比較する方法についてコメントすることはできません).

_</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom 2> /dev/null | 
  LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -ve '"%02u"' |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -
_

今与える:

_openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom < /dev/zero 2>   1.13s user 0.16s system 12% cpu 10.174 total
LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]'  0.56s user 0.20s system 7% cpu 10.173 total
tr -d x  2.50s user 0.10s system 25% cpu 10.172 total
hexdump -ve '"%02u"'  9.96s user 0.19s system 99% cpu 10.172 total
dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock  4.38s user 0.20s system 45% cpu 10.171 total
paste -sd "$(printf '%99s\\n')" - > /dev/null
_

ボトルネックとなっているhexdumpに戻ります。

まだCPUに余裕があるので、これらのhexdumpのうち3つを並行して実行できます。

_</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom 2> /dev/null | 
  LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  (hexdump -ve '"%02u"' <&3 & hexdump -ve '"%02u"' <&3 & hexdump -ve '"%02u"') 3<&0 |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -
_

(_<&3_は、バックグラウンドで実行されたときに/ dev/nullでコマンドの標準入力を閉じるzsh以外のシェルに必要です)。

これで6.2秒に短縮され、CPUがほぼ完全に使用されます。

81

shuf が利用できる場合(最近GNU coreutilsが行う))、これを行うことができます:

time shuf -r -n $((512*1024*1024)) -i 0-9 | paste -sd "$(printf '%99s\\n')" -

私のVMでは、これはステファンの回答よりも約3:4だけ少し遅くなっています。

23
Digital Trauma

これが私が理解するのが簡単であることを望んでいる解決策です:

od -An -x /dev/urandom | tr -dc 0-9 | fold -w100 | awk NF=NF FS= | head -c1G
  • odは、/dev/randomから16進数の均一なストリームを作成します。
  • trは文字を取り除き、0-9桁のみを保持します
  • foldは、1行あたり100桁であることを保証します
  • awkは行内にスペースを挿入します
  • headは入力を1ギガバイトに切り捨てます
14
sam hocevar

これにはjotコマンドを使用できます。

jot -r 50000000 0 9 | fmt -w 200 > output.txt
6
gardenhead

これはStéphaneChazelasの方法に似ていますが、パフォーマンスを向上させるために64ビットを一度に読み取りました。分布は依然として均一ですが、以前のように最良のケースでは8桁だけではなく、8バイトごとに19桁が得られます。

Perl -nle 'BEGIN{$/=\8; $,=" "}
           $n = unpack("Q");
           next if $n >= 10000000000000000000;
           $s = sprintf("%019u", $n);
           Push @a, (split //, $s);
           if (@a >= 100) {print (splice @a, 0, 100);}' < /dev/urandom | head -c1G

32ビットプラットフォームでは、19ではなく9桁が毎回読み取られます。

6
phuclv

スピードが必要な場合は、コンパイルされたプログラミング言語を使用する際にNominal Animalに同意します。ただし、Cで独自のRNGコードを記述する必要はありません。C++ 11は、標準ライブラリの一部として優れたMersenne Twisterを提供しています。

#include <time.h>
#include <random>
#include <iostream>
using namespace std;

int main() {
    mt19937 gen(time(0)); 
    uniform_int_distribution<> dist(0,9);

    for(int j=0; j<5000000; j++){
        for (int i = 0; i < 99; i++) {  
            cout << dist(gen) << " ";
        }  
        cout << dist(gen) << endl;
    }
    return 0;
}

上記のコードはかなりシンプルで、出力をファイルにパイプするときに約1分かかります。 100桁の十分な大きさの文字列を作成し、その桁にハッキングすることで、はるかに速く処理できます。これにより、coutをすべての数字ではなくすべての行に呼び出すことができます。

#include <time.h>
#include <random>
#include <iostream>
using namespace std;

int main() {
    mt19937 gen(time(0)); 
    uniform_int_distribution<> dist(0,9);

    char line[201];
    for(int i=1; i<199; i++)
        line[i] = ' ';
    line[199] = '\n';
    line[200] = 0;

    for(int j=0; j<5000000; j++){
        for (int i = 0; i < 199; i += 2) {  
            line[i] = dist(gen)+'0';
        }  
        cout << line;
    }
    return 0;
}

このコードは私のマシンに約6秒かかります。これは標準出力であることを忘れないでください。そのため、ファイルにパイプしてください。

免責事項がいくつかあります。まず、これをWindows PCで書いています。ライブラリはすべてLinuxに存在すると思いますが、私が間違っている場合は、必ず指摘してください。

また、実際には、正確に5億の空白で区切られた数字を出力します。これは、技術的にはギガバイトですが、望んだとおりではない場合があります。 500万行、1行あたり100桁を出力します。違いが重要な場合は、行数を増やすことができます。私のWindowsボックスでは、ファイルは10 ^ 9バイトより少し大きいようですが、これは余分な改行文字と関係があると思います。

3
James Hollis

「ランダム」の定義に依存します。暗号的にランダムな場合は、優れたライブラリを入手して弾丸を噛み、実行されるまで待つ必要があります。

かなりランダムに見えるものが必要な場合は、簡単な方法を次に示します。

  1. 数GBの長さのファイルを取得します。あなたの好きな映画は良いでしょう。
  2. 繰り返しパターンを絞り出す簡単な方法であるGzip it
  3. 一度にニブル(ハーフバイト)でファイルを通過します。各値は0から15の間になります。1未満または10を超える値を破棄します。最初の10億の生存者のそれぞれから1を引き、それを数字として書き出します。

遅いマシンで実行するには1時間かかる場合があります。ほとんどの目的に十分な速度と十分なランダム性。

1
Malvolio
#!/bin/bash
FILE_CREAT='/tmp/testfile'
MAX_SIZE=$(( 1 * 1024 * 1024 ))
rm -rf ${FILE_CREAT}
while true
do
    STRING=''
    for (( i = 0 ; i < 100 ; i++ ))
    do
        NUM_RAN=$(cat /dev/urandom | tr -dc 0-9 | head -c 1)
        if [ $i -eq 0 ]
        then
            STRING=${NUM_RAN}
        else
            STRING=${STRING}' '${NUM_RAN}
        fi
    done
    echo ${STRING} >> $FILE_CREAT
    FILE_SIZE=$(du -s ${FILE_CREAT} | awk '{print $1}')
    if [ ${FILE_SIZE} -ge ${MAX_SIZE} ]
    then
        break
    fi
done
exit $1
1
NamNT