1

I need to be able to generate a random number between 2013 and 2044 (inclusive). I have tried the two scripts below, but the output is not quite random in either case. Many numbers are repeated, and others missing. Is there a way to get around this?

Here is the first script:

    i="1"
    x="2014"
    y="2044"
    while [ $i != 32 ]; do
            var=$RANDOM
            var=$[ $x + $var % ($y + 1 -$x) ]
            echo $var
            i=$[$i+1]
     done

Here is the second

    i="1"
    while [ $i != 32 ]; do
            var=$(shuf -i 2014-2044 -n1)
            echo $var
            i=$[$i+1]
    done

Thanks, Ciara

cohara
  • 111
  • 3
  • 19
  • 1
    `shuf` is nonstandard. It doesn't exist on OS X, for example. – kojiro Mar 20 '14 at 14:59
  • 2
    Please demonstrate how the output you receive differs from what you expect. Quite often properly random data differs from what people intuitively expect. A sample of size 32 is much too small to judge anyway. – tripleee Mar 20 '14 at 15:01
  • Could you perhaps use `/dev/urandom`? `dd if=/dev/urandom bs=(small) count=(small) | od -s` could get you some values to play with. – kojiro Mar 20 '14 at 15:02
  • If what you actually want is a randomized ordering of a range of numbers, please edit the question to clarify this requirement (or just mark as duplicate of one of the many similar questions). – tripleee Mar 20 '14 at 15:04
  • 1
    `$[...]` is a very old syntax; use `$((...))` instead. – chepner Mar 20 '14 at 15:13
  • Do you actually want a random permutation of the numbers between 2013 and 2044? – chepner Mar 20 '14 at 15:15
  • Hi. Yes @fedorqui I know about the shuf tool, I use it above. – cohara Mar 20 '14 at 16:21
  • @tripleee the output I get does not contain all the numbers between 2013 and 2044, I need all of them just once. I can't find another instance of someone asking this question before. – cohara Mar 20 '14 at 16:21
  • Thanks @chepner, I'll do that, and yes I want the number selection to be random. – cohara Mar 20 '14 at 16:22
  • http://stackoverflow.com/questions/1917045/randomizing-arg-order-for-a-bash-for-statement and http://stackoverflow.com/questions/886237/how-can-i-randomize-the-lines-in-a-file-using-a-standard-tools-on-redhat-linux are similat, although not precise duplicates. – tripleee Mar 20 '14 at 18:47
  • Also http://stackoverflow.com/questions/7742218/how-to-randomize-a-list-and-iterate-through-the-randomized-list-bash – tripleee Mar 20 '14 at 18:51

4 Answers4

2

When you generate x random numbers in the range [y,z] and x > z-y, you will have some repetitions. You may also encounter some numbers repeated in consecutive slots because when you take mod, a number of those random numbers will actually map onto the same number. If you increase your range, you will notice that the numbers are more random.

unxnut
  • 8,509
  • 3
  • 27
  • 41
2

This is the nature of random numbers. Let's try a run with 3100 iterations. We'll expect to get 100 entries for each number in [2014, 2044]:

#!/bin/bash
x=2014
y=2044
n=()
for ((i=1; i<=3100; i++)); do
    (( n[x + RANDOM % (y-x+1)] += 1 ))
done
for ((i=x; i<=y; i++)); do
    printf "%d\t%d\n" $i ${n[i]}
done
2014    95
2015    96
2016    98
2017    87
2018    96
2019    82
2020    111
2021    105
2022    113
2023    100
2024    102
2025    95
2026    89
2027    110
2028    102
2029    93
2030    107
2031    109
2032    115
2033    92
2034    108
2035    96
2036    94
2037    109
2038    91
2039    113
2040    90
2041    123
2042    82
2043    102
2044    95
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • Thanks @glenn jackman, is there any other way of shuffling through the numbers I want randomly, but so that each one is returned exactly once? – cohara Mar 20 '14 at 16:23
0

Since you want a random permutation, the random numbers you want to generate are actually indices into a list of the remaining unselected numbers. Something like

unselected=( {2014..2044} )
while (( ${#unselected[@]} > 0 )); do
    # Pick a random index
    index=$(( $RANDOM % ${#unselected[@]} ))
    echo ${unselected[index]}
    # Remove the value at that index
    unset unselected[index]
    unselected=( "${unselected[@]}" )
done
chepner
  • 497,756
  • 71
  • 530
  • 681
0
seq 2014 2044 | sort -R

This creates the range of numbers with seq, then reorders the lines randomly.

tripleee
  • 175,061
  • 34
  • 275
  • 318