算法大作业
- 题目1 实现大数相乘
- 题目要求:
编程语言内置的数据类型都是有长度限制的,最长的long类型的长度是2^63-1,如果两个数的乘积超过了这个长度,那么就会出现错误,那么如何实现两个长度为20位二进制数字的乘积。 - 算法思路
- 暴力相乘法:
这个方法的思路主要是使用两个循环模拟手动运算的过程,超出长度的数字用字符串表示,计算的过程中,需要注意在计算过程中的数字的位数调整。时间复杂度为O(n^2)。 - 分治法
分治法的主要思路是将问题分成可直接求解的子问题,针对这个问题可求解的子问题就是两个个位数相乘,可以直接得到结果。分治法在递归的过程中停止条件是两个数字都为个位数。另外,本题目还会用到两个数字乘积的一个性质:ABCD = AC10^n + (BC + A * D)* 10^(n/2) + B*D,这个性质对求解题目非常的重要,主要是在对子问题进行合并的时候,需要确定子问题得到的解的位数。分治法的时间复杂度比较复杂,但是会比暴力相乘法要好一点。
1、暴力相乘法
- 代码
import com.sun.xml.internal.fastinfoset.util.CharArray;
public class BigNumber_Brute_force {
public static void main(String[] args){
//随机产生两个20位的二进制数字
int n = 20;
long value1 = (long)(Math.random()*Math.pow(2,n-1)+Math.pow(2,n-1));
long value2 = (long)(Math.random()*Math.pow(2,n-1)+Math.pow(2,n-1));
System.out.println("value1:"+ Long.toBinaryString(value1));
System.out.println("value2:"+ Long.toBinaryString(value2));
String temp1 = Long.toBinaryString(value1);
String temp2 = Long.toBinaryString(value2);
//将二进制数字表示成字符数组的形式
char[] input1 = temp1.toCharArray();
char[] input2 = temp2.toCharArray();
System.out.println(Character.getNumericValue(input1[1]));
long sum = 0;
//暴力法求解
for(int i = 0; i< input1.length; ++i){
for(int j = 0; j< input2.length; ++j){
sum += (Character.getNumericValue(input1[j]) & Character.getNumericValue(input2[i]))* Math.pow(2,i+j);
}
}
System.out.println("result:"+ Long.toBinaryString(sum));
}
}
2、分治法
- 代码
public class BigNumber_Divide {
public static void main(String[] args){
//产生20位的二进制数
int n = 20;
long value1 = (long)(Math.random()*Math.pow(2,n-1)+Math.pow(2,n-1));
long value2 = (long)(Math.random()*Math.pow(2,n-1)+Math.pow(2,n-1));
System.out.println("value1:"+ Long.toBinaryString(value1));
System.out.println("value2:"+ Long.toBinaryString(value2));
String input1 = Long.toBinaryString(value1);
String input2 = Long.toBinaryString(value2);
long result = Divide(input1, input2);
System.out.println(Long.toBinaryString(result));
}
//分治法划分二进制数字
public static long Divide(String num1, String num2){
if(num1.length() == 1 || num2.length() == 1){
return Long.parseLong(num1) & Long.parseLong(num2);
}
int length1 = num1.length();
int lenght2 = num2.length();
//确定划分标准
int mid = Math.max(lenght2,length1)/2;
//AB * CD = AC * 2^n + (BC + AD) * 2^(n/2) + BD
//确定A,B,C,D
String a = num1.substring(0, mid);
String b = num1.substring(mid);
String c = num2.substring(0,mid);
String d = num2.substring(mid);
//求解子问题的解
long z2 = Divide(a, c);
long z0 = Divide(b,d);
long z1 = Divide(a,c) - z2 - z0;
//合并子问题
return (long)(z2*Math.pow(2, lenght2)+ z1*Math.pow(2, mid) + z0);
}
}
- 题目2 实现找到最近两个点的距离
- 题目描述:给定一堆二维坐标点,找到距离最近的两个点的坐标
- 算法思路:
-
Brute-Force方法:即暴力破解法,循环求出所有点之间的距离,统计出最近的两个点的距离即可,算法的时间复杂度为O(n^2)。
-
分治法:将问题划分成子问题进行求解,那么使用分治法就得确定几个点:
- 递归循环的终止条件
- 子问题的划分的标准
- 子问题怎么合并成整个问题
这几个点就是整个分治法的代码逻辑,那么我们来一个一个确定下来
- 子问题最小时可以是问题规模里面只有两个点的时候,我们就可以直接计算出来
- 为了保证子问题的规模大致相同,我们得让子问题的点的个数相同,那么我们可以对问题的中的点按照X坐标进行排序,取中间X坐标的点作为问题划分的关键点
- 子问题在合并成整个问题时,需要考虑一个问题就是,如果最短距离的两个点分别出现在两个子问题中的情况,针对这种情况的话,我们可以以关键点为中心,构建一个距离中心点的X长度为当前最小距离的区域,超出这个长度点我们可以直接忽略,因为如果两个点之间的X坐标的距离超过当前最小距离,那么这两个点的距离一定超过最小距离,同理Y轴坐标也是这样。所以我们在合并子问题的时候,需要再多做这样一步操作,来防止上述情况的发生,这也是本道题的难点之一。具体的实现看代码即可。
- 暴力法
- 代码实现
import javafx.scene.effect.Light;
import java.util.ArrayList;
public class MinDistance_Brute_force {
public static void main(String[] args){
//构建1000个坐标点,保证坐标点不重复
Point[] points = new Point[1000];
ArrayList<Integer> list_x = new ArrayList<>();
ArrayList<Integer> list_y = new ArrayList<>();
while (list_x.size()<=1000){
if(!list_x.contains((int)(Math.random()*100000))){
list_x.add((int)(Math.random()*100000));
}
}
while (list_y.size()<=1000){
if(!list_y.contains((int)(Math.random()*100000))){
list_y.add((int)(Math.random()*100000));
}
}
for (int i = 0; i< 1000; ++i){
points[i] = new Point(list_x.get(i), list_y.get(i));
}
//计算所有两个点之间的距离,记录最小值
double minDis = Integer.MAX_VALUE;
for(int i = 0; i< points.length; ++i){
for(int j = i+1; j< points.length; ++j){
if(Distance(points[i], points[j]) < minDis){
minDis = Distance(points[i], points[j]);
}
}
}
System.out.println(minDis);
}
public static double Distance(Point p1, Point p2){
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y- p2.y, 2));
}
}
//构建一个坐标点
class Point{
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
- 分治法求解
- 代码实现
//import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
import java.util.ArrayList;
public class MinDistance {
public static void main(String[] args) {
//随机产生1000个坐标不重复的点
Point[] points = new Point[1000];
ArrayList<Integer> list_x = new ArrayList<>();
ArrayList<Integer> list_y = new ArrayList<>();
while (list_x.size()<=1000){
if(!list_x.contains((int)(Math.random()*10000))){
list_x.add((int)(Math.random()*10000));
}
}
while (list_y.size()<=1000){
if(!list_y.contains((int)(Math.random()*10000))){
list_y.add((int)(Math.random()*10000));
}
}
for (int i = 0; i< 1000; ++i){
points[i] = new Point(list_x.get(i), list_y.get(i));
}
//System.out.println(points);
for(Point temp:points){
System.out.println("X:"+temp.x+" Y:"+ temp.y);
}
//对点进行以X坐标大小进行排序
Arrays.sort(points, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return (o1.x > o2.x)? 1: (o1.x == o2.x)?0:-1;
}
});
//分治法调用
System.out.println(divide(points,0,points.length-1));
}
public static double divide(Point[] points, int left, int right){
//终止条件1
if(left == right){
return Integer.MIN_VALUE;
}
//终止条件2
if(right - left == 1){
return Distance(points[left], points[right]);
}
//子问题的划分
int mid = (left + right)/2;
double left_min = divide(points, left, mid);
double right_min = divide(points, mid, right);
double min_dis = left_min<right_min?left_min:right_min;
//子问题合并过程中需要防止两个点分别在子问题中
ArrayList<Point> temp = new ArrayList<Point>();
for(int i = left ; i< right - left; ++i) {
if (points[mid].x - points[i].x < min_dis && Math.abs(points[mid].y - points[i].y) < min_dis) {
temp.add(points[i]);
}
}
for(int i = 0; i< temp.size(); ++i){
for(int j = i+1; j< temp.size();++j){
if(min_dis > Distance(temp.get(i), temp.get(j))){
min_dis = Distance(temp.get(i),temp.get(j));
}
}
}
return min_dis;
}
public static double Distance(Point p1, Point p2){
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y- p2.y, 2));
}
}
class Point {
public int x, y;
Point(int x, int y){
this.x = x;
this.y = y;
}
}