# 分治法（二）

--------------------------------------------------------------------------------------------------------------------------------------------------

4.5　　大整数乘法和Strassen矩阵乘法

1）大整数乘法

a  =  a1 * 10^(n1/2)   +  a0　　　　　　-----n1为a的位数

b  =  b1 * 10^(n2/2)  +  b0　　　　　　-----n2为b的位数

public static long Mutiply(String a,String b)//用字符串读入2个大整数    {        long result = 0;        if(a.length() == 1 || b.length() == 1)    //递归结束的条件            result =  Mul(a,b);        else            //如果2个字符串的长度都 >= 2        {            String a1 = a.substring(0, a.length() / 2 );        //截取前一半的字符串(较短的一半)            String a0 = a.substring(a1.length(), a.length());    //截取后一半的字符串            //System.out.println(a1);            //System.out.println(a0);            String b1 = b.substring(0, b.length() / 2);            String b0 = b.substring(b1.length(), b.length());                        //分治的思想将整数写成这样： a = a1 * 10^(n1/2) + a0, b = b1 * 10^(n2/2)，相乘展开得到以下四项            //其中n1，n2为2个整数a，b的位数            result = (long) (Mutiply(a1,b1) * Math.pow(10, a0.length() + b0.length())            + Mutiply(a1,b0) * Math.pow(10, a0.length()) + Mutiply(a0,b1) * Math.pow(10, b0.length())            + Mutiply(a0,b0));        }                return result;    }

BigIntegerMutiply
package Section4;import java.util.Arrays;/*第4章 分治法  大整数乘法--计算2个大整数的乘积*/public class BigIntegerMutiply {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        long a = 95211154;        long b = 9039;        String s1 = "95211154";        String s2 = "9039";                long suppose = a * b;        long result = Mutiply(s1,s2);                System.out.println(suppose + "  " + result);        System.out.println(suppose == result);            }        public static long Mutiply(String a,String b)//用字符串读入2个大整数    {        long result = 0;        if(a.length() == 1 || b.length() == 1)    //递归结束的条件            result =  Mul(a,b);        else            //如果2个字符串的长度都 >= 2        {            String a1 = a.substring(0, a.length() / 2 );        //截取前一半的字符串(较短的一半)            String a0 = a.substring(a1.length(), a.length());    //截取后一半的字符串            //System.out.println(a1);            //System.out.println(a0);            String b1 = b.substring(0, b.length() / 2);            String b0 = b.substring(b1.length(), b.length());                        //分治的思想将整数写成这样： a = a1 * 10^(n1/2) + a0, b = b1 * 10^(n2/2)，相乘展开得到以下四项            //其中n1，n2为2个整数a，b的位数            result = (long) (Mutiply(a1,b1) * Math.pow(10, a0.length() + b0.length())            + Mutiply(a1,b0) * Math.pow(10, a0.length()) + Mutiply(a0,b1) * Math.pow(10, b0.length())            + Mutiply(a0,b0));        }                return result;    }        private static long Mul(String s1,String s2){    //计算2个字符串表示的大整数的乘积        //实际上只有当一个字符串的长度为1时，这个函数才会被调用                int[] a = new int[s1.length()];    //存放大整数s1的各位        int[] b = new int[s2.length()];    //存放大整数s2的各位                for(int i = 0;i < a.length;i++)        //将字符'i'转化为整数i放入整数数组            a[i] = (int) s1.charAt(i) - 48;            for(int i = 0;i < b.length;i++)            b[i] = (int) s2.charAt(i) - 48;        long num1 = toNum(a);        long num2 = toNum(b);                return num1 * num2;        }        private static long toNum(int[] a){ //将一个整数的位数组转化为它对应的数        long result = 0;        for(int i = 0;i < a.length;i++)            result = result * 10 + a[i];        //System.out.println(result);        return result;    }        }

95211154和903923的乘积：

86063551957142  86063551957142
true

2）Strassen矩阵乘法

public static int[][] StrassenMul(int[][] a,int[][] b){    //a，b均是2的乘方的方阵        int[][] result = new int[a.length][a.length];        if(a.length == 2)        //如果a,b均是2阶的，递归结束条件            result = StrassMul(a,b);        else                    //否则（即a，b都是4,8,16...阶的）        {            //a的四个子矩阵            int[][] A00 = copyArrays(a,1);            int[][] A01 = copyArrays(a,2);            int[][] A10 = copyArrays(a,3);            int[][] A11 = copyArrays(a,4);            //b的四个子矩阵            int[][] B00 = copyArrays(b,1);            int[][] B01 = copyArrays(b,2);            int[][] B10 = copyArrays(b,3);            int[][] B11 = copyArrays(b,4);                        //递归调用            int[][] m1 = StrassenMul(addArrays(A00,A11),addArrays(B00,B11));            int[][] m2 = StrassenMul(addArrays(A10,A11),B00);            int[][] m3 = StrassenMul(A00,subArrays(B01,B11));            int[][] m4 = StrassenMul(A11,subArrays(B10,B00));            int[][] m5 = StrassenMul(addArrays(A00,A01),B11);            int[][] m6 = StrassenMul(subArrays(A10,A00),addArrays(B00,B01));            int[][] m7 = StrassenMul(subArrays(A01,A11),addArrays(B10,B11));                        //得到result的四个子矩阵            int[][] C00 = addArrays(m7,subArrays(addArrays(m1,m4),m5));//m1+m4-m5+m7            int[][] C01 = addArrays(m3,m5);    //m3+m5            int[][] C10 = addArrays(m2,m4);    //m2+m4            int[][] C11 = addArrays(m6,subArrays(addArrays(m1,m3),m2));//m1+m3-m2+m6                        //也可以按照下列方法来求C            //C00 = addArrays(StrassenMul(A00,B00),StrassenMul(A01,B10));            //C01 = addArrays(StrassenMul(A00,B01),StrassenMul(A01,B11));            //C10 = addArrays(StrassenMul(A10,B00),StrassenMul(A11,B10));            //C11 = addArrays(StrassenMul(A10,B01),StrassenMul(A11,B11));                        //将四个子矩阵合并成result            Merge(result,C00,1);            Merge(result,C01,2);            Merge(result,C10,3);            Merge(result,C11,4);        }        return result;    }

Strassen
package Section4;/*第4章 分治法  Strassen矩阵乘法*/public class Strassen {    //该程序可以对两个同阶的2^n阶的矩阵采用Strassen算法做矩阵乘法    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        int[][] a = {                    {1,0,2,1},                    {4,1,1,0},                    {0,1,3,0},                    {5,0,2,1}                    };                int[][] b = {                    {0,1,0,1},                    {2,1,0,4},                    {2,0,1,1},                    {1,3,5,0}                    };                    int[][] result = StrassenMul(a,b);        System.out.println("输出矩阵：");                for(int i = 0;i < result.length;i++)        {            for(int j = 0;j < result.length;j++)                System.out.print(result[i][j] + "  ");            System.out.println();        }            }            public static int[][] StrassenMul(int[][] a,int[][] b){    //a，b均是2的乘方的方阵        int[][] result = new int[a.length][a.length];        if(a.length == 2)        //如果a,b均是2阶的，递归结束条件            result = StrassMul(a,b);        else                    //否则（即a，b都是4,8,16...阶的）        {            //a的四个子矩阵            int[][] A00 = copyArrays(a,1);            int[][] A01 = copyArrays(a,2);            int[][] A10 = copyArrays(a,3);            int[][] A11 = copyArrays(a,4);            //b的四个子矩阵            int[][] B00 = copyArrays(b,1);            int[][] B01 = copyArrays(b,2);            int[][] B10 = copyArrays(b,3);            int[][] B11 = copyArrays(b,4);                        //递归调用            int[][] m1 = StrassenMul(addArrays(A00,A11),addArrays(B00,B11));            int[][] m2 = StrassenMul(addArrays(A10,A11),B00);            int[][] m3 = StrassenMul(A00,subArrays(B01,B11));            int[][] m4 = StrassenMul(A11,subArrays(B10,B00));            int[][] m5 = StrassenMul(addArrays(A00,A01),B11);            int[][] m6 = StrassenMul(subArrays(A10,A00),addArrays(B00,B01));            int[][] m7 = StrassenMul(subArrays(A01,A11),addArrays(B10,B11));                        //得到result的四个子矩阵            int[][] C00 = addArrays(m7,subArrays(addArrays(m1,m4),m5));//m1+m4-m5+m7            int[][] C01 = addArrays(m3,m5);    //m3+m5            int[][] C10 = addArrays(m2,m4);    //m2+m4            int[][] C11 = addArrays(m6,subArrays(addArrays(m1,m3),m2));//m1+m3-m2+m6                        //也可以按照下列方法来求C            //C00 = addArrays(StrassenMul(A00,B00),StrassenMul(A01,B10));            //C01 = addArrays(StrassenMul(A00,B01),StrassenMul(A01,B11));            //C10 = addArrays(StrassenMul(A10,B00),StrassenMul(A11,B10));            //C11 = addArrays(StrassenMul(A10,B01),StrassenMul(A11,B11));                        //将四个子矩阵合并成result            Merge(result,C00,1);            Merge(result,C01,2);            Merge(result,C10,3);            Merge(result,C11,4);        }        return result;    }            private static void Merge(int[][] result,int[][] C,int flag){        //将C复制到result的相应位置        switch(flag)        {            case 1:                for(int i = 0;i < result.length/2;i++)                    for(int j = 0;j < result.length/2;j++)                    result[i][j] = C[i][j];                break;            case 2:                for(int i = 0;i < result.length/2;i++)                    for(int j = result.length/2;j < result.length;j++)                        result[i][j] = C[i][j-result.length/2];                break;            case 3:                for(int i = result.length/2;i < result.length;i++)                    for(int j = 0;j < result.length/2;j++)                        result[i][j] = C[i - result.length/2][j];                break;            case 4:                for(int i = result.length/2;i < result.length;i++)                    for(int j = result.length/2;j < result.length;j++)                        result[i][j] = C[i - result.length/2][j-result.length/2];                break;        }    }            private static int[][] copyArrays(int[][] a,int flag){        //得到分割矩阵的子矩阵        int[][] result = new int[a.length/2][a.length/2];        switch(flag)        {            case 1:                for(int i = 0;i < a.length/2;i++)                    for(int j = 0;j < a.length/2;j++)                        result[i][j] = a[i][j];                break;            case 2:                for(int i = 0;i < a.length/2;i++)                    for(int j = a.length/2;j < a.length;j++)                        result[i][j-a.length/2] = a[i][j];                break;                case 3:                for(int i = a.length/2;i < a.length;i++)                    for(int j = 0;j < a.length/2;j++)                        result[i - a.length/2][j] = a[i][j];                break;                case 4:                for(int i = a.length/2;i < a.length;i++)                    for(int j = a.length/2;j < a.length;j++)                        result[i-a.length/2][j-a.length/2] = a[i][j];                break;        }                return result;    }            private static int[][] StrassMul(int[][] a,int[][] b){        //计算2个二阶的矩阵乘法        //Strassen方法使用了7次乘法，18次加法（传统方法是8次乘法4次加法）        int[][] result = new int[2][2];                int m1 = (a[0][0] + a[1][1]) * (b[0][0] + b[1][1]);        int m2 = (a[1][0] + a[1][1]) * b[0][0];        int m3 = a[0][0] * (b[0][1] - b[1][1]);        int m4 = a[1][1] * (b[1][0] - b[0][0]);        int m5 = (a[0][0] + a[0][1]) * b[1][1];        int m6 = (a[1][0] - a[0][0]) * (b[0][0] + b[0][1]);        int m7 = (a[0][1] - a[1][1]) * (b[1][0] + b[1][1]);                result[0][0] = m1 + m4 - m5 + m7;        result[0][1] = m3 + m5;        result[1][0] = m2 + m4;        result[1][1] = m1 + m3 - m2 + m6;                return result;    }            private static int[][] addArrays(int[][] a,int[][] b){        //求2个同阶矩阵的和        int[][] result = new int[a.length][a.length];        //System.out.println(result.length);        for(int i = 0;i < result.length;i++)            for(int j = 0;j < result.length;j++)            //for(int j = 0;i < result.length;j++)                    result[i][j] = a[i][j] + b[i][j];        return result;    }            private static int[][] subArrays(int[][] a,int[][] b){        //矩阵减法        int[][] result = new int[a.length][a.length];        for(int i = 0;i < result.length;i++)            for(int j = 0;j < result.length;j++)            //for(int j = 0;i < result.length;j++)                result[i][j] = a[i][j] - b[i][j];        return result;    }    }

5  4  7  3
4  5  1  9
8  1  3  7
5  8  7  7

--------------------------------------------------------------------------------------------------------------------------------------------------

4.6　　用分治法解最近点对问题和凸包问题

1）最近点对问题

a，预排序！

b，在预排序基础上，将点递归的分为左右一半。

public static Point[] getNearestPoints(Point[] Points) {        //从一个点数组里面找到最近的两个点，并返回这两个点        Point[] result = new Point[2];        if (Points.length == 3 || Points.length == 2) //递归结束的条件            result = getNear(Points);        else //多于3个点，分治，分别找出两个子集合的最近点对，然后合并结果        {            Point[] left = Arrays.copyOfRange(Points, 0, Points.length / 2);//最后一个下标不包括            Point[] right = Arrays.copyOfRange(Points, Points.length / 2,                    Points.length);                        //得到2个子集里面分别最短距离的2个点            Point[] result1 = getNearestPoints(left);            Point[] result2 = getNearestPoints(right);            double d1 = dPoints(result1[0], result1[1]);            double d2 = dPoints(result2[0], result2[1]);            //忘了将result赋值            if (d1 <= d2)                result = result1;            else                result = result2;                        //合并结果：找到全局距离最短的两个点            double dmin = Math.min(d1, d2);            int x1 = left.length - 1;//两个x的分界点            int x2 = x1 + 1;            //在Points.length/2是一个整数时是错误的            //int x1 = Points[Points.length/2 - 1].x;//两个x的分界点            //int x2 = Points[Points.length/2].x;            for (int i = x1; i >= 0; i--) {                //if(x2 - Points[i].x > dmin)        //直接导致调试很久都不知道错在哪！！！！！！                if (Points[x2].x - Points[i].x > dmin)                    break;                else                    //for(int j = Points.length/2;j < Points.length;j++)                    for (int j = x2; j < Points.length; j++) {                        //System.out.println(Points[j].y);                        //if(Points[j].x - x1 > dmin)                        if (Points[j].x - Points[x1].x > dmin)                            break;                        else {                            double temp = dPoints(Points[i], Points[j]);                            //System.out.println(temp);                            if (temp < dmin) {                                dmin = temp;                                result[0] = Points[i];                                result[1] = Points[j];                            }                        }                    }            }        }        return result;    }

NearestPoint
package Section4;import java.util.Arrays;/*第4章 分治法  寻找最近点对*/public class NearestPoint {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        Point[] Points = new Point[10];        Points[0] = new Point(3, 4);        Points[1] = new Point(2, 5);        Points[2] = new Point(3, 8);        Points[3] = new Point(13, 9);        Points[4] = new Point(7, 8);        Points[5] = new Point(7, 12);        Points[6] = new Point(90, 0);        Points[7] = new Point(5, 8);        Points[8] = new Point(7, 9);        Points[9] = new Point(3, 6);        //Points[10] = new Point(3,41);        Arrays.sort(Points);//按照x坐标升序对点预排序,n*log(n)的复杂度---Arrays提供的静态排序方法        Point[] result = new Point[2];        result = getNearestPoints(Points);        System.out.println("输出距离最近的两个点是： ");        //System.out.println(result[0].x);        for (int i = 0; i < result.length; i++)            System.out.print("(" + result[i].x + "," + result[i].y + ")   ");    }    public static Point[] getNearestPoints(Point[] Points) {        //从一个点数组里面找到最近的两个点，并返回这两个点        Point[] result = new Point[2];        if (Points.length == 3 || Points.length == 2) //递归结束的条件            result = getNear(Points);        else //多于3个点，分治，分别找出两个子集合的最近点对，然后合并结果        {            Point[] left = Arrays.copyOfRange(Points, 0, Points.length / 2);//最后一个下标不包括            Point[] right = Arrays.copyOfRange(Points, Points.length / 2,                    Points.length);                        //得到2个子集里面分别最短距离的2个点            Point[] result1 = getNearestPoints(left);            Point[] result2 = getNearestPoints(right);            double d1 = dPoints(result1[0], result1[1]);            double d2 = dPoints(result2[0], result2[1]);            //忘了将result赋值            if (d1 <= d2)                result = result1;            else                result = result2;                        //合并结果：找到全局距离最短的两个点            double dmin = Math.min(d1, d2);            int x1 = left.length - 1;//两个x的分界点            int x2 = x1 + 1;            //在Points.length/2是一个整数时是错误的            //int x1 = Points[Points.length/2 - 1].x;//两个x的分界点            //int x2 = Points[Points.length/2].x;            for (int i = x1; i >= 0; i--) {                //if(x2 - Points[i].x > dmin)        //直接导致调试很久都不知道错在哪！！！！！！                if (Points[x2].x - Points[i].x > dmin)                    break;                else                    //for(int j = Points.length/2;j < Points.length;j++)                    for (int j = x2; j < Points.length; j++) {                        //System.out.println(Points[j].y);                        //if(Points[j].x - x1 > dmin)                        if (Points[j].x - Points[x1].x > dmin)                            break;                        else {                            double temp = dPoints(Points[i], Points[j]);                            //System.out.println(temp);                            if (temp < dmin) {                                dmin = temp;                                result[0] = Points[i];                                result[1] = Points[j];                            }                        }                    }            }        }        return result;    }    private static Point[] getNear(Point[] Points) {        //返回仅有2个点或者三个点的点数组中距离最小的两个点        Point[] result = new Point[2];        if (Points.length == 2)            result = Points;        else //有3个点,枚举求距离最短的两个点        {            double d1 = dPoints(Points[0], Points[1]);            double d2 = dPoints(Points[0], Points[2]);            double d3 = dPoints(Points[1], Points[2]);            if (d1 <= d2 && d1 <= d3) {                result[0] = Points[0];                result[1] = Points[1];            } else if (d2 <= d3) {                result[0] = Points[0];                result[1] = Points[2];            } else {                result[0] = Points[1];                result[1] = Points[2];            }        }        return result;    }    private static double dPoints(Point a, Point b) {        //求两个点之间的距离        return Math.pow(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2), 0.5);    }}class Point implements Comparable { //二维的点    public int x;    public int y;    public Point(int x, int y) {        this.x = x;        this.y = y;    }    public int compareTo(Object o) {        // TODO Auto-generated method stub        Point obj = (Point) o;        if (this.x < obj.x)            return -1;        else if (this.x > obj.x)            return 1;        return 0;    }}

(7,8)   (7,9)

2）凸包问题

a，首先定义射线p1到p2的左侧：若p1 p2  p构成的顺序是逆时针，称p在射线的左侧

b，三角形p1  p2   p3的面积等于下列行列式的一半：

package Section4;/*第4章 分治法  凸包问题的分治解法*/import java.util.Arrays;import java.util.Iterator;import java.util.LinkedList;import java.util.Stack;public class ConvexHull {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        Point[] Points = new Point[15];                Points[0] = new Point(-5, 7);        Points[1] = new Point(3,-6);        Points[2] = new Point(5, 4);        Points[3] = new Point(-5, -5);        Points[4] = new Point(1, 7);        Points[5] = new Point(6, 0);                Points[6] = new Point(0, 0);        Points[7] = new Point(-5, 0);        Points[8] = new Point(3, -2);        Points[9] = new Point(3, 4);                Points[10] = new Point(1, 6);        Points[11] = new Point(5, 3);        Points[12] = new Point(-4, -5);        Points[13] = new Point(-3, 6);        Points[14] = new Point(2, 5);            Arrays.sort(Points);        //预排序处理                LinkedList<Point> list = new LinkedList<Point> ();        for(int i = 0;i < Points.length;i++)            list.add(Points[i]);                            //list存放全部的顶点                LinkedList<Point> result = getConvexHulls(list);    //result用来存放最终的结果顶点                System.out.println("一共有 " + result.size() + " 个顶点, " + "凸包的顶点是： ");        Iterator it = result.iterator();        while(it.hasNext())        {            Point next = (Point) it.next();            System.out.print("(" + next.x + "," + next.y + ")" + "  " );        }        }        public static LinkedList<Point> getConvexHulls(LinkedList<Point> list){        //将凸包顶点以result链表返回        LinkedList<Point> result = new LinkedList<Point>();                 Point temp1 = list.removeFirst();        Point temp2 = list.removeLast();        result.add(temp1);        result.add(temp2);                //递归的处理temp1 ---> temp2左右两侧的点        dealWithLeft(temp1,temp2,result,list);        dealWithLeft(temp2,temp1,result,list);//注意每次要将result带着，存放结果集                return result;    }        private static void dealWithLeft(Point p1,Point p2,LinkedList result,LinkedList list){        //递归的处理p1，p2构成的射线左边的点        Iterator it = list.iterator();                //找出左边最高的点Pmax        Point Pmax = null;        int max = 0;        while(it.hasNext())        {            Point next = (Point) it.next();            int x1 = p1.x,y1 = p1.y;            int x2 = p2.x,y2 = p2.y;            int x3 = next.x,y3 = next.y;                        //int max = 0;//小小的一个错误啊！！！！！！！            int compute = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;            if(compute > max)            {                max = compute;                Pmax = next;            }            }                //又找到了一个顶点        if(Pmax != null)        {            result.add(Pmax);            list.remove(Pmax);                        //递归            dealWithLeft(p1,Pmax,result,list);            dealWithLeft(Pmax,p2,result,list);            }    }        private static boolean onLeft(Point target,Point p1,Point p2){        //判断target是否在p1--->p2射线的左侧        int x1 = p1.x,y1 = p1.y;        int x2 = p2.x,y2 = p2.y;        int x3 = target.x,y3 = target.y;                int compute = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;        if(compute > 0)            return true;        else            return false;    }        }

(-5,7)  (6,0)  (1,7)  (5,4)  (-5,-5)  (3,-6)

--------------------------------------------------------------------------------------------------------------------------------------------------

posted @ 2011-06-12 01:10 jinmengzhe 阅读(...) 评论(...) 编辑 收藏