Использование bash для создания случайных последовательных числовых последовательностей из заданного диапазона

Используя заданный диапазон значений, я пытаюсь создать случайные экземпляры из трех смежных чисел.

Например, учитывая диапазон 1-100и желая три (не перекрывающихся) случайных экземпляра трех смежных чисел, результат будет примерно таким:

4 5 6
50 51 52
32 33 34

Я пытался использовать команду shuf, например,

shuf -i 1-100 -n 3

но это, кажется, не позволяет генерировать последовательности случайных непрерывных чисел. Есть идеи?

2 ответа

Решение

Можно генерировать тройки равномерно и без необходимости тестирования и отклонения перекрытий.

Задача сводится к тому, чтобы найти случайную последовательность размером k {a1ak} из {1…N −2} так, чтобы минимальная разница между любыми двумя значениями подпоследовательности была не менее 3. (N −2, потому что значения выбраны первые значения каждой тройки, поэтому наибольшее не может быть больше, чем N-2.)

Это можно сделать, начав со случайной упорядоченной подпоследовательности {a'1a'k} из {1…N- ((k−1) × 2 + 2)}, а затем установив для каждого ai значение a'i+2 (я-1). Наконец, последовательность может быть случайным образом перемешана.

Это может быть легко обобщено, чтобы найти кортежи размера m.

В Баш:

# tuples n k m
tuples () { 
    local -i n=${1:-100} k=${2:-3} m=$((${3:-3}-1))
    if ((n < k*m + k)); then return 1; fi
    local -i i=0 a
    for a in $(shuf -i 1-$((n - k * m)) -n $k | sort -n); do
        # -s'  ' to work around a bug in coreutils 8.20 and 8.21
        seq -s'  ' $((a+i)) $((a+i+m))
        i+=m
    done | shuf
}

Основной ответ состоит в том, чтобы сгенерировать три отдельных случайных значения из диапазона 1 .. (100 - 3 + 1) или 1..98, а затем получить n,n+1,n+2 из каждого значения n.

Это только оставляет вас с непересекающимся требованием. Вам необходимо проверить, что абсолютное значение разрыва между любыми двумя из трех чисел составляет не менее 3; если нет, сгенерируйте новый номер. Вам нужно решить, нормально ли производить (при условии, что "случайное поколение" произвело перестановку {1, 4, 7}):

1 2 3
4 5 6
7 8 9

или должен ли быть разрыв между наборами чисел. Если должен быть разрыв, то вы проверяете, что расстояние между парами сгенерированных значений составляет не менее 4, а не 3.

Решив потребовать промежуток, по крайней мере, один, между наборами по 3 значениям, я получил следующий сценарий:

#!/bin/bash

min=1           # Minimum value
max=100         # Maximum value
adj=3           # Number of adjacent values per set
num=3           # Number of sets

r_min=$min
r_max=$(($max - $adj + 1))

base=()
while [ ${#base[*]} -lt $num ]
do
    next=$(($RANDOM % ($r_max - $r_min + 1) + $r_min))
    ok=yes
    for n in ${base[@]}
    do
        gap=$(($next - $n))
        [ $gap -lt 0 ] && gap=$((- $gap))
        if [ $gap -le $adj ]
        then ok=no; break
        fi
    done
    if [ $ok = yes ]
    then base+=( $next )
    fi
done

for n in ${base[@]}
do
    for ((i = 0; i < $adj; i++))
    do printf "%4d" $(($n + $i))
    done
    echo
done

Обратите внимание, что если количество наборов точек и количество точек в наборе ($num а также $adj в коде) получить слишком большой, вы можете получить бесконечный цикл, так как не хватает возможностей. Например, с $adj на 3, настройка $num до 25 и более гарантирует бесконечные циклы; Вы можете легко столкнуться с неприятностями задолго до этого.

Пример прогонов:

$ bash randcont.sh               
  16  17  18
  92  93  94
   6   7   8
$ bash randcont.sh
  81  82  83
  40  41  42
  13  14  15
$ bash randcont.sh
  61  62  63
  71  72  73
  23  24  25
$ bash randcont.sh
  54  55  56
   7   8   9
  46  47  48
$

Существует механизм смещения для генерации случайных чисел - смещение в сторону меньших чисел. Если это проблема, вы также можете решить, как ее исправить.

Я не уверен, что это лучший метод; Есть, вероятно, некоторые методы, которые позволяют использовать менее грубую силу и невежество. Но он работает "ОК" на требованиях к образцу.

Другие вопросы по тегам