random-utils.scm 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. (library (random-utils)
  2. (export make-random-integer-generator
  3. try-for-probability
  4. choose-random-element
  5. fisher-yates-shuffle)
  6. (import
  7. (except (rnrs base) error)
  8. (only (guile) lambda* λ values error
  9. ;; random numbers
  10. random:uniform
  11. ;; vectors
  12. list->vector
  13. vector-ref
  14. vector?)
  15. ;; SRFI-27 for random number utilities
  16. (srfi srfi-27)))
  17. (define make-random-integer-generator
  18. (lambda* (#:key seed)
  19. "Get a procedure for generating uniformly distributed
  20. random integers from 0 up to a not included bound, which is
  21. seeded by the keyword argument seed, which must be a
  22. positive integer."
  23. (cond
  24. [seed
  25. (let ([rand-src (make-random-source)])
  26. ;; Set the given seed to guarantee same results for
  27. ;; same invokations.
  28. (random-source-pseudo-randomize! rand-src 0 seed)
  29. ;; Obtain a procedure, which gives uniformly
  30. ;; distributed integers.
  31. (random-source-make-integers rand-src))]
  32. [else
  33. (let ([rand-src (make-random-source)])
  34. ;; Try to make the random source truly random. How
  35. ;; this works depends on the specific implementation
  36. ;; of SRFI-27.
  37. (random-source-randomize! rand-src)
  38. (random-source-make-integers rand-src))])))
  39. (define try-for-probability
  40. (lambda* (probability #:key (random-state #f))
  41. "Generate a random inexact uniformly distributed float
  42. between [0,1) and test, if it is less than the given
  43. PROBABILITY. Return #t if the number is smaller than the
  44. given PROBABILITY and #t if the number is equal of bigger
  45. the given PROBABILITY. If a RANDOM-STATE is given, use the
  46. RANDOM-STATE to generate the random number and return both
  47. the number and the updated RANDOM-STATE."
  48. (cond
  49. [random-state
  50. (values (< (random:uniform random-state) probability)
  51. random-state)]
  52. [else
  53. (< (random:uniform) probability)])))
  54. (define choose-random-element
  55. (lambda* (seq #:key (rand-int-gen (make-random-integer-generator)))
  56. (cond
  57. [(pair? seq)
  58. (let* ([vec (list->vector seq)]
  59. [random-index (rand-int-gen (vector-length vec))])
  60. (vector-ref vec random-index))]
  61. [(vector? seq)
  62. (vector-ref seq (rand-int-gen (vector-length seq)))]
  63. [else
  64. (error "expected sequence of type list or vector, got:" seq)])))
  65. (define fisher-yates-shuffle
  66. (lambda* (lst #:key (seed #f))
  67. (let ([get-rand-int (make-random-integer-generator #:seed seed)]
  68. [lst-as-vec (list->vector lst)])
  69. (let loop
  70. ;; Build up a list as result, which contains the elements of the
  71. ;; original list.
  72. ([result '()]
  73. ;; The list needs to have the same amount of elements as the original
  74. ;; list.
  75. [elements-to-pick (vector-length lst-as-vec)])
  76. (cond
  77. [(zero? elements-to-pick) result]
  78. [else
  79. (let*
  80. ;; Get a random number. [0,limit)
  81. ;; example: limit = 10, rand-int = 9
  82. ([rand-int (get-rand-int elements-to-pick)]
  83. ;; Get randomly an existing value from the vector of values, which
  84. ;; was created from the given list.
  85. ;; example: val = 9th value
  86. [val (vector-ref lst-as-vec rand-int)])
  87. ;; At the position, where we go the value from, set another value,
  88. ;; overwriting the value we already picked. Overwrite it with the
  89. ;; value, which otherwise cannot be picked any longer, as we count
  90. ;; down the elements-to-pick and lower the limit for the random
  91. ;; integer generation.
  92. ;; There are 2 cases here:
  93. ;; Case 1: The picked value was already the value at the position (-
  94. ;; elements-to-pick 1). In this case, it does not matter, that the
  95. ;; value can not be picked again and we only write it back to its own
  96. ;; position.
  97. ;; Case 2: The picked value was any value at an index lower than (-
  98. ;; elements-to-pick 1). In this case, we keep the possibility, that
  99. ;; the value at (- elements-to-pick 1) can be picked in the next
  100. ;; iteration, by writing that value to the position of the picked
  101. ;; value.
  102. ;; This way, as the limit for random integers gets lower, all values
  103. ;; will eventually be picked.
  104. ;; Save the value at the highest possible index, so that it can be
  105. ;; picked next iteration. Overwrite already picked value.
  106. (vector-set! lst-as-vec
  107. rand-int
  108. ;; Take the value at the highest index.
  109. (vector-ref lst-as-vec
  110. (- elements-to-pick 1)))
  111. (loop
  112. ;; Add the randomly chosen value to the list of values.
  113. (cons val result)
  114. ;; Count down the elements, which we still need to pick.
  115. (- elements-to-pick 1)))])))))