<>直线
<>1.题目
在平面直角坐标系中,两点可以确定一条直线。
如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。
给定平面上2 × 3 个整点{(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z},
即横坐标是0 到1 (包含0 和1) 之间的整数、纵坐标是0 到2 (包含0 和2) 之间的整数的点。
这些点一共确定了11 条不同的直线。
给定平面上20 × 21 个整点{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},
即横坐标是0 到19 (包含0 和19) 之间的整数、纵坐标是0 到20 (包含0 和20) 之间的整数的点。
请问这些点一共确定了多少条不同的直线。(填空题)
答:本题遍历根据所有的点,计算出每两个点之间的斜率与截距,只要斜率与截距不同,就可以出确定直线不一样,但是还要考虑斜率不存在的情况,也就是直线垂直于x坐标轴的情况。
<>2.代码
public int requireDirectLine(int x,int y) { Test5 test5 = new Test5(); int arr[
][] =new int[x][y]; HashSet<String> set = new HashSet<>(); for(int i = 0; i <
arr.length; i++) { for(int j = 0; j < arr[0].length; j++) {
//取双重for循环为一个点,即(i,j),另外的双重for循环为另外一个点,即(p,q) for(int p = 0; p < arr.length; p++
) { for(int q = 0; q < arr[0].length; q++) { if( i == p && j ==q ) { //这一步判断可以不要
continue; } if( i != p) { //x1 == x2时直线斜率不存在,即当直线垂直于x轴时,直线斜率不存在。 String line =
""; int u = q - j; //y2-y1 int d = p - i; //x2-x1 int g = gcd(u,d); //u、d的最大公约数
line= line + u/g+ "/"+ d/g + "+"; //(y2-y1)/(x2-x1) k = u/g+ "/"+ d/g int b = j
* d - u *i; // y = kx +b -> b = y - kx 将点(i,j),k=(y2-y1)/x2-x1代入。 // 可得b=
(y0(x2-x1)- x0(y2-y1))/x2-x1 //即求除数与被除数的最大公约数就可以求出该直线的最简式,最简式一样即两条直线是同一条直线。 int
g2= gcd(b,d); //求 y0(x2-x1) - x0(y2-y1))/x2-x1 的最大公约数 line = line + b/g2 + "/" +
d/g2; // 直线 为 kx + b ,k、b不一致,将直线加入set。 set.add(line); } } } } } if( y == 1) {
//当y为1时,即只有一行,这时无需加上垂直的直线,即垂直与x坐标轴的值。 return set.size(); } return set.size() + x
; //加上斜率不存在的直线。即横坐标的值。 } //辗转相除法求最大公约数-->递归方式 public int gcd(int x,int y){ if( y
== 0){ return x; } return gcd(y,x%y); }
答案:40257
本题直接使用double计算k,b会出现除不尽的情况,因此会存在多于的直线,会使结果不准确。如下
错误解法示例:
public static void main(String[] args) { double x = requireDirectLine(20,21);
System.out.print(x); } public static double requireDirectLine(int x,int y) { int
arr[][] =new int[x][y]; HashSet<ArrayList<Double>> set = new HashSet<>(); for(
double i = 0; i < arr.length; i++) { for(double j = 0; j < arr[0].length; j++) {
for(double p = 0; p < arr.length; p++ ) { for( double q = 0; q < arr[0].length;
q++) { if( i == p && j ==q ) { continue; } if( i != p) { double k = (q-j)/(p-i);
if(k == -0.0) { k= 0.0; } double b = (-k)*i + j ; System.out.println("p1:("+i+
","+j+"),p2:("+p+","+q+")"+" "+"k:"+k+","+"b:"+b); ArrayList<Double> list = new
ArrayList<>(); list.add(k); list.add(b); set.add(new ArrayList<Double>(list)); }
} } } } Iterator<ArrayList<Double>> it = set.iterator(); if( y == 1) { return
set.size(); } return set.size() + x; }
可以看到结果比正确答案多出了很多。
上述错误为错误代码实例,只要数量足够大,就会出现除不尽的情况,因此难免会出现重复问题。因此,使用将直线化为最简式的方式。
<>3.求解最大公因数
<>3.1.辗转相除法
* 如果b等于0,计算结束,a就是最大公约数。
* 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
* 回到第一步。 //迭代 public static int gcd2(int x,int y ){ while( y != 0 ){ int temp
= y; y = x % y; x = temp ; } return x; } //递归 public static int gcd3(int x,int y
){ if(y == 0 ){ return x; } return gcd3(y,x%y); }
<>3.2.定义法
根据定义从1开始遍历至两个元素的最小值,选取两者的公约数,即12能除的尽,18也能除尽的最大数,即为最大公约数。
public int gcd1(int x,int y){ int max = 1; for(int i = 2; i <= x && i<=y; i++ )
{ if(x % i==0 && y % i == 0){ max = i; } } return max; }