美文网首页
模版:数学知识(1)

模版:数学知识(1)

作者: 得力小泡泡 | 来源:发表于2023-03-18 12:58 被阅读0次

质数:在大于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

相关文章

  • day06-模版使用

    1)创建模版文件夹2)配置模版目录 3)使用模版文件 给模版文件传递数据模版变量的使用{{ 模版变量名 }}ind...

  • Python_Flask 基础2

    1模版语法 1.1 模版语法主要分为两种: 变量和标签 模版中的变量 : {{ var }} 模版中的标签:{% ...

  • Grafana+Prometheus监控详细Linux服务器资源

    1、到Grafana官网获取模版 2、复制模版ID,返回grafana页面导入模版Import dashboard...

  • 渲染模版

    Flask框架渲染jinja模版:一、如何渲染模版:--1.模版放在‘templates’文件夹下;--2.从‘f...

  • 谈判模版1

    一 沟通的基础原则 1 结构: a 决定权在对方 说服的力量,分解为六个行动要素,分别是:互惠、稀缺、从众、权威、...

  • VanGo-VSIX制作

    1. 预备知识 单个基本模版制作: VS自定义项目模版 多个工程模版制作:Creating project tem...

  • 数学知识NO.1

    两数的和一定,差越小,乘积越大。

  • 学习微信小程序(3)——组件

    一、自定义组件 1、组件模版和样式 组件模版组件模版的写法与页面模板相同。组件模版与组件数据结合后生成的节点树,将...

  • express

    1、设置为html模版

  • 设计模式[14]-模版方法模式-Template Method

    1.模版方法模式简介 模版方法模式(Template Method Pattern)是行为型(Behavioral...

网友评论

      本文标题:模版:数学知识(1)

      本文链接:https://www.haomeiwen.com/subject/kailuhtx.html