应用欧几里得算法计算下述最大公因数。
(a)
(b)
编写程序计算两个整数 a 与 b 的最大公因数 gcd(a, b), 即使 a 与 b 中的一个等于零,程序也应该运行,但要确保 a 与 b 都是 0 时不会出现死循环。
见代码部分
设 是将欧几里得算法应用于 a 与 b 得到的相继余数,证明每两步会缩小余数至少一半。换句话说,验证:
由此证明欧几里得算法在至多步终止,其中是以2为底的对数。特别地,证明步数至多是b的位数的7倍。(提示:的值是多少?)
证:,又
如果 m 与 n 都整除L,则数 L 被称为 m 与 n 的公倍数。最小的这种 L 被称为 m 与 n 的最小公倍数,记为 .例如,.
(a) 求下述最小公倍数。
(b) 对于在(a)中计算的每个, 比较与的值,试找出他们之间的关系。
(c) 证明你说发现的关系对所有 m 与 n成立。
(d) 用(b)中的结果计算.
(e) 假设 且 , 求m 与 n。存在一种以上的可能性吗?如果存在,求出所有的 m 与 n。
(a)
(b)
(c) 证: 令 ,则有,否则存在更大的数 整除 m 与 n。
显然 是能被 p 和 q整除的最小公倍数。又有
(d)
(e)
“”算法如下:从给定的正整数 n 出发。如果 n 是偶数则将他除以二;如果是奇数则把它替换成.如此反复进行下去。例如,如果由5开始,则得到数列
如果由7开始,则得到
注意到 1 后数列正好连续重复 .一般地,下述两种可能性之一将会出现。
(i) 可能最后会重复出现先前的某数a,这种情况下两个 a 之间的数段会无穷次重复,此时称算法在最后一个不重复值处终止,数列中不同项的个数叫做算法长度。例如,对 5 与 7,算法在 1 处终止。5的算法长度是 6, 7的算法长度是 17.
(ii) 可能从不重复相同的数,此时说算法没有终止。
(a)
(b) 猜想:对于的 始终在 1 处终止
(c)
(d)
(d)
编写程序来运行上题叙述的算法。用户输入 n,程序就会给出 算法的长度L(n)与终止值$$T(n). 用你的程序来制作一个表格,给出所有开始值 的算法长度与终止值。
见代码部分。
public int gcd(int x, int y){
while(y != 0){
int t = x % y;
x = y;
y = t;
}
return x;
}
public int LCM(int x, int y){
if(x == 0 || y == 0) return 0;
return x * y / gcd(x, y);
}public class HailSerial {
int[] len;//算法长度
int[] end;//终止值
int n;//上限
int ring;//临时变量,环值
int ring_pre;//环尾值
public void hail(int n){
this.n = n;
len = new int[n + 1];
end = new int[n + 1];
for (int i = 1; i <= n; i++) {
ring = -1;
//貌似只能使用哈希表,假设我们不知道终点是1,且终点可以是任何数字
dfs(-1, i, new HashMap<>(), 1);
}
}
private Pair<Integer, Integer> dfs(int last, int cur, Map<Integer, Integer> mp, int dep) {
if(cur <= n && len[cur] > 0) return new Pair<>(len[cur], end[cur]);
if(mp.containsKey(cur)){//发现环入口
ring = cur;//设置环值并开始归的操作
ring_pre = last;//设置环外的终止值
return new Pair<>(dep - mp.get(cur), -1);
}
mp.put(cur, dep);
Pair<Integer, Integer> s;
if(cur % 2 == 0){
s = dfs(cur, cur / 2, mp, dep + 1);
}else {
s = dfs(cur,cur * 3 + 1, mp, dep + 1);
}
Pair<Integer, Integer> rs;
if(ring != -1){//还在环内
if(cur == ring){//找到环入口,开始出环
rs = new Pair<>(s.getKey(), ring_pre);
ring = -1;
}else {//还在环内,终止值是上一个,算法长度为环长
rs = new Pair<>(s.getKey(), last);
}
}else {
rs = new Pair<>(s.getKey() + 1, s.getValue());
}
if(cur <= n){
len[cur] = rs.getKey();
end[cur] = rs.getValue();
}
return rs;
}
@Test
public void test(){
hail(100);
for (int i = 1; i <= 100; i++) {
System.out.println(i + ":" + len[i] + ":" + end[i]);
}
}
} (a) 求方程
的一组整数解。
(b)求方程
的一组整数解。
(a) (b)
求下述方程的所有整数解:
(a)
(b)
(c)
本章叙述的求解包含相当多的代数运算与回代。本题描述计算 与 的另一种方法,它容易在计算机上应用。
(a) 证明图6.1中的算法能计算 与 的最大公因数 以及方程 的一组特解 .
(b) 运用你使用的计算机语言,在计算机上运行上述算法。
(c) 用你的程序对下述有序对计算以及 的整数解.
(d) 时你的程序出问题吗?修改程序使得程序能正确处理这种情况。
(e) 为了后面的应用,求出是有用的。修改你的程序,使得程序总能得到的解。(提示:如果是解,则也是解。)
(a) ,且有递推公式,当时返回。使用矩阵法观察, 可以发现 与 具有相同的迭代矩阵。
(b) 见代码部分
(c) (i) (ii) (iii)
(d) 见代码部分
(e) 见代码部分
(a) 求方程
的整数解.
(b) 满足什么条件时方程
有整数解?当有解时给出求解的一般方法.
(c) 使用 (b) 中的方法求方程
的一组整数解.
(a)
(b) 答: 满足 时有整数解
- (i) 求解
- (ii) 求解 且时方程无整数解,.
- (iii) 联和(1) (2)求得一组特解,
- (iv) 求得所有整数解
(c) ,
假设 . 证明对每个整数 ,方程 有整数解 与 . (提示:先求 的解再乘以 .)求方程的一组解,尽量让比较小.
有时我们仅对方程的非负整数解感兴趣.
(a) 解释为什么方程没有整数解 与 .
(b) 制作形如的数值表,给出不可能出现的数值的猜想,然后证明你的猜想是正确的。
(c) 对 的下述值,求不能表示成 形式的最大整数.
(i) (ii) (iii)
(d) 设.使用 (c) 中的结果猜想出不能表示成形式的最大整数(用 与 表示)。对的至少两个值检验你的猜测。
(e) 证明你在 (d) 中猜出的公式是正确的.
(f) 试将上述问题推广到三项和 .例如,不能表示成 的最大整数是什么。
(a) ???懵了,这不是在特定情况下有解么
(b)
猜想时, 不能表示的最大数为
(c) (i) (ii) (iii)
(d) 如(b)中所猜想.
(e) 证:时, 不能表示的最大数为
假设存在 使得 , 则有
又,与矛盾,令
等式两边同时乘以 可得
若
即对所有都可以表示成 的形式
(f) 但时, 设不能表示的最大数为M,则有
有时候我们会思考,如果身边只有纸和笔,如何快速有效的算出, 现提供一种有效的方法, 行列式法。以为例。(虽然这种方法是我临时想出来的,但我觉得它早已有之)
那如何用行列式求通解系数呢,观察上式行列式计算结果,取第一行和第三行(行列式右边为0的行)。
public int[] _gcd(int a, int b) {
if(a == 0) return new int[]{0, 1, b};
if(b == 0) return new int[]{1, 0, a};
int x = 1, g = a, v = 0, w = b;
while (w > 0) {
int q = g / w;
int t = g % w;
int s = x - q * v;
x = v;
g = w;
v = s;
w = t;
}
x %= v;
if(x < 0) x += Math.abs(v);
int y = (g - a * x) / b;
//特别地,当g = 1时,称 x 是 a 模 b 下的乘法逆元
return new int[]{x, y, g};
}