定义:
对任意给定的一个自然数n,将分母小于等于n的不可约的真分数按升序排列,并且在第一个分数之前加上0/1,在最后一个分数之后加上1/1,这个序列称为n级法雷数列,即法雷数列是0和1之间最简分数升序排列的数列,所以n级法雷数列的个数即为 满足 1 <= a < b <= n && gcd( a , b ) == 1 的二元组( a , b )的数量 + 2(0/1 和 1/1)
性质:
- 除了1级法雷数列外,所有的法雷数列都有奇数个元素,其中居于正中间的那个元素一定是1/2.
- n级法雷数列中,若相邻两个元素是 a / b 和 c / d ( a / b < c / d ),则这两个数的差为1 / bd, 这个差的最小值为1/(n*(n-1)), 最大值为1/n,,在法雷数列的第一个元素(0/1)与其后继以及最后一个元素(1/1)与前驱之间的差取到最大值,而正中间的那个元素1/2 与其前驱和后继元素之间的差取次大值1/(n*2).
- 任意两个相邻元素 a / b 和 c / d ( a / b < c / d ),满足 b * c - a * d == 1
- 任意三个相邻元素,中间元素 == (前驱元素分子 + 后继元素分子)/(前驱元素分母 + 后继元素分母)
- 设F(n)为第n级法雷数列的个数,phi(n)为欧拉函数的值,则F( n ) = F( n-1 ) + phi( n ) ( 考虑法雷序列定义 ,正确性显然)
- 当n趋于正无穷时,n级法雷数列包含的元素的个数趋于 3 * n * n / ( pi * pi ) ( pi = acos(-1) )
构造:
根据性质4即可简单构造,Stern-Brocot树是一种数据结构,能构造法雷数列。Stern-Brocot树是从0 (= 0⁄1)和1 (= 1⁄1)开始,取中间分数来构成法里数列,即根据性质4构造。
Stern-Brocot树生成规则:
上图是一棵Stern-Brocot树,其生成规则如下:
从第1行到第n行,每行相邻两数a/b和c/d,产生中间数(a+c)/(b+d),置于下一行中。将一行的分数(包括0/1,1/0),进行约分简化,则每一行(包括0/1,1/0,1/1),不会出现两个相同的分数。若分子或者分母大于n,则去掉该分数,将剩下的分数,从小到大排序,得到数列F。
根据这个思路,代码也比较好写
#include <bits/stdc++.h> using namespace std; int n; void dfs(int a1, int b1, int a2, int b2) //a1/b1 , a2/b2 { if (b1 + b2 > n) return; dfs(a1, b1, a1 + a2, b1 + b2);//先左边,左边比较小 printf("%d/%d ",a1+a2,b1+b2); dfs(a1 + a2, b1 + b2, a2, b2);//右边 } int main() { scanf("%d",&n); //序列级数 printf("0/1 "); dfs(0, 1, 1, 1); //会从小到大输出结果 printf("1/1 "); return 0; }
正确性:
如何保证这种方法生成的都是最简分数?
利用性质3和数学归纳法易证
如何保证不重不漏?
不重:因为有序,所以,不重,为什么有序?根据该算法思想显然,不显然的话看代码就显然了
不漏:每个分数都是从其一个上界和下届逼近,类似二分,显然不漏
例题:
POJ3090 Visible Lattice Points
HDU6624 fraction (这一题算是法雷数列的一个扩展,就是求两个分数之间分母最小的分数为多少)