I am writing a Monty Hall simulator and found it necessary to generate a number within a range excluding one number.
It seemed easy, so I naively wrote:
(The g/... functions are part of my personal library. Their use should be clear enough):
(defn random-int-excluding "Generates a random number between min-n and max-n; excluding excluding-n. min-n is inclusive, while max-n is exclusive." [min-n max-n excluding-n rand-gen] (let [rand-n (g/random-int min-n max-n rand-gen) rand-n' (if (= rand-n excluding-n) (inc rand-n) rand-n)] (g/wrap rand-n' min-n (inc max-n))))
This generates a random number within the range and, if it is equal to the excluded number, adds it; if necessary. Of course, this led to the fact that after the excluded quantity doubled, it was chosen, since it was chosen if it was selected or excluded. Samples of output frequencies for the range from 0 to 10 (max. Exception), with the exception of 2:
([0 0.099882] [1 0.100355] [3 0.200025] [4 0.099912] [5 0.099672] [6 0.099976] [7 0.099539] [8 0.100222] [9 0.100417])
Then I read this answer , which seemed much simpler and based on it, wrote:
(defn random-int-excluding "Generates a random number between min-n and max-n; excluding excluding-n. min-n is inclusive, while max-n is exclusive." [min-n max-n excluding-n rand-gen] (let [r1 (g/random-int min-n excluding-n rand-gen) r2 (g/random-int (inc excluding-n) max-n rand-gen)] (if (g/random-boolean rand-gen) r1 r2)))
In principle, it divides the range into 2 smaller ranges: from minus to the number excluded, and from the excluded number + 1 to max. It generates a random number from these ranges, then randomly selects one of them. Unfortunately, although, as I noted in the answer, this gives distorted results if both sections do not have the same size. Samples of output frequencies; same conditions as above:
([0 0.2499497] [1 0.2500795] [3 0.0715849] [4 0.071297] [5 0.0714366] [6 0.0714362] [7 0.0712715] [8 0.0715285] [9 0.0714161])
Please note that the number of parts of a smaller range before the excluded number is much more likely. To fix this, I would have to skew it to select numbers from a larger range more often, and indeed, I am not good enough at maths in general to understand how to do this.
I looked at the accepted answer from a related question, but for me it seems like a version of my first attempt, which allows to exclude more than one number. I would expect, compared to what the answering machine claimed, the numbers at the end of the exclusion range would be preferable, because if you select a number that is within the excluded range, it simply increases the number outside the range.
Since this will be one of the most named functions in the simulation, I would really like to avoid the “brute force” method of the cycle, while the generated number is excluded, since the range will contain only 3 numbers, so there is a 1/3 chance that You will need to try every attempt.
Does anyone know a simple algorithm for choosing a random number from a continuous range, but excludes one number?