模版:数学知识(1)

质数:在大于1的整数中,如果只包含1和本身这两个约数,则称该数为质数或者素数

(1)判断质数(试除法)
(2)分解质因素(试除法)
(3)求1~n中所有的质数
(4)阶乘分解

1、判断质数(试除法)O( \sqrt n)

(1)从定义出发判断是否质数

时间复杂度O(n)
    static boolean is_primes(int x)
    {
        if(x < 2) return false;
        for(int i = 2;i <= x;i ++)
        {
            if(x % i == 0) return false;
        }
        return true;
    }

(2)从整除角度出发判断是否质数
定理:n中最多只包含一个大于\sqrt n的质数

时间复杂度O( \sqrt n)

若d能整除n,则d/n 也能整除n,例如 2能整除12 ,则6也能整除12
从小到大枚举到 min(d,n/d),不妨设d <= n/d,则d^2 <= n

    static boolean is_primes(int x)
    {
        if(x < 2) return false;
        for(int i = 2;i <= x / i;i ++)
        {
            if(x % i == 0) return false;
        }
        return true;
    }

2、分解质因素(试除法)O( \sqrt n)

public static void divide(int x)
    {
        for(int i = 2;i <= x / i;i++)
        {
            if(x % i == 0)
            {
                int s = 0;
                while(x % i == 0)
                {
                    s ++;
                    x /= i;
                }
                System.out.println(i + " " + s);
            }
        }
        if(x > 1) System.out.println(x + " " + 1);
        System.out.println();
    }

3、求1~n中所有的质数

(1)朴素筛法O(nlogn)
原理:任意整数x的倍数2x,3x,...都不是质数
由于每次筛选的个数的累加是
(n/2 + n/3 + n/4 +... + n/n) = n(1 + 1/2 + 1/3 +...+ 1/n) = nlnn

image.png

static int N = 1000000;
    static int[] primes = new int[N];
    static boolean[] st = new boolean[N];
    static int cnt = 0;//记录质数的个数
    public static void get_primes(int n)
    {
        for(int i = 2;i <= n;i++)
        {
            if(!st[i])
            {
                primes[cnt ++] = i;
            }
            for(int j = i * 2;j <= n;j += i)
                st[j] = true;
        }
    }

(2)埃氏筛法O(nloglogn)

只需把质数的倍数筛掉即可

从1到n中一共有n/(lnn)个质数
每次筛选的个数的累加是
(n/2 + n/3 + n/5 + n/7 + n/11 +...+ 1/n) = O(nloglogn)

    static int N = 1000000;
    static int[] primes = new int[N];
    static boolean[] st = new boolean[N];
    static int cnt = 0;//记录质数的个数
    public static void get_primes(int n)
    {
        for(int i = 2;i <= n;i++)
        {
            if(!st[i])
            {
                primes[cnt ++] = i;
                for(int j = i * 2;j <= n;j += i)
                    st[j] = true;
            }
        }
    }

(2)线性筛法O(n)
原理:每个合数i * p只会被它的最小质因子p筛一次

1、i % primes[j] == 0
primes[j]一定是i的最小质因子,primes[j]一定是primes[j] * i的最小质因数
2、i % primes[j] != 0
primes[j] 一定小于i的所有质因子,primes[j]一定是primes[j] * i的最小质因数
注意:在 2 操作中,若满足则必须停下,否则j ++ 时,后面的i * primes[j] 的最小质因子不是primes[j]

注意:筛[0,n]区间内的质数和筛[0,n)区间内的质数的区别在于这行代码for (int j = 0; i * primes.get(j) < n; j++) ,若i * primes.get(j) <= n,则就筛[0,n]的所有质数,也可以写成primes.get(j) <= n / i;i * primes.get(j) < n,则筛[0,n]的所有质数

    static int N = 1000010;
    static boolean[] st = new boolean[N];//st[x]存储x是否被筛掉
    static int cnt = 0;
    static int[] primes = new int[N];//primes[]存储所有素数
    public static void get_primes(int x)
    {
        for(int i = 2;i <= x ;i++)
        {
            if(!st[i]) primes[cnt ++] = i;
            for(int j = 0;primes[j] <= x / i;j++)
            {
                st[primes[j] * i] = true;
                if(i % primes[j] == 0) break;//primes[j]一定是i的最小质因子
            }
        }
    }

4、阶乘分解

算法分析

  • 1、筛出1~10^6的所有质数
  • 2、枚举每个质因子Pn!表示1 * 2 * 3... * n,从1n中,求P的次数:\lfloor \frac{n}{p} \rfloor + \lfloor \frac{n}{p^2} \rfloor + \lfloor \frac{n}{p^3} \rfloor ...(一共有log_{p}n项)
    • P的倍数有\lfloor \frac{n}{p} \rfloor
    • P^2的倍数有\lfloor \frac{n}{p^2} \rfloor
    • P^3的倍数有\lfloor \frac{n}{p^3} \rfloor
    • ...

时间复杂度 O(n)

\frac{n}{lnn}个质数,每个质数求log_{p}n次,因此时间复杂度是O(n)级别

