Rational Number RNG
JavaScript (ES6), 81 bytes
Returns \$p/q\$ as [p,q]
.
f=(p=0,q=1)=>Math.random()<(g=(a,b)=>b?g(b,a%b):a)(p,q)/2?f(++p-q?p:!++q,q):[p,q]
Try it online!
Or Try some statistics!
How?
We recursively build the fractions \$p/q\$ in the relevant order, but without any explicit deduplication, until a random number picked in \$[0,1[\$ is greater than or equal to \$gcd(p,q)/2\$.
If \$p\$ and \$q\$ are not coprime, we have \$gcd(p,q)/2\ge1\$, which always triggers the recursive call. This is the expected behavior because the corresponding fraction must be discarded.
If \$p\$ and \$q\$ are coprime, we have \$gcd(p,q)/2=1/2\$, which is the desired probability of triggering the next call.
Commented
f = ( // f is a recursive function taking:
p = 0, // p = numerator, starting at 0
q = 1 // q = denominator, starting at 1
) => //
Math.random() < // pick a random number in [0,1[
(g = (a, b) => // g is a recursive function computing the gcd of 2 integers
b ? // if b is not equal to 0:
g(b, a % b) // recursive call with (b, a mod b)
: // else:
a // return a
)(p, q) / 2 ? // if our random number is less than gcd(p, q) / 2:
f( // do a recursive call:
++p - q ? p // increment p if p < q - 1
: !++q, // otherwise: set p to 0 and increment q
q //
) // end of recursive call
: // else:
[p, q] // stop recursion and return [p, q]
Ruby, 74 67 bytes
Probably not the golfiest solution for Ruby, but it was the first thing that sprung to mind. Increment the counter $.
(starts at 0) each time rand
is at least 0.5 to determine the index, then determine the list of rationals and output the right one.
-7 bytes from histocrat.
d,*a=1r
$.+=1until rand<0.5
a|=(0...d+=1).map{|n|n/d}until p *a[$.]
Try it online!
Julia, 61 49 71 bytes
p>q=rand()<.5 ? (p<q-1 ? (while gcd(p+=1,q)!=1 end;p>q) : 1>q+1) : p//q
In un-golfed form, this reads
function f(p,q) # define the function
if rand() < 0.5 # flip a coin.
if p < (q - 1) # if the coin gives heads, check if p < (q - 1)
while gcd(p+1, q) != 1 # if the greatest common divisor or p+1 and q is not equal to 1
p = p + 1 # we increment p, because otherwise we'd get a fraction we already saw.
end
return f(p, q) # recursively call f(p, q) (where p has been incremented)
else
return f(1, q+1) # if p is not greater than q - 1, we recursively call f(1, q+1)
end
else
return p//q # If the coin flip gives tails, we return the Rational number p//q
end
end
Now let's check to make sure we did this right:
julia> using UnicodePlots; data = [0>1 for _ in 1:10_000_000]; histogram(data)
┌ ┐
[0.0 , 0.05) ┤▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 5000106
[0.05, 0.1 ) ┤ 0
[0.1 , 0.15) ┤ 1168
[0.15, 0.2 ) ┤▇ 82600
[0.2 , 0.25) ┤ 0
[0.25, 0.3 ) ┤▇▇ 313684
[0.3 , 0.35) ┤▇▇▇▇▇▇▇▇ 1250427
[0.35, 0.4 ) ┤ 39124
[0.4 , 0.45) ┤ 310
[0.45, 0.5 ) ┤ 0
[0.5 , 0.55) ┤▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 2498798
[0.55, 0.6 ) ┤ 168
[0.6 , 0.65) ┤ 19403
[0.65, 0.7 ) ┤▇▇▇▇ 625484
[0.7 , 0.75) ┤ 77
[0.75, 0.8 ) ┤▇ 166131
[0.8 , 0.85) ┤ 2473
[0.85, 0.9 ) ┤ 47
└ ┘
Frequency
julia> for frac in [(0//1), (1//2), (1//3), (2//3), (1//4), (3//4), (1//5), (2//5), (3//5), (4//5), (1//6), (5//6)]
freq = count(x -> x == frac, data)/length(data)
println("P($(frac)) ≈ $freq")
end
P(0//1) ≈ 0.5000106
P(1//2) ≈ 0.2498798
P(1//3) ≈ 0.1250427
P(2//3) ≈ 0.0625484
P(1//4) ≈ 0.0313065
P(3//4) ≈ 0.0156477
P(1//5) ≈ 0.0077772
P(2//5) ≈ 0.0039116
P(3//5) ≈ 0.0019398
P(4//5) ≈ 0.0009654
P(1//6) ≈ 0.0004828
P(5//6) ≈ 0.0002473
Notice that P(5//6) ≈ 0.5 * P(1//6)
as required since 2//6
, 3//6
, and 4//6
are reducible fractions.
Try it online!