random choose


SUBMITTED BY: foxhunters

DATE: Aug. 8, 2016, 12:20 a.m.

FORMAT: Text only

SIZE: 6.2 kB

HITS: 8047

  1. #!/bin/bash
  2. # random-between.sh
  3. # Random number between two specified values.
  4. # Script by Bill Gradwohl, with minor modifications by the document author.
  5. # Corrections in lines 187 and 189 by Anthony Le Clezio.
  6. # Used with permission.
  7. randomBetween() {
  8. # Generates a positive or negative random number
  9. #+ between $min and $max
  10. #+ and divisible by $divisibleBy.
  11. # Gives a "reasonably random" distribution of return values.
  12. #
  13. # Bill Gradwohl - Oct 1, 2003
  14. syntax() {
  15. # Function embedded within function.
  16. echo
  17. echo "Syntax: randomBetween [min] [max] [multiple]"
  18. echo
  19. echo -n "Expects up to 3 passed parameters, "
  20. echo "but all are completely optional."
  21. echo "min is the minimum value"
  22. echo "max is the maximum value"
  23. echo -n "multiple specifies that the answer must be "
  24. echo "a multiple of this value."
  25. echo " i.e. answer must be evenly divisible by this number."
  26. echo
  27. echo "If any value is missing, defaults area supplied as: 0 32767 1"
  28. echo -n "Successful completion returns 0, "
  29. echo "unsuccessful completion returns"
  30. echo "function syntax and 1."
  31. echo -n "The answer is returned in the global variable "
  32. echo "randomBetweenAnswer"
  33. echo -n "Negative values for any passed parameter are "
  34. echo "handled correctly."
  35. }
  36. local min=${1:-0}
  37. local max=${2:-32767}
  38. local divisibleBy=${3:-1}
  39. # Default values assigned, in case parameters not passed to function.
  40. local x
  41. local spread
  42. # Let's make sure the divisibleBy value is positive.
  43. [ ${divisibleBy} -lt 0 ] && divisibleBy=$((0-divisibleBy))
  44. # Sanity check.
  45. if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o ${min} -eq ${max} ]; then
  46. syntax
  47. return 1
  48. fi
  49. # See if the min and max are reversed.
  50. if [ ${min} -gt ${max} ]; then
  51. # Swap them.
  52. x=${min}
  53. min=${max}
  54. max=${x}
  55. fi
  56. # If min is itself not evenly divisible by $divisibleBy,
  57. #+ then fix the min to be within range.
  58. if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then
  59. if [ ${min} -lt 0 ]; then
  60. min=$((min/divisibleBy*divisibleBy))
  61. else
  62. min=$((((min/divisibleBy)+1)*divisibleBy))
  63. fi
  64. fi
  65. # If max is itself not evenly divisible by $divisibleBy,
  66. #+ then fix the max to be within range.
  67. if [ $((max/divisibleBy*divisibleBy)) -ne ${max} ]; then
  68. if [ ${max} -lt 0 ]; then
  69. max=$((((max/divisibleBy)-1)*divisibleBy))
  70. else
  71. max=$((max/divisibleBy*divisibleBy))
  72. fi
  73. fi
  74. # ---------------------------------------------------------------------
  75. # Now, to do the real work.
  76. # Note that to get a proper distribution for the end points,
  77. #+ the range of random values has to be allowed to go between
  78. #+ 0 and abs(max-min)+divisibleBy, not just abs(max-min)+1.
  79. # The slight increase will produce the proper distribution for the
  80. #+ end points.
  81. # Changing the formula to use abs(max-min)+1 will still produce
  82. #+ correct answers, but the randomness of those answers is faulty in
  83. #+ that the number of times the end points ($min and $max) are returned
  84. #+ is considerably lower than when the correct formula is used.
  85. # ---------------------------------------------------------------------
  86. spread=$((max-min))
  87. # Omair Eshkenazi points out that this test is unnecessary,
  88. #+ since max and min have already been switched around.
  89. [ ${spread} -lt 0 ] && spread=$((0-spread))
  90. let spread+=divisibleBy
  91. randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min))
  92. return 0
  93. # However, Paulo Marcel Coelho Aragao points out that
  94. #+ when $max and $min are not divisible by $divisibleBy,
  95. #+ the formula fails.
  96. #
  97. # He suggests instead the following formula:
  98. # rnumber = $(((RANDOM%(max-min+1)+min)/divisibleBy*divisibleBy))
  99. }
  100. # Let's test the function.
  101. min=-14
  102. max=20
  103. divisibleBy=3
  104. # Generate an array of expected answers and check to make sure we get
  105. #+ at least one of each answer if we loop long enough.
  106. declare -a answer
  107. minimum=${min}
  108. maximum=${max}
  109. if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then
  110. if [ ${minimum} -lt 0 ]; then
  111. minimum=$((minimum/divisibleBy*divisibleBy))
  112. else
  113. minimum=$((((minimum/divisibleBy)+1)*divisibleBy))
  114. fi
  115. fi
  116. # If max is itself not evenly divisible by $divisibleBy,
  117. #+ then fix the max to be within range.
  118. if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then
  119. if [ ${maximum} -lt 0 ]; then
  120. maximum=$((((maximum/divisibleBy)-1)*divisibleBy))
  121. else
  122. maximum=$((maximum/divisibleBy*divisibleBy))
  123. fi
  124. fi
  125. # We need to generate only positive array subscripts,
  126. #+ so we need a displacement that that will guarantee
  127. #+ positive results.
  128. disp=$((0-minimum))
  129. for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do
  130. answer[i+disp]=0
  131. done
  132. # Now loop a large number of times to see what we get.
  133. loopIt=1000 # The script author suggests 100000,
  134. #+ but that takes a good long while.
  135. for ((i=0; i<${loopIt}; ++i)); do
  136. # Note that we are specifying min and max in reversed order here to
  137. #+ make the function correct for this case.
  138. randomBetween ${max} ${min} ${divisibleBy}
  139. # Report an error if an answer is unexpected.
  140. [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] \
  141. && echo MIN or MAX error - ${randomBetweenAnswer}!
  142. [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] \
  143. && echo DIVISIBLE BY error - ${randomBetweenAnswer}!
  144. # Store the answer away statistically.
  145. answer[randomBetweenAnswer+disp]=$((answer[randomBetweenAnswer+disp]+1))
  146. done
  147. # Let's check the results
  148. for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do
  149. [ ${answer[i+disp]} -eq 0 ] \
  150. && echo "We never got an answer of $i." \
  151. || echo "${i} occurred ${answer[i+disp]} times."
  152. done
  153. exit 0

comments powered by Disqus