这里要求你找到最接近给定有理 x 的 n = N-1 阶的Farey number,其中 x 在 [0,1] 内,x 和 N 是输入参数。您可以像下面的 sn-p 一样对最接近 x 的数字进行蛮力搜索:
public static void BruteForce(double x, int N, out int numerator, out int denominator)
{
// Farey sequence of order n = N-1:
int n = N - 1;
// Find the closest Farey number [0/1,1/n,...(n-1)/n,1/1] to the 'x' given...
double min_diff = 1.0;
int num_closest = 0;
int denom_closest = 0;
for (int denom = 1; denom < n + 1; denom++)
{
for (int num = 1; num < denom; num++)
{
double r = (double)num / (double)denom;
double diff = System.Math.Abs(x - r);
if (diff < min_diff)
{
min_diff = diff;
num_closest = num;
denom_closest = denom;
}
}
}
numerator = num_closest;
denominator = denom_closest;
}
该算法虽然具有二次增长顺序,但不是那么有用。
更好的方法是利用法雷对的属性。正如the Farey mediant property theorem 所说,如果 p=a/b 和 q=c/d>p 是最低分数的分数,并且它们之间没有分母小于任何一个的分数,则分母最小的分数 ⊕ 是 (a+c)/ (b+d)。这个分数称为“中位数”,它将 Farey 序列分成两个区间 [0/1,...,⊕] 和 [⊕,...,1/1],其中没有分母低于中位数本身的数字.上述定理现在可以应用于包含有理参数 x 的区间。如果找到的中位数的分母高于 Farey 序列的 n 阶,则意味着搜索结束,搜索区间现在只包含两个 Farey 有理数,我们需要选择最接近 x 的一个。
算法如下(感谢Yakov Galka),做了一些细微的调整:
public static void MediantSearch(double x, int N, out int numerator, out int denominator)
{
// Farey sequence of order n = N-1:
int order = N - 1;
// Numerator and denominator bounds...
int n = order - 1;
int d = order;
// Define the search bounds...
int lo_num = 0; int lo_denom = 1;
int hi_num = 1; int hi_denom = 1;
while(true)
{
int mediant_num = lo_num + hi_num;
int mediant_denom = lo_denom + hi_denom;
if(mediant_denom > d)
{
// Break the search and return the closest...
if((x - (double)lo_num/(double)lo_denom) < ((double)hi_num/(double)hi_denom - x))
{
numerator = lo_num;
denominator = lo_denom;
}
else
{
numerator = hi_num;
denominator = hi_denom;
}
break;
}
if((double)mediant_num/(double)mediant_denom < x)
{
lo_num = mediant_num;
lo_denom = mediant_denom;
}
else if((double)mediant_num / (double)mediant_denom > x)
{
hi_num = mediant_num;
hi_denom = mediant_denom;
}
else
{
numerator = mediant_num;
denominator = mediant_denom;
break;
}
}
}
该算法具有对数增长顺序 O(log(n))。