カードをシャッフルする

元ネタは カードをシャッフルする ですが、以下のようなものがシャッフルアルゴリズムでは良く用いられます。 引数があれば引数の値を用い、引数がなければ 100 を用いるのに三項演算子を用いています。

#! /usr/bin/gawk -f
BEGIN {

    # 毎回同じなのも嫌なので srand() で初期化
    srand();

    # シャッフルします
    rand_array(ARGV[1] ? ARGV[1] : 100);

    # シャッフルしたものを表示
    for (i = 1; i <= num_arr; i++) {
        print array[i];
    }
}

# ランダムな配列を生成
function rand_array(n) {
    for (i = 1; i <= n; i++) {
        array[i] = i;
    }
    shuffle_array(array);
}

# 配列をシャッフル
function shuffle_array(arr,    tmp) {
    for (a in arr) {
        num_arr++;
    }
    for (i = num_arr; i >= 1; i--) {
        j = int((i + 1) * rand()) + 1;
        tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

実行してみます。

$ gawk -f shuffle_2.awk 10
5
10
3
8
1
2
4
7
6
9

本当にシャッフルされているかどうかを以下のようにして確かめます。 これはダブリがあれば引数で指定している数字よりも少なくなるため、指定した引数と同じであることを検証するものです。 最後の '$0=$1' は '{print $1}' でもいいのですが、$1 を $0 に代入することで、代入が成立した場合に真になり、'{$0 = $1; print $0}' と同じになります。

$ gawk -f shuffle_2.awk 10 | sort | uniq | wc | gawk '$0=$1'
10

確かにシャッフルされているようです。

連想配列で呼び出す

awk の配列は連想配列なので、そのまま呼び出せばシャッフルされます。

#! /usr/bin/gawk -f
BEGIN {

    # 配列を生成
    for (i = 1; i <= 10; i++) {
        array[i] = i;
    }

    # 連想配列として配列を呼び出す
    for (i in array) {
        print array[i];
    }
}

実行してみます。

$ gawk -f shuffle_3.awk
4
5
6
7
8
9
10
1
2
3

gawk には裏技があって、環境変数 WHINY_USERS (弱っちいユーザー) をセットするとソートされて出てきます。 何とも不名誉な環境変数です。

$ WHINY_USERS=1 gawk -f shuffle_3.awk
1
10
2
3
4
5
6
7
8
9