Рациональные числа перечисляются.Например, этот код находит k-тое рациональное значение в открытом интервале 0..1 с порядком, в котором {n1, d1}
предшествует {n2, d2}
, если (d1<d2 || (d1==d2 && n1<n2))
при условии, что {n,d}
является взаимно простым.
RankedRational[i_Integer?Positive] :=
Module[{sum = 0, eph = 1, den = 1},
While[sum < i, sum += (eph = EulerPhi[++den])];
Select[Range[den - 1], CoprimeQ[#, den] &][[i - (sum - eph)]]/den
]
In[118]:= Table[RankedRational[i], {i, 1, 11}]
Out[118]= {1/2, 1/3, 2/3, 1/4, 3/4, 1/5, 2/5, 3/5, 4/5, 1/6, 5/6}
Теперь яхотел бы генерировать случайные рациональные числа, учитывая верхнюю границу знаменателя, своего рода равномерно, так что для достаточно большого знаменателя рациональные числа будут равномерно распределены по единичному интервалу.
Интуитивно, можно выбрать среди всех рациональных чисел:малые знаменатели с равными весами:
RandomRational1[maxden_, len_] :=
RandomChoice[(Table[
i/j, {j, 2, maxden}, {i,
Select[Range[j - 1], CoprimeQ[#, j] &]}] // Flatten), len]
Можно ли более эффективно генерировать случайные рациональные числа с этим распределением, не создавая их всех?Это не займет много времени, чтобы эта таблица стала огромной.
In[197]:= Table[RankedRational[10^k] // Denominator, {k, 2, 10}]
Out[197]= {18, 58, 181, 573, 1814, 5736, 18138, 57357, 181380}
Или, возможно, можно эффективно генерировать рациональные числа с ограниченным знаменателем, имеющим другое распределение "по ощущениям, как единообразное"?
EDIT Это код Mathematica, который выполняет генерацию принятия-отклонения, предложенную btilly.
Clear[RandomFarey];
RandomFarey[n_, len_] := Module[{pairs, dim = 0, res, gcds},
Join @@ Reap[While[dim < len,
gcds = cfGCD[pairs = cfPairs[n, len - dim]];
pairs = Pick[pairs, gcds, 1];
If[pairs =!= {},
dim += Length@Sow[res = pairs[[All, 1]]/pairs[[All, 2]]]];
]][[2, -1]]
]
Следующие скомпилированные функции генерируют пары целых чисел {i,j}
, такие что 1<=i < j<=n
:
cfPairs =
Compile[{{n, _Integer}, {len, _Integer}},
Table[{i, RandomInteger[{i + 1, n}]}, {i,
RandomChoice[2 (n - Range[n - 1])/(n (n - 1.0)) -> Range[n - 1],
len]}]];
и следующая скомпилированная функция вычисляет gcd.Предполагается, что входные данные представляют собой пару натуральных чисел.
cfGCD = Compile[{{prs, _Integer, 1}}, Module[{a, b, p, q, mod},
a = prs[[1]]; b = prs[[2]]; p = Max[a, b]; q = Min[a, b];
While[q > 0, mod = Mod[p, q]; p = q; q = mod]; p],
RuntimeAttributes -> Listable];
Затем
In[151]:= data = RandomFarey[12, 10^6]; // AbsoluteTiming
Out[151]= {1.5423084, Null}
In[152]:= cdf = CDF[EmpiricalDistribution[data], x];
In[153]:= Plot[{cdf, x}, {x, 0, 1}, ImageSize -> 300]