Подтвердить что ты не робот

Генерация случайных рациональных чисел

Рационалы перечислимы. Например, этот код находит 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}

Или, может быть, можно эффективно генерировать рациональные решения с ограниченным знаменателем, имеющим другое "чувственно подобранное" распределение?


РЕДАКТИРОВАТЬ. Это код 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 += [email protected][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]

enter image description here

4b9b3361

Ответ 1

Я настоятельно рекомендую посмотреть "угадать номер" . игра для произвольных рациональных чисел? для некоторого вдохновения в отношении вашей основной проблемы.

Если ваша цель должна быть примерно одинаковой как можно скорее, и вы не возражаете против выбора разных рациональных методов с разными вероятностями, должен быть эффективен следующий алгоритм.

lower = fractions.Fraction(0)
upper = fractions.Fraction(1)

while lower < upper:
    mid = (upper + lower)/2
    if 0 == random_bit():
        upper = largest_rational_under(mid, denominator_bound)
    else:
        lower = smallest_rational_over_or_equal(mid, denominator_bound)

Обратите внимание, что обе эти вспомогательные функции можно вычислить, пройдя дерево Стерн-Броко к середине. Также обратите внимание, что с некоторыми незначительными изменениями вы можете легко преобразовать это в итеративный алгоритм, который выплевывает последовательность рациональных чисел и в конечном итоге будет сходиться с равным вероятностью в любом месте интервала. Я считаю это свойство приятным.


Если вы хотите, чтобы точное распределение было изначально указано, а rand(n) дает случайное целое число от 1 до n, тогда следующий псевдокод будет работать для знаменателя n:

Try:
    k = rand(n * (n+1) / 2)
    do binary search for largest j with j * (j-1) / 2 < k
    i = k - (j * (j-1) / 2)
    if (i, j) are not relatively prime:
        redo Try
answer = i/j

В среднем для больших n вам нужно Try около 2,55 раза. Поэтому на практике это должно быть довольно эффективно.

Ответ 2

С оценкой знаменателя рациональность неравномерно распределена (например, 1/2 отделяется от всего остального с помощью хорошего пробела.

Тем не менее, что-то вроде

In[300]:= Rationalize[RandomReal[1, 10], 0.001]

Out[300]= {17/59, 45/68, 11/31, 9/16, 1/17, 13/22, 7/10, 1/17, 5/21, 8/39}

работает для вас?

Ответ 3

Вот некоторые случайные мысли о проблеме, которую вы поднимаете. Я не тщательно проверил математику, чтобы я мог быть здесь 1 или там. Но он представляет собой своеобразное рассуждение, за которым я последую.

Рассмотрим только фракции в интервале (0,1). Это намного проще. Мы можем иметь дело позже с 1/1 и неправильными фракциями.

Stern - Brocot Tree однозначно перечисляет каждую уменьшенную положительную общую дробь (и, следовательно, каждое положительное рациональное число, меньшее или равное единице) один раз, в порядке и в сокращенной форме, как node в дереве. В этом двоичном дереве любой node и, следовательно, любая фракция может быть достигнута конечной последовательностью поворотов влево-вправо, начиная с самого верхнего уровня (для удобства позвольте называть его уровнем -1), содержащим 0/1 и 1/0. [Да, 1/0. Это не опечатка!]

Учитывая знаменатель, k, вам нужно взять не более k поворотов, чтобы достичь любой приведенной доли j/k, где j меньше k. Например, если знаменатель равен 101, все возможные дроби с знаменателем 101 или менее будут находиться на дереве где-то между Уровнем 1 (содержащий 1/1) и уровнем 101 (содержащий 1/101 в крайнем левом положении).

Предположим, что у нас есть генератор чисел, которые порождают 0 и 1. (Пожалуйста, не спрашивайте меня, как это сделать, я понятия не имею.) Леф произвольно решает, что Left = 0 и Right = 1.

Предположим, что у нас есть еще один генератор чисел, который может произвольно генерировать целые числа между 1 и n. Предположим далее, что первое сгенерированное число равно 0, т.е. повернуть налево: это гарантирует, что дробь будет падать в интервале (0,1).

Выберите максимальный знаменатель, k. Случайно сгенерируйте число, m, между 1 и k. Затем создайте случайный список R и L. Траверс (т.е. Спуск) дерево Штерн-Брокот, следуя списку поворотов. Остановитесь, когда вы достигнете фракции назначения.

Если эта дробь имеет знаменатель, равный или меньший, чем k, остановите свой номер.

Если знаменатель больше k, поднимитесь по дереву (по тому же пути, который вы спустили), пока не достигнете доли с знаменателем, не превышающим k.

Я не знаю, что генерация числа действительно случайна. Я даже не знаю, как это сказать. Но за то, что он worthe, я не обнаруживаю никакого очевидного источника предвзятости.