数学问题的解题窍门
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); }