程序中的数学
一,大数
下面为我的高精度整数模板
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> using namespace std; #define MAXN 500 struct BigNum { int num[MAXN]; int len; }; //高精度比较 a > b return 1, a == b return 0; a < b return -1; int Comp(BigNum &a, BigNum &b) { int i; if (a.len != b.len) return (a.len > b.len) ? 1 : -1; for (i = a.len - 1; i >= 0; i--) if (a.num[i] != b.num[i]) return (a.num[i] > b.num[i]) ? 1 : -1; return 0; } //高精度加法 BigNum Add(BigNum &a, BigNum &b) { BigNum c; int i, len; len = (a.len > b.len) ? a.len : b.len; memset(c.num, 0, sizeof(c.num)); for (i = 0; i < len; i++) { c.num[i] += (a.num[i] + b.num[i]); if (c.num[i] >= 10) { c.num[i + 1]++; c.num[i] -= 10; } } if (c.num[len]) len++; c.len = len; return c; } //高精度减法,保证a >= b BigNum Sub(BigNum &a, BigNum &b) { BigNum c; int i, len; len = (a.len > b.len) ? a.len : b.len; memset(c.num, 0, sizeof(c.num)); for (i = 0; i < len; i++) { c.num[i] += (a.num[i] - b.num[i]); if (c.num[i] < 0) { c.num[i] += 10; c.num[i + 1]--; } } while (c.num[len - 1] == 0 && len > 1) len--; c.len = len; return c; } BigNum Mul(BigNum &a, BigNum &b) { int i, j, len = 0; BigNum c; memset(c.num, 0, sizeof(c.num)); for (i = 0; i < a.len; i++) { for (j = 0; j < b.len; j++) { c.num[i + j] += (a.num[i] * b.num[j]); if (c.num[i + j] >= 10) { c.num[i + j + 1] += c.num[i + j] / 10; c.num[i + j] %= 10; } } } len = a.len + b.len - 1; while (c.num[len - 1] == 0 && len > 1) len--; if (c.num[len]) len++; c.len = len; return c; } BigNum Pow(BigNum &a, int n) { BigNum ans; ans.num[0] = 1, ans.len = 1; while (n) { if (n & 1) ans = Mul(ans, a); a = Mul(a, a); n >>= 1; } return ans; } //高精度除以低精度,除的结果为c, 余数为f void Div1(BigNum &a, int &b, BigNum &c, int &f) { int i, len = a.len; memset(c.num, 0, sizeof(c.num)); f = 0; for (i = a.len - 1; i >= 0; i--) { f = f * 10 + a.num[i]; c.num[i] = f / b; f %= b; } while (len > 1 && c.num[len - 1] == 0) len--; c.len = len; } //高精度*10 void Mul10(BigNum &a) { int i, len = a.len; for (i = len; i >= 1; i--) a.num[i] = a.num[i - 1]; a.num[i] = 0; len++; while (a.num[len - 1] == 0 && len > 1) len--; a.len = len; } //高精度除以高精度,除的结果为c,余数为f void Div2(BigNum &a, BigNum &b, BigNum &c, BigNum &f) { int i, len = a.len; memset(c.num, 0, sizeof(c.num)); memset(f.num, 0, sizeof(f.num)); f.len = 1; for (i = len - 1; i >= 0; i--) { Mul10(f); f.num[0] = a.num[i]; while (Comp(f, b) >= 0) { f = Sub(f, b); c.num[i]++; } } while (len > 1 && c.num[len - 1] == 0) len--; c.len = len; } void print(BigNum &a) { //输出大数 int i; for (i = a.len - 1; i >= 0; i--) printf("%d", a.num[i]); puts(""); } void Init(BigNum &a, char *s, int &tag) { //字符串转化为大数 memset(a.num, 0, sizeof(a.num)); int i = 0, j = strlen(s); if (s[0] == '-') { j--; i++; tag *= -1; } a.len = j; for (; s[i] != '\0'; i++, j--) a.num[j - 1] = s[i] - '0'; } int main() { freopen("data.txt", "r", stdin); BigNum a, b, c, f; char s1[100], s2[100]; while (~scanf("%s%s", s1, s2)) { int tag = 1; Init(a, s1, tag); Init(b, s2, tag); Div2(a, b, c, f); if (tag < 0) putchar('-'); print(c); print(f); // c = Pow(a, 2); // print(c); } return 0; } /* 99999999999999999999999999999980000000000000000000000000000001 9999999999999999999999999999999 */
JAVA中BigInteger大整数类 和 BigDecimal大浮点数类
基本函数:
1.valueOf(parament); 将参数转换为制定的类型
比如 int a=3;
BigInteger b=BigInteger.valueOf(a);
则b=3;
String s=”12345”;
BigInteger c=BigInteger.valueOf(s);
则c=12345;
2.add(); 大整数相加
3.subtract(); 相减
4.multiply(); 相乘
5.divide(); 相除取整
6.remainder();取余
7.pow(); a.pow(b)=a^b
8.gcd(); 最大公约数
9.abs(); 绝对值
10.negate();取反数
11.mod(); a.mod(b)=a%b=a.remainder(b);
12.max(); min();
13.punlic int comareTo();
14.boolean equals(); 是否相等
15.BigInteger构造函数:BigInteger(String val);
基本常量:BigInteger.ONE BigInteger.TEN BigInteger.ZERO
读入:用Scanner类定义对象进行控制台读入,Scanner类在java.util.*包中
Scanner cin=new Scanner(System.in);// 读入
while(cin.hasNext()) { //等同于!=EOF
int n;
BigInteger m;
n=cin.nextInt(); //读入一个int;
m=cin.BigInteger();//读入一个BigInteger;
System.out.println(m.toString());
}
JAVA四则运算示例代码
import java.util.Scanner; import java.math.*; import java.math.BigInteger; public class Main { public static void main(String args[]) { Scanner cin = new Scanner(System.in); BigInteger a, b; char op; String s; while (cin.hasNext()) { a = cin.nextBigInteger(); s = cin.next(); op = s.charAt(0); if (op == '+') { b = cin.nextBigInteger(); System.out.println(a.add(b)); } else if (op == '-') { b = cin.nextBigInteger(); System.out.println(a.subtract(b)); } else if (op == '*') { b = cin.nextBigInteger(); System.out.println(a.multiply(b)); } else { BigDecimal a1, b1, eps; String s1, s2; s1 = a.toString(); a1 = new BigDecimal(s1); b = cin.nextBigInteger(); s2 = b.toString(); b1 = new BigDecimal(s2); eps = a1.divide(b1); System.out.println(eps); } } cin.close(); } }
大数相乘有快速傅里叶求法,仍在学习FFT
二,数论
1.素数表
int pri[MAXN], pris; bool notprime[MAXN]; void init() { //Euler筛法每个合数只会被它最小的质因数筛去,因此时间复杂度为O(N) memset(notprime, 1, sizeof(notprime)); int i, j; for (i = 2, pris = 0; i < MAXN; i++) { if (notprime[i]) pri[pris++] = i; for (j = 0; j < pris && i * pri[j] < MAXN; j++) { notprime[i * pri[j]] = 0; if (i % pri[j] == 0) break; } } }
2.欧拉函数 1到n-1跟n互质的数的个数
//1~maxn欧拉函数表 eu[1] =1; for (i = 2; i < maxn; i += 2) if(!eu[i]) { for (j = i; j < maxn; j += i){ if(!eu[j]) eu[j] = j; eu[j] = eu[j] / i * (i - 1); } //容斥原理求单个数欧拉函数 unsigned euler(int x){ int i, res=x; for (i = 2; i < (int)sqrt(x * 1.0) + 1; i++) if(x%i==0) { res = res / i * (i - 1); while (x % i == 0) x /= i; //保证i一定是素数 } if (x > 1) res = res / x * (x - 1); return res; } //素数欧拉性质 phi(pi^ai) = pi^ai - pi^(ai-1)
3.扩展欧几里德求ax+by=gcd(a,b)
#define LL long long LL exGcd(LL a,LL b,LL &x,LL &y) { //ax+by=gcd(a,b) if(b==0) { x=1, y=0; return a; } LL temp=exGcd(b,a%b,x,y); LL t=x; x=y; y=t-a/b*y; return temp; }
4.乘法逆元
/* * 当要求(a/b)%p的值,且a很大,由于除法没有同余公式,要用乘法逆元改成可同余的相乘 * 逆元k满足 b*k = 1 (mod p) * 即 b*k=p*x+1,把k带入(a*k)%p就得到(a/b)%p * 扩展的欧几里德算法求乘法逆元模板 * (a/b) mod p = ( a*[b与p(互质)乘法逆元]k ) mod p */ /************************************/ #define LL long long LL exGcd(LL a,LL b,LL &x,LL &y) { //ax+by=gcd(a,b) if(b==0) { x=1, y=0; return a; } LL temp=exGcd(b,a%b,x,y); LL t=x; x=y; y=t-a/b*y; return temp; } LL cal(LL a,LL b) { // "b*k=p*x+1"这里a = "b", y = "k" LL x,y; exGcd(a,b,x,y); x=(x%b+b)%b; //重要 return x; } /* * 模板2,欧拉函数f[p],欧拉定理说明当gcd(a,p)=1,a^f[p]=1 (mod p) * 费马小定理的推广 p是素数 a^(p-1)=1 (mod p) * b*k%p = 1 = b^(p-1)%p ,则逆元 k = b^(p-2) */ LL pow(LL x, LL y) { LL ret = 1; while (y) { if (y&1) ret=(ret*x)%mod; x=(x*x)%mod; y>>=1; } return ret; } LL inv( LL a ,LL b) { return pow(a, b - 2 )%b; }
5.素因子分解
//prime_factor()传入n, 返回不同质因数的个数 //f存放质因数,nf存放对应质因数的个数 //dfs(0,n)求出[1, n]内有多少个数和n不互质 LL f[MAXN], nf[MAXN]; int prime_factor(LL n) { int cnt = 0; for(int i = 0; n > 1 && pri[i] *pri[i]<= n; i++) if (n % pri[i] == 0) { for (nf[cnt] = 0; n % pri[i] == 0; nf[cnt]++, n /= pri[i]); f[cnt++] = pri[i]; } if (n > 1) nf[cnt++] = 1, f[cnt - 1] = n; return cnt; } void dfs(int index, LL n) { if (index == cnt) { //枚举完所有素因子 sta.insert(n); //sta 保存所有因子 return; } dfs(index + 1, n); for (int i = 0; i < nf[index]; i++) { n /= (LL) f[index]; dfs(index + 1, n); } } LL dfs(int start, LL n) { //容斥原理搜索和n不互质的个数 LL s = 0; for(int i = start;i < cnt;i++) s += n/f[i] - dfs(i+1, n/f[i]); return s; }
6.求和公式,k = 1..n
1. sum( k ) = n(n+1)/2
2. sum( 2k-1 ) = n^2
3. sum( k^2 ) = n(n+1)(2n+1)/6
4. sum( (2k-1)^2 ) = n(4n^2-1)/3
5. sum( k^3 ) = (n(n+1)/2)^2
6. sum( (2k-1)^3 ) = n^2(2n^2-1)
7. sum( k^4 ) = n(n+1)(2n+1)(3n^2+3n-1)/30
8. sum( k^5 ) = n^2(n+1)^2(2n^2+2n-1)/12
9. sum( k(k+1) ) = n(n+1)(n+2)/3
10. sum( k(k+1)(k+2) ) = n(n+1)(n+2)(n+3)/4
12. sum( k(k+1)(k+2)(k+3) ) = n(n+1)(n+2)(n+3)(n+4)/5
三,组合数学
错排公式递推式 f[1]=0,f[2]=1,f[i] = (i-1)*(f[i-1]+f[i-2])
如若一个排列式的所有的元素都不在原来的位置上,则称这个排列为错排
全部抽错概率即全部排错的情况个数除以的全排列个数
1. C(m,n)=C(m,m-n)
2. C(m,n)=C(m-1,n)+C(m-1,n-1)
3. m个物品放到到不同的n个容器中,插板法结果为c(n+m-1,n-1)
#define MOD 1000000007 long long c[505][505]; void Init() { for(int i=0;i<=500;i++) { c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD; } }
容斥原理
分母是1001的最简分数一共有多少个?
分析:这一题实际上就是找分子中不能与1001进行约分的数。由于1001=7×11×13,所以就是找不能被7,11,13整除的数。
解答:1~1001中,有7的倍数1001/7 = 143;有11的倍数1001/11 = 91,有13的倍数1001/13 = 77 ;有7*11=77的倍数1001/77 = 13 ,有7*13=91的倍数1001/91 = 11,有11*13=143的倍数1001/43 = 7.有1001的倍数1个。
由容斥原理知:在1~1001中,能被7或11或13整除的数有(143+91+77)-(13+11+7)+1=281,从而不能被7、11或13整除的数有1001-281=720(个).也就是说,分母为1001的最简分数有720个。
康托展开
int first[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; unsigned int factorial[21] = { 1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,1932053504,1278945280,2004310016,2004189184,4006445056,3396534272,109641728,2192834560 }; unsigned int GetPermutationNum(int permutation[], int len) { bool used[11] = { 0 }; int perInt[11]; for (int i = 0; i < len; ++i) for (int j = 0; j < len; ++j) { if (permutation[i] == first[j]) { perInt[i] = j; break; } } unsigned int num = 0; for (int i = 0; i < len; ++i) { unsigned int n = 0; for (int j = 0; j < perInt[i]; ++j) { if (!used[j]) ++n; } num += n * factorial[len - i - 1]; used[perInt[i]] = 1; } return num; }
Gray码
这种遍历顺序作为一种编码方式存在,叫做Gray码,发明它主要是为了减少读写磁盘在两个相邻扇区之间的误差问题。
n阶的Gray码相当于在构造的n维立方体上的Hamilton回路,因为沿着立方体上的边走一步,n维坐标中只会有一个值改变。
Gray码和Hanoi塔问题等价。Gray码改变的是第几个数,Hanoi塔就该移动哪个盘子。比如,3阶的Gray码每次改变的元素所在位置依次为1-2-1-3-1-2-1,这正好是3阶Hanoi塔每次移动盘子编号。如果我们可以快速求出Gray码的第n个数是多少,我们就可以输出任意步数后Hanoi塔的移动步骤。Gray码的第n个数(从0算起)是n xor (n >> 1)。下面我们把二进制数和Gray码都写在下面,可以看到左边的数异或自身右移的结果就等于右边的数。
二进制数 Gray码
000 000
001 001
010 011
011 010
100 110
101 111
110 101
111 100