web-dev-qa-db-ja.com

年間隔で、ランダムな日付のシーケンスをどのように生成できますか?

ここで必要なのは、年の範囲(1987年から2017年)を指定して6つの日付を生成するコマンドです。例えば:

12/10/1987
30/04/1998
22/02/2014
17/08/2017
19/07/2011
14/05/2004

sedgawkなどを使用して、それをどのように行うことができますか?

12
Raymond83

問題を、最初の可能な日付を表す数値と最後の可能な日付(実際には最後の可能な直後の日付)を表す数値の間で、UNIXエポック形式の乱数を生成することに変えることができます。それ以外はすべて、標準の日付変換によって処理されます。 gawkbash(浮動小数点対15ビット整数)よりも乱数解決が優れているため、gawkを使用します。 Rand()の結果Nは0 <= N <1のような浮動小数点であることに注意してください。これが上限を下回る理由であり、ローリングできない制限です。結果の数のオプションの3番目のパラメーターがあります。

#!/usr/bin/gawk -f
BEGIN {
    first=mktime(ARGV[1] " 01 01 00 00 00")
    last=mktime(ARGV[2]+1 " 01 01 00 00 00")
    if (ARGC == 4) { num=ARGV[3] } else { num=1 }
    ARGC=1
    range=last-first
    srand(sprintf("%d%06d", systime(), PROCINFO["pid"]))
    for (i=1; i <= num; i++) {
        print strftime("%d/%m/%Y", range*Rand()+first)
    }
}   

例えば:

./randomdate.gawk 1987 2017 6
26/04/1992
28/04/2010
21/04/2005
17/02/2010
06/10/2016
04/04/1998
10
A.B

dateshufおよびxargsの場合:

開始日と終了日を「1970-01-01 00 000:00 UTCからの秒数」に変換し、shufを使用してこの範囲の6つのランダムな値を出力します。この結果をxargsにパイプし、値を目的の日付形式に変換します。

編集: 2017年の日付を出力に含める場合は、1年-1s(2017-12-31 23:59:59)から終了日まで。 shuf -iは、開始と終了を含む乱数を生成します。

shuf -n6 -i$(date -d '1987-01-01' '+%s')-$(date -d '2017-01-01' '+%s')\
 | xargs -I{} date -d '@{}' '+%d/%m/%Y'

出力例:

07/12/1988
22/04/2012
24/09/2012
27/08/2000
19/01/2008
21/10/1994
19
Freddy

Perl:

Perl -MTime::Piece -sE '
    my $t1 = Time::Piece->strptime("$y1-01-01 00:00:00", "%Y-%m-%d %H:%M:%S");
    my $t2 = Time::Piece->strptime("$y2-12-31 23:59:59", "%Y-%m-%d %H:%M:%S");
    do {
        my $time = $t1 + int(Rand() * ($t2 - $t1));
        say $time->dmy;
    } for 1..6;
' -- -y1=1987 -y2=2017

出力例:

10-01-1989
30-04-1995
10-12-1998
02-01-2016
04-06-2006
11-04-1987
10
glenn jackman

以下がoneの方法で、ほとんどがawkです:

#!/bin/sh

[ "$end" -ge "$start" ] || exit 1

awk -v start=$1 -v end=$2 '
  BEGIN {
    srand();
    for(i=1; i <= 6; i++)
      printf "%02d/%02d/%d\n", 1 + Rand() * 28, 1 + Rand() * 12, start + Rand() * (end-start);
  }
' < /dev/null

シェルスクリプトは2つのパラメーターを取り、それらを変数としてawkに渡します。awkは入力を読み取らず、BEGINブロックですべての作業を行います。

乱数ジェネレータをシードした後、printfステートメントで6回ループします。そのprintステートメントは、1〜28(1月は安全)、1〜12、月、および指定された開始年と終了年の間の乱数を生成して、範囲内の可能な日付のサブセットを選択します。ランダムですが、完全にカバーされているわけではありません。29〜31日が含まれている月は表示されません。

別の方法では、GNU日付とbash機能を使用します。

#!/bin/bash

start=$1
end=$2

[[ "$start" -le "$end" ]] || exit 1

startsec=$(date -d "1/1/$start" +%s)
for((i=1; i<=6; i++))
do
  r=$((RANDOM % (1 + end - start)*365*24*60*60))
  date -d @$((startsec + r)) +%d/%m/%Y
done

これは、開始日の1月1日の秒以降の秒数を計算することで機能します。次に、ループごとに、追加するオフセット秒数をランダムに指定します。乱数は、指定された範囲にわたる秒数に制限されます。 GNU date次に、その日付を目的の形式に操作します。

9
Jeff Schaller

年数の差が約90未満の場合は、$RANDOM bashの変数を使用して、日数でオフセットを指定し、dateコマンドの制限された機能を使用して計算を実行します。

#!/bin/bash
s=$(date +%s -d "1/1/$1")          # start in seconds since 1 Jan 1970
e=$(date +%s -d "1/1/$(($2+1))")   # start of end year +1 in seconds
days=$(((e-s)/(24*3600)))          # number of days from start to end
factor=$((32767/$days))            # RANDOM is 0 to 32767. See how many
toobig=$(($factor*$days))          # exact multiples of days.
                                   # if RANDOM is too large, draw again
for((i=0;i<${3:-1};i++))           # produce $3 random dates
do
    r=$RANDOM                      # find a random number < toobig
    while (( r >= toobig ))        # if toobig, then loop.
    do r=$RANDOM
    done
    offset=$(($r/$factor))         # get (0,days) from (0,factor*days)
    # output horrible day/month/year for N days past start date
    date -d "$offset days 1/1/$1" +%d/%m/%Y
done

乱数を選択する内部ループは、バイアスを修正しようとします。乱数ソースが0、1、2、3、4、5、6、7、8、または9を等しく与えることができ、0と3の間の乱数が必要な場合、ソースから0または1を取得する場合2または3の場合は0を報告し、1または4の場合は2を報告し、6または7の場合は3を報告し、8または9の場合はソースからのこの数を無視します。 。

4
icarus