求最大公约数——算法设计与分析基础随笔(一)

求两个数m和n的最大公约数,方法有很多种,最容易想到的就是遍历到min(m, n)为止,找出最大的公约数,再进行剪枝,遍历到min(m, n)的平方根为止。方法有很多,上述两种只是最简单的,也是最没有技术含量的,接下来记录的是一些较为巧妙的方法。

 算法出自古希腊数学家欧几里得所著的《几何原本》,这本书里面讲解了一个简单的公约数的算法。用现代数学的术语来表示,欧几里得算法采用的方法就是递归下列等式,直到m mod n等于0:

 gcd(m, n) = gcd(n, m mod n)  (m mod n 表示m除以n之后的余数) 

一般传统的计算gcd(m, n)的算法如下:

第一步:将min(m, n)的值赋给t;

第二步:m除以t,如果余数为0,则进入第三步;否则进入第四步;

第三步:n除以t,如果余数为0,返回t的值作为结果;否则进入第四步;

第四步:t = t - 1;返回第二步。

注意这个算法有一个输入的要求,不可以输入0,因为输入0的话t会等于0,也就会报错。

实现的代码如下:

    private int gcd(int m, int n) {
        int result = 1;
        int t = Math.min(m, n);

        if (t == 0) {//筛选出输入有问题的输入
            return result;
        }

        while (t != 1) {
            if (m % t == 0) {
                if (n % t == 0) {//体现第三步
                    result = t;
                    break;
                }
            }
            t = t - 1;
        }

        return result;
    }

当然,这时候肯定会想到,我们初中学习的通过求每个数字的质因数分解的方法来解决这个问题,但是很遗憾的是,我们在找质因数的这个过程中其实是使用到了既有知识的——我们知道哪个是质因数,但是机器不知道,所以这条路很难实现。

既然以前学过的质因数分解很难实现,那就学习一下一个同样能做到找到质因数的算法——“埃拉托色尼筛法”。该算法一开始初始化一个2~n的连续整数序列,作为候选指数。然后在算法的第一个循环中,将类似4和6这样的2的倍数从序列中消去。第二个循环针对3的倍数(对于6来说,其实在第一个循环中已经消去了),依次下去,直到没有可以消去的元素,剩下的整数就是我们要求的质数。

算法的实现代码如下:

    private List sieve(int n) {
        List result = new ArrayList<>();
        int[] arr = new int[n + 1];
        double floor = Math.floor(Math.sqrt(n + 1));

        for (int index = 0; index < n + 1; index++) {
            arr[index] = index;
        }

        for (int index = 2; index <= floor; index++) {
            if (arr[index] != 0) {
                int j = arr[index] * arr[index];
                while (j <= n) {//对于平方大于n的可以避免多次消去相同的数字
                    arr[j] = 0;
                    j = j + index;
                }
            }
        }

        for (int index = 2; index < floor; index++) {
            if (arr[index] != 0) {
                result.add(arr[index]);
            }
        }

        return  result;
    }

接下来就是对两列质数找出相同的元素,最后将相同的元素的乘积,则可得到我们的目标。具体实现如下:

import java.util.ArrayList;
import java.util.List;

public class Sieve {
    private List sieve(int n) {
        List result = new ArrayList<>();
        int[] arr = new int[n + 1];
        double floor = Math.floor(Math.sqrt(n + 1));

        for (int index = 0; index < n + 1; index++) {
            arr[index] = index;
        }

        for (int index = 2; index < floor + 1; index++) {
            if (arr[index] != 0) {
                int j = arr[index] * arr[index];
                while (j <= n) {//对于平方大于n的可以避免多次消去相同的数字
                    arr[j] = 0;
                    j = j + index;
                }
            }
        }

        for (int index = 2; index < n + 1; index++) {
            if (arr[index] != 0) {
                result.add(arr[index]);
            }
        }

        return  result;
    }

    private List getGcdArr(int target, List arr) {
        List result = new ArrayList<>();

        int index = 0;
        while (target > 1) {
            if (target % arr.get(index) == 0) {
                result.add(arr.get(index));
                target = target / arr.get(index);
            } else {
                index++;
            }
        }

        return result;
    }

    private int findGcd(List integers1, List integers2) {
        int result = 1;

        for (Integer element : integers1) {
            if (integers2.contains(element)) {
                result = result * element;
                integers2.remove(element);
            }
        }

        return result;
    }
}

 

你可能感兴趣的:(Java,算法)