问题描述 [1] :
给定输入N,输出0到1之间分母小于或等于N的真分数,并递增输出。
比如说,N = 5时输出:

0/1 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 1/1

解决方案 [2] :
这个问题有个很漂亮的解决方法,称为Stern-Brocot树,取这个名字是因为这是由德国数学家Moritz Stern和法国的修理钟表商Achille Brocot独立发现的。
先来讨论一般的情况,即生成所有的非负真分数。算法的思想是由(0/1, 1/0)开始,然后不断地重复以下的操作:在相邻的两个分数m/n和m'/n'插入(m+m')/(n+n')。

伪代码:

<span style="font-size:14px;">main:print(0/1);printOrderedFraction(0, 1, 1, 1);print(1/1);
printOrderedFraction(m, n, m', n'):if (n+n' <= N)printOrderedFraction(m, n, m+m', n+n');print(m+m'/n+n')printOrderedFraction(m+n', n+n', m', n');end if</span>

例题HDU4556

上图是一棵Stern-Brocot树,其生成规则如下:
  从第1行到第n行,每行相邻两数a/b和c/d,产生中间数(a+c)/(b+d),置于下一行中。将一行的分数(包括0/1,1/0),进行约分简化,则每一行(包括0/1,1/0,1/1),不会出现两个相同的分数。若分子或者分母大于n,则去掉该分数,将剩下的分数,从小到大排序,得到数列F。
  现在请您编程计算第n行的数列F的个数。
Input
输入包含多组测试用例,每组输入数据是一个正整数n(n<=1000000)。
Output
对于每组的测试数据n,请输出第n行的数列F的个数。
Sample Input
1 2 4 6
Sample Output
3 5 13 25
#include<iostream>
#include<cstdio>
#include<cstring>using namespace std;const int N=1000010;long long euler[N];void get_euler(){int i,j;for(int i=1;i<N;i++)euler[i]=i;for(i=2;i<N;i++)if(euler[i]==i)for(j=i;j<N;j+=i)euler[j]-=euler[j]/i;
}void Init(){get_euler();for(int i=2;i<N;i++)euler[i]+=euler[i-1];
}int main(){//freopen("input.txt","r",stdin);Init();int n;while(~scanf("%d",&n)){cout<<euler[n]*2+1<<endl;}return 0;
}

算法的正确性证明 [2] :
1. 为什么按照这种方法生成的数都是真分数 ?
如果能够证明对于任何阶段都有m'n - mn' = 1,那么就得到m和n、m'和n'是互素的,从而m/n和m'/n'是真分数,这样就证明了生成的数都是真分数。
用归纳法证明。
1) 将(0/1, 1/0)代入,得1*1 - 0*0 = 1。命题成立。
2) 假设前面阶段产生的(m/n, m'/n')满足表达式,那么当插入一个新的中间项(m+m')/(n+n')时,有
(m+m')n - m(n+n') = m'n - mn' = 1
m'(n+n') - (m+m')n' = m'n - mn' = 1
综上命题成立。

2. 为什么能产生所有的真分数,是否存在某个真分数a/b被忽略掉 ?
因为对于真分数a/b,初始时它满足
m/n = 0/1 < (a/b) < 1/0 = m'/n'
假设当前阶段有m/n < (a/b) < m'/n',那么算法添加中间项(m+m')/(n+n')就存在三种情况:
1) 如果a/b = (m+m')/(n+n'),那么命题成立。
2) 如果(m+m')/(n+n') < a/b,那么就令m <- (m+m'), n <- (n+n')
3) 否则, 令m' <- (m+m'), n' <- (n+n')
这样一直迭代下去。
由于a/b - m/n > 0 并且 m'/n' - a/b > 0
=> an - bm >= 1 并且 bm' - an' >= 1
因此有 (m'+n')(an-bm) + (m+n)(bm'-an') >= m'+n'+m+n
化简得到 a(m'n-mn')+b(m'n-mn')=a+b >= m'+n'+m+n
因为每次迭代m, n, m'和n'其中一个递增,因此最多需要a+b步就可以产生a/b。

3. 为什么每个真分数只出现一次,而不会出现两次或者更多 ?
这个个问题可以根据第4个问题的答案来回答。

4. 为什么生成的数列是有序的 ?
同样可以用归纳法证明。如果m/n < m'/n'并且所有的数都是非负的,那么有
m/n < (m+m')/(n+n') < m'/n'
故根据算法构造的数列是递增的。

Reference:
[1] USACO. Problem 64: Ordered Fractions. http://ace.delos.com/usacoprob2?a=aOabvHGOS88&S=frac1
[2] Grapham, Kunth and Patashnik. Concrete Mathematics(2th edition). Chapter 4 Number Theory: 4.5 Relative Primality

Stern-Brocot树 (生成0-1之间的所有真分数)相关推荐

  1. 5.练习:猜数字:随机生成[0,100]之间的随机数,让用户猜生成的数字,显示猜大了还是猜小了,如果猜对了,提示共猜了多少次???

    5.练习:猜数字:随机生成[0,100]之间的随机数,让用户猜生成的数字,显示猜大了还是猜小了,如果猜对了,提示共猜了多少次??? 学习:第7遍 1.随机生成[0,100]之间的随机数 让用户猜生成的 ...

  2. java中生成1000~10000之间的随机数

    要生成在[min,max]之间的随机整数,可使用Random类进行相关运算: Random random = new Random(); int s = random.nextInt(max)%(ma ...

  3. VB 生成0~1的随机小数(不包含1),再用VB,感慨万千

    今天迫于无奈,接手一个已经离职快两年的前同事的代码(VB6.0),增加一个生成0~1之间随机数的功能. VB中的写法:Rnd() 为了美观,还要格式化: xxx = Format(Rnd(), &qu ...

  4. java将0到9随机输出_生成0到9之间的随机整数

    回答(20) 2 years ago 对于您给出的示例(从0开始直到9的整数),最干净的解决方案如下: from random import randrange randrange(10) 2 yea ...

  5. 随机密码生成python_每日一课 | Python 中生成 0 到 9 之间的随机整数

    很少有Python示例向您展示如何生成0(含)和9(含)之间的随机整数0 1 2 3 4 5 6 7 8 9 1.randrange 1.1生成0到9之间的随机整数 #!/usr/bin/python ...

  6. python随机生成数字_Python 中生成 0 到 9 之间的随机整数

    很少有Python示例向您展示如何生成0(含)和9(含)之间的随机整数0 1 2 3 4 5 6 7 8 9 1.randrange 1.1生成0到9之间的随机整数 #!/usr/bin/python ...

  7. Java黑皮书课后题第7章:*7.7(统计个位数的数目)编写一个程序,生成0和9之间的100个随机整数,然后显示每一个数出现的次数

    *7.7(统计个位数的数目)编写一个程序,生成0和9之间的100个随机整数,然后显示每一个数出现的次数 题目 题目描述 破题 代码 运行示例 题目 题目描述 *7.7(统计个位数的数目)编写一个程序, ...

  8. 生成0到9之间的随机整数

    如何在Python中生成0到9(含)之间的随机整数? 例如, 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 #1楼 import random print(random. ...

  9. 【python初学者日记】读入正整数n,生成并输出一个含有n个0~100之间的随机列表,求其平均值(保留2位小数)

    读入正整数n,生成并输出一个含有n个0~100之间的随机列表,求其平均值(保留2位小数) 问题分析 代码实现 问题分析 本题涉及了几个基础概念:循环.随机数.列表.代码比较简单,可以加深对这3个概念的 ...

  10. java的Random类生成随机的double范围【0,1)Math.random()生成0到100之间的数【0-100)【1-100】

    使用Random生成随机的double,范围[0,1) double b = new Random().nextDouble(); 测试用例 public class TestRandom {publ ...

最新文章

  1. 李世石宣布退役,高呼AI不可战胜:将举行史上首次让子人机大战
  2. 兼容低版本迅雷的js调用
  3. nvm 解决nodejs无法全局/usr/bin/node问题
  4. plor 回归的r方_简单线性回归模型
  5. 树莓派linux驱动学习之LED控制
  6. Spark1.0 安装
  7. BM惊爆:EOS一周年工作KPI
  8. oracle虚拟机 centos6.5,虚拟机oracle virtualbox 上安装centos6.5 网络设置
  9. php自定义表单怎么导入excel,织梦dedeCMS将自定义表单数据导入到excel文档实现方法...
  10. leetcode 767. Reorganize String | 767. 重构字符串(贪心+分桶+26路归并)
  11. USACO2.2【统计,dp,模拟,位运算】
  12. mysql递归查询所有上下节点_非递归打印二叉树的所有路径,保存父节点和孩子节点到底有啥差别...
  13. Java 接口做参数,接口回调
  14. HTTPS协议的简述
  15. 动易模板制作示例(一)
  16. vue中使用友盟统计,统计到每个路由
  17. 联通鸿蒙卡怎么样,联通不限流量卡,联通无限流量,正规资费
  18. SM2算法+开发中注意事项
  19. Hadoop之POC测试总结
  20. 第三方支付机构有哪些?他们的资金是如何运作保障用户的资金安全的?

热门文章

  1. 酒店无线产品认证靠谱吗
  2. CPS攻击案例(一)——基于脉冲宽度调制PWM的无人机攻击
  3. NOIP(CSP)初赛知识总结
  4. 常见硬件面试题(含答案)盘点,硬件工程师学习笔记
  5. 对话《财富》40U40|闪马智能创始人兼CEO彭垚:人口、变局与下一代智慧系统
  6. mpaas的h5容器之离线包的一些总结
  7. 非谓语动词---不定式作名词
  8. 高通不行了? 骁龙835排出前三, 国产处理器崛起, A11当老大
  9. 2022年武汉市高新技术企业各区县申报条件补贴、高企迁移奖励补助20万
  10. 总不能因为杯子碎了就不再喝水了吧