• 数学问题的解题窍门


    2018-11-12 20:11:11

    数学,特别是数论和计算机科学有着密切的联系,所以也常被选做题材。虽然数学问题大多需要使用特定方法求解,但其中有几个基础算法扮演着重要的角色。

    一、辗转相除法

    1、求最大公约数

    让我们来看一下如下的问题。

    问题描述:

    给定平面上的两个格点P1(x1, y1)和P2(x2,y2),线段P1P2上,除P1和P2以外一共有几个格点?

    限制条件:

    -10 ^ 9 <= x1, x2,y1, y2 <= 10 ^ 9

    问题求解:

    其实本题是两个距离的最大公约数 - 1,要注意特判距离为0时的答案是0。

    gcd(a, b) = gcd(b, a % b),直到第二项为0,直接输出第一项。

    算法时间复杂度为O(log max(a, b))以内。

    int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
    

    2、扩展欧几里得算法

    扩展欧几里得定理:对于与不完全为0的非负整数 a, b, gcd(a, b)表示a, b的最大公约数。那么存在整数x,y使得 gcd(a, b)=ax + by。

    那么如何高效的求解这个公式呢?

    我们不妨设 a>b。
    1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
    2,ab != 0 时
    设 ax 1 +by 1 =gcd(a,b);
    bx 2 +(a%b) y 2 =gcd(b,a%b);
    根据朴素欧几里德原理有 gcd(a,b)=gcd(b,a%b);
    则:ax 1 +by 1 =bx 2 +(a%b)y 2 ;
    即:ax 1 +by 1 =bx 2 +(a-(a/b)*b)y 2 =ay 2 +bx 2 -(a/b)*by 2 ;
    根据恒等定理得:x 1 =y 2 ; y 1 =x 2 -(a/b)*y 2 ;
    这样我们就得到了求解 x1,y1 的方法:x1 ,y1  的值基于 x2 ,y2.

    通过上述的变换,就可以将原本求x,y的问题,转化成求x2,y2的问题,并且两个系数在衰减,直到b = 0,答案是x = 1, y = 0。

    public class Extgcd {
        public int extgcd(int a, int b, int[] x, int[] y) {
            if (b == 0) {
                int res = a;
                x[0] = 1;
                y[0] = 0;
                return res;
            }
            else {
                int res = extgcd(b, a % b, x, y);
                int tmp = x[0];
                x[0] = y[0];
                y[0] = tmp - a / b * y[0];
                return res;
            }
        }
    
        public static void main(String[] args) {
            Extgcd e = new Extgcd();
            int[] x = new int[1];
            int[] y = new int[1];
            System.out.println(e.extgcd(4, 11, x, y));
            System.out.println(x[0]);
            System.out.println(y[0]);
        }
    }
    

    二、有关素数的基础算法

    素数广泛应用于密码学中,因而也有很多相关算法。不过程序设计竞赛涉及的主要是埃式筛法、简单的素性测试和整数分解这类算法。

    1、素性测试

    问题描述:

    给定正整数n,请判断n是不是正素数。

    限制条件:

    1 <= n <= 10 ^ 9

    问题求解:

    所谓素数,就是指恰好有2个约数的整数。因为n的约数都不超过n,因此只需要判断2 - n - 1即可。另外,如果d是n的约数,那么n / d自然也是其约数,所以只需要检测2 - sqrt(n)。

        public boolean isPrime(int num) {
            for (int i = 2; i * i <= num; i++) {
                if (num % i == 0) return false;
            }
            return num != 1;
        }
    

    2、埃式筛法

    如果只是对一个数进行素性检测,通常O(sqrt(n))的算法已经够用了。但是如果需要对许多整数进行素性检测,则有更为高效的算法。

    问题描述:

    给定整数n,请问n以内有多少个素数。

    限制条件:

    n <= 10 ^ 6

    问题求解:

        public int sieve(int n) {
            int res = 0;
            boolean[] isPrime = new boolean[n + 1];
            Arrays.fill(isPrime, true);
            isPrime[0] = isPrime[1] = false;
            for (int i = 2; i <= n; i++) {
                if (isPrime[i]) {
                    res++;
                    for (int j = 2 * i; j <= n; j += i) {
                        isPrime[j] = false;
                    }
                }
            }
            return res;
        }
    

    3、区间筛法

    问题描述:

    给定整数a 和 b,请问区间[a, b)内有多少素数?

    限制条件:

    a < b <=  10 ^ 12

    b - a <= 10 ^ 6

    问题求解:

    b以内的合数的最小质因数一定是不超过sqrt(b)的,因此如果有了sqrt(b)的素数表,就可以将[a, b)中的素数完全筛选出来。

    以下使用POJ #2689测试程序。

    需要特别注意一下边界点,如果a = 1,则直接让其自增即可,另外对于长度为1的单个数据点,直接输出即可。

    import java.util.Arrays;
    import java.util.Scanner;
    
    public class SegmentSieve {
        public String segmentSieve(long a, long b) {
            if (a + 1 == b) return "There are no adjacent primes.";
            boolean[] small = new boolean[(int)Math.sqrt(b)];
            boolean[] isPrime = new boolean[(int)(b - a)];
            Arrays.fill(small, true);
            Arrays.fill(isPrime, true);
            for (int i = 2; i < small.length; i++) {
                if (small[i]) {
                    for (int j = i * 2; j < small.length; j += i) small[j] = false;
                    for (long j = Math.max((long)2, (a % i == 0 ? a / i : (a / i) + 1)) * i; j < b; j += i) isPrime[(int)(j - a)] = false;
                }
            }
            int minLen = isPrime.length;
            int maxLen = 0;
            int a1 = -1;
            int b1 = -1;
            int a2 = -1;
            int b2 = -1;
            int prev = -1;
            for (int i = 0; i < isPrime.length; i++) {
                if (isPrime[i]) {
                    if (prev == -1) prev = i;
                    else {
                        int len = i - prev;
                        if (len < minLen) {
                            minLen = len;
                            a1 = prev;
                            b1 = i;
                        }
                        if (len > maxLen) {
                            maxLen = len;
                            a2 = prev;
                            b2 = i;
                        }
                    }
                    prev = i;
                }
            }
            if (minLen != isPrime.length) {
                return String.format("%d,%d are closest, %d,%d are most distant.", (long)(a1 + a), (long)(b1 + a), (long)(a2 + a), (long)(b2 + a));
            }
            else return "There are no adjacent primes.";
        }
    
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            while (sc.hasNext()) {
                long a = sc.nextLong();
                long b = sc.nextLong() + 1;
                if ((int) a == 1) a++;
                SegmentSieve ss = new SegmentSieve();
                System.out.println(ss.segmentSieve(a, b));
            }
        }
    }
    

    三、快速幂运算

    除了数学问题之外,也有很多地方用到了幂运算。可以使用反复平方法来进行快速幂运算,时间复杂度为O(logn)

    问题描述:

    问题求解:

    主要的问题就是在n = Integer.MIN_VALUE的时候,-n会溢出,所以最好把n转成long。

        public double myPow(double x, int n) {
            return pow(x, (long)n);
        }
        
        private double pow(double x, long n) {
            if (n == 0) return 1;
            if (n < 0) {
                n = -n;
                x = 1 / x;
            }
            return (n % 2 == 0) ? pow(x * x, n / 2) : x * pow(x * x, n / 2);
        }
    
  • 相关阅读:
    SpringBlade 端口占用 Web server failed to start. Port 80 was already in use.
    SpringBlade 找不到或无法加载主类 springboot.Application
    Java idea 常用快捷键
    Java Velocity
    个人 一些需求
    Java MyBatis-Plus 基本使用
    Java Spring Initializr 创建的项目 包是一层一层的,需要隐藏一下空包
    MapReduce之自定义OutputFormat
    数据链路层之PPP协议
    MapReduce之GroupingComparator分组(辅助排序、二次排序)
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/9948767.html
Copyright © 2020-2023  润新知