程序中的数学

一,大数

下面为我的高精度整数模板

#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

 

 

posted @ 2013-11-03 22:49  匡时@下一站.info  阅读(301)  评论(0编辑  收藏  举报