简单好玩的算法

求最大公约数的辗转相除法

    public static long gcd(long a,long b){
        long max=a>b?a:b;
        long min=a>b?b:a;
        if(max%min==0) return min;
        return gcd(min,max%min);
    }

利用的定理是:
设f(a,b)为a和b的最大公约数,则f(a,b)=f(b,a%b)。
证明:
对于a=b*q+r,有a-b*q=r。假设a和b的最大公约数为c,则a、b一定能被c整除,则m*a±n*b也一定能被c整除,故r也能被c整除。

求最大公约数的更相减损法

    public static long gcd1(long a,long b){
        if(a==b) return a;
        long max=a>b?a:b;
        long min=a>b?b:a;
        return gcd1(min,max-min);
    }

相比辗转相除法,这种方法不需要相比减法较为耗时的取模运算,但是这种方法不稳定,两个数相差很大或两个数相差很小时(其实相差很小会转变为相差很大的情况),这种方法就退化到O(n)了。
更相减损法原理不太懂……

用楼层测试鸡蛋的硬度

给两个蛋,有一百层楼,测试蛋在第几层楼扔下去会碎,如果没碎,蛋可以继续扔。求用最优策略,在最坏情况下要扔的次数。
可能题目描述不清楚,看不懂的百度一下吧

    public static int getTimeInWorstCase(int n){
        //对于第k层,蛋碎了,就从1到k-1层逐层往扔,这时候是k次;如果蛋没碎,就对n-k层继续之前操作。对每个k,取两者较大值;对所有k,取最小值。
        //map[n]= Math.min(Math.max(k,map[n-k]+1)),k=1...n-1,蛋在n层一定会碎,这是题目设定,所以扔到n-1就够了。蛋可能在1层都会碎,所以从1开始扔。
        //map[1]=1
        int[] map=new int[n+1];
        map[1]=1;
        for(int i=2;i<=n;i++){
            int curMin=Integer.MAX_VALUE;
            for(int j=1;j<i;j++){
                int temp=Math.max(j,map[i-j]+1);
                if(temp<curMin)
                    curMin=temp;
            }
            map[i]=curMin;
        }
        return map[n];
    }

还可以拓展到很多个蛋的情况。
对于两个蛋,还有一种不用动态规划的方法,但不懂为什么能这么做。
分为若干段,如果在第一个点a碎了,就从1开始到a-1,一共a次,如果第一个点没碎,就在第二个点b扔,b碎了,就从a+1到b-1扔;这是要保证a=b-a-1+2,即这一段长度为b-a=a-1后面的若干段以此类推。
所以对于n层,n<=a+(a-1)+(a-2)+…1,当n=100时,a>=14,得结果14

快速幂

    public static int myPow(int a, int b){
        int res=1,base=a;
        while(b!=0){
            if((b&1)!=0){

                res*=base;
            }
            base*=base;
            b=b>>1;
        }
        return res;
    }

原理:
2^11=(2^(2^0)) * (2^(2^1)) * (2^(2^3))
当二进制位为1时才相乘,为0乘以base。

判断一个数是否为素数

只有1和其本身为其约数的数称为素数。
易知,对于一个合数(非素数),必有一个小于或等于其平方根的约数(除1以外)。证明:对于合数a,必然存在a=b*c (b!=1,c!=1),若b、c同时大于开根号的a,则b * c>a。所以b、c不可能同时大于开根号的a。
(质因数分解也可以用到)

boolean isPrime(int n){
    int sqrtn=(int)Math.sqrt(n);
    for(int i=2;i<=sqrtn;++i){
        if(n%i==0) return false;
    }
    return 1!=n;//这里不直接return true,考虑到n为1的情况,代码简洁了
}
点赞