Java 代码

import java.util.Scanner;

public class Main {
    static int N = 1000010; 
    static boolean[] st = new boolean[N];
    static int[] primes = new int[N];
    static int cnt = 0;
    static void init(int n)
    {
        for(int i = 2;i <= n;i ++)
        {
            if(!st[i]) primes[cnt ++] = i;
            for(int j = 0;primes[j] * i <= n;j ++)
            {
                st[primes[j] * i] = true;
                if(i % primes[j] == 0) break;
            }
        }
    }
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        init(n);
        for(int i = 0;i < cnt;i ++)
        {
            int p = primes[i],s = 0;
            for(int j = n;j != 0;j /= p)
            {
                s += j / p;
            }
            System.out.println(p + " " + s);
        }
    }
}

快速幂

&是二进制的“与”运算
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 0
3 & 2 = 0111 & 0010 = 0010 = 2

x & 1 的结果就是取二进制的最末位
x & 1 == 0 , 则 x 为偶数
x & 1 == 1 , 则 x 为奇数

>> 运算比较单纯,二进制去掉最后一位

image.png

image.png

public static long qmi(int a,int k,int p)
    {
        long res = 1 % p;
        long t = a;
        while(k > 0)
        {
            if((k & 1) == 1) res = res * t % p;
            k >>= 1;
            t = t * t % p;
        }
        return res;
    }

快速幂求逆元

引理:

image.png

image.png

证明分析:
费马小定理的推导来自杨辉三角形,在杨辉三角形中,可以发现每一行都是(x + 1) ^ n的展开式的系数,当n为任意质数p时,因为在展开过程中没有被约分过,因此展开式中的系数除了第一项和最后一项,其余全是p的倍数,
因此当p为质数时,(n + 1)^p - n^p - 1一定是质数p的倍数
由于下列方程一定成立
(n ^ p - n) + [(n + 1)^p - n^p - 1] = (n + 1)^p - (n + 1)
由于第二大项一定是质数p的倍数,对于这个式子的某个命题,第n项成立,则第n + 1项也成立,因此
n = 1时,n^p - n = 1 - 1 = 0,0可以被任意质数整除,
由数学归纳法,费马小定理证毕。

image.png

image.png

给定n组ai,pi,其中pi是质数,求ai模pi的乘法逆元,若逆元不存在则输出impossible。

import java.util.Scanner;

public class Main {
    public static long qmi(int a,int k,int p)
    {
        long res = 1 % p;
        long t = a;
        while(k > 0)
        {
            if((k & 1) == 1) res = res * t % p;
            k >>= 1;
            t = t * t % p;
        }
        return res;
    }
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        while(n -- > 0)
        {
            int a = scan.nextInt();
            int p = scan.nextInt();
            long t = qmi(a,p - 2,p);
            if(a % p == 0) System.out.println("impossible");//若a是p的倍数,则一定无解
            else System.out.println(t);
        }
    }
}

龟速乘法

算法分析

快速幂龟速乘法的对比
快速幂解决a ^ b mod p的问题,而龟速乘法解决a * b mod p的问题

本质上
快速幂:a ^ b mod p = a * a * a ... * a % p
龟速乘:a * b mod p = a + a + a ... + a % p

快速幂:从a 算出 a^2, 再算出 a^4a^8...(通过 k 的二进制中哪些位需要加上该位的值,算出 a^k)
龟速乘:从a 算出 2 * a, 再算出4 * a8 * a...(通过 k 的二进制中哪些位需要加上该位的值,算出a * k)

注意:Java 的同学还可以直接使用BigInteger算很大的值

时间复杂度 O(logb)

参考文献

算法提高课

Java 代码(龟速乘)

import java.util.Scanner;

public class Main {
    static long qadd(long a,long k,long p)
    {
        long res = 0;
        while(k > 0)
        {
            if((k & 1) == 1) res = (res + a) % p;
            k >>= 1;
            a = (a + a) % p;
        }
        return res;
    }
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        long a = scan.nextLong();
        long b = scan.nextLong();
        long p = scan.nextLong();
        System.out.println(qadd(a,b,p));
    }
}

Java 代码(BigInteger)

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        BigInteger a = new BigInteger(scan.next());
        BigInteger b = new BigInteger(scan.next());
        BigInteger p = new BigInteger(scan.next());
        System.out.println(a.multiply(b).mod(p));
    }
}

扩展欧几里得

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    static int x;
    static int y;
    public static int exgcd(int a,int b,int[] x,int[] y)
    {
        if(b == 0)
        {
            x[0] = 1;
            y[0] = 0;
            return a;
        }
        int d = exgcd(b,a % b,y,x);
        y[0] = y[0] - a/b * x[0];
        return d;
    }
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine().trim());
        int[] x = new int[1];
        int[] y = new int[1];
        while(n -- > 0)
        {
            String[] s1 = reader.readLine().split(" ");
            int a = Integer.parseInt(s1[0]);
            int b = Integer.parseInt(s1[1]);
            exgcd(a,b,x,y);
            System.out.println(x[0] + " " + y[0]);
        }
    }

}

image.png

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容