2024-08-14 【基础数论】,和字节跳动大佬的技术面谈

void init(int n){

inv[1]=1;

for(int i=2;i<=n;i++){

inv[i] = ( mod - (mod/i)) * inv[mod % i] % mod;

//线性递推

}

}


素数筛


**素数记数函数:**小于等于x的素数的个数,用F(x)表示,随着x的增大,有这样近似的结果:

F(x) ~ x / ln(x)

利用素数来解决问题,如何求出一定范围内的所有素数

p3912 素数个数

int pri[maxn],cnt=0;

void getprime(int n){//暴力求法

for(int i=2;i<=n;i++){

int flag=1;

for(int j=2;j<i;j++){

if(i%j==0){flag=0;break;}

}

if(flag) pri[cnt++]=i;

}

}

埃氏筛法

思想:素数的倍数一定不是素数

局限:每个合数会被多次筛去:24会被2,3筛去

const int maxn=100;

bool isprime[maxn];

int pri[maxn],cnt=0;

void getprime(int n){

for(int i=1;i<=n;i++) isprime[i]=1;

isprime[0]=isprime[1]=0; //初始化

for(int i=2;i<=n;i++){ //枚举n

if(isprime[i]){ //判断是否为素数的倍数

for(int j=i*i;j<=n;j+=i){

isprime[j]=0; //更改标记

}

}

}

for(int i=2;i<=n;i++){

if(isprime[i])

pri[cnt++]=i;

}

}

线性筛法(欧拉筛):一种可以保证所有数只被筛去一次的筛法,时间复杂度为O(n)。

欧拉筛的难点就在于对if (i % prime[j] == 0)这步的理解,当i是prime[j]的整数倍时,记 m = i / prime[j],那么 i * prime[j+1] 就可以变为 (m * prime[j+1]) * prime[j],这说明 i * prime[j+1] 是 prime[j] 的整数倍,不需要再进行标记(在之后会被 prime[j] * 某个数 标记),对于 prime[j+2] 及之后的素数同理,直接跳出循环,这样就避免了重复标记。

bool judge[maxn];//判断是否为合数

const int maxn=100;

int pri[maxn],cnt=0;

void getprime(int n){

for(int i = 2;i<=n;i++){

if(!judge[i])

pri[cnt++] = i; //记录素数

for(int j = 0;j<cnt;j++){

if(i*pri[j]>n)break; //判断是否越界

judge[i*pri[j]] = 1; //标记合数

if(i%pri[j]==0)break; //关键:当合数进入循环就退出,内层循环只进行一次,减少时间复杂度

}

}

}

区间素数筛

P1835 素数密度

typedef long long ll;

const int maxn = 1000005;

bool is_prime[maxn]; //偏移后判断素数的数组,脚标从0开始

bool is_prime_small[maxn]; //筛选[2,sqrt(b))中所有的素数

ll prime[maxn]; //记录区间中的素数

ll prime_num=0; //区间素数下标

//对区间[a,b)内的整数执行筛法,is_prime[i-a]=true — 表示i是素数 注意这里下标偏移了a,所以从0开始。

void segment_sieve(ll a,ll b) {

for(ll i=0;i*i<b;++i) is_prime_small[i]=true; //对[2,sqrt(b))的初始化全为质数

for(ll i=0;i<b-a;++i) is_prime[i]=true; //对下标偏移后的[a,b)进行初始化

for(ll i=2;i*i<b;++i) {

if(is_prime_small[i]) {

for(ll j=2i;jj<b;j+=i) is_prime_small[j]=false; //筛选[2,sqrt(b));

//(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选

for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i) is_prime[j-a]=false;

}

}

for(ll i=0;i<b-a;++i) //统计个数,prime_num 即为[a,b)区间素数个数,prime[]存储素数是哪些

if(is_prime[i]) prime[prime_num++]=i+a;

}


唯一分解定理


算术基本定理):任意大于1的整数可以表示成质数的乘积。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9P47Hi1j-1628948300609)(C:\Users\86138\AppData\Roaming\Typora\typora-user-images\image-20210813174940598.png)]

判断数字n的因子有哪些,只需要枚举到sqrt(n)即可

判断n范围内哪些数字能够成为因子,只需枚举到n/2即可

//唯一分解定理

const int maxn = 10000 + 7;

int prime[maxn],len,n,num; //prime:质数

int p[maxn],q[maxn]; //p:因子 q:幂次

bool judge[maxn];

void isPrime(){//素数打表

len = 0;

memset(judge,0,sizeof(judge));

judge[1] = 1;

for(int i = 2;i<=maxn;i++){

if(!judge[i])

prime[len++] = i;

for(int j = 0;j<len;j++){

if(i*prime[j]>maxn)break;

judge[i*prime[j]] = 1; //标记不合法

if(i%prime[j]==0)break;

}

}

}

void Split(int k){//分解

memset(p,0,sizeof§);

memset(q,0,sizeof(q));

for(int i = 0;i<len;i++){

while(k%prime[i]==0){ //能被分解

if(!p[num])p[num] = prime[i];

q[num]++;

k/=prime[i];

}

if(p[num])num++;

if(k==1)break;//退出条件是变为1

}

}

int main()

{

isPrime();

while(scanf(“%d”,&n)!=EOF){

num = 0;

Split(n);

printf("%d = ",n);

for(int i = 0;i<num;i++){

if(i!=0)printf(“*”);

printf(“%d^%d”,p[i],q[i]);

}

printf(“\n”);

}

return 0;

}

优化:

根据求因子条件,素数只枚举到prime[i]*prime[i]<=n , 最后若n>1就说明还剩下一个一次方的素数。

原因:

若n%prime[i]==0,则n = prime[i] * q , 然后我们把n里面的prime[i]因子除尽以后,n = 1* k , n就变成了一个新的数字,因为我们的素数枚举是从小到大的,小的除尽了,如果k是一个合数,那么肯定能拆成素数乘积,而且这个素数是肯定>prime[i]的,也一定满足prime[j]prime[j] <=n是能继续拆的;另一方面,如果只剩下一个素数,就不会满足prime[j]*prime[j]<=n了,所以这种方法是可行的。

//优化:

//根据求因子条件,素数只枚举到prime[i]*prime[i]<=n , 最后若n>1就说明还剩下一个一次方的素数

void Split(int k){

memset(p,0,sizeof§);

memset(q,0,sizeof(q));

for(int i = 0;i<len&&prime[i]*prime[i]<=k;i++){//判断条件变化

while(k%prime[i]==0){

if(!p[num])p[num] = prime[i];

q[num]++;

k/=prime[i];

}

if(p[num])num++;

}

if(k>1){p[num] = k;q[num++]++;}//结尾判断

}

//直接分解法(不用素数打表)

void Split(int k){

memset(p,0,sizeof§);

memset(q,0,sizeof(q));

for(int i = 2;i*i<=k;i++){

while(k%i==0){

if(!p[num])p[num] = i;

q[num]++;

k/=i;

}

if(p[num])num++;

}

if(k>1){p[num] = k;q[num++]++;}

}

应用:

欧拉函数求互质


欧拉函数


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYuCqi3E-1628948300611)(C:\Users\86138\AppData\Roaming\Typora\typora-user-images\image-20210813181650346.png)]

欧拉公式的延伸:一个数的所有质因子之和是 euler(n)*n/2;

//欧拉函数,在分解质因数的同时求得欧拉函数

//①直接求小于或等于n,且与n互质的数个数:

inline int euler_one(int n)

{

int ans = n;

for(int i = 2; i * i <= n; ++ i){

if(n % i == 0){ //合数

ans = ans / i * (i - 1); //公式

while(n % i == 0)n /= i; //n除因子改变n

}

}

if(n > 1)ans = ans / n * (n - 1); //还能分解一个质因子

return ans;

}

//②求[1,n]之间每个数的质因数的个数

首先,要明确一些性质,下面三条性质我们会在欧拉函数线性筛中使用到。

1.如果p为素数,则φ§=p-1;

2.若a为n的质因子,且(n%a0&&(n/a)%a0),即如果a是n的多重质因子,则φ(n)=φ(n/a)*a;

3.若a为n的质因子,但(n%a==0&&(n/a)%a! =0),即如果a是n的单质因子,则φ(n)=φ(n/a)*(a-1);

//线性筛欧拉函数建立在线性筛素数的基础之上

void getPhi()

{

phi[1] = 1;//存欧拉函数值

cnt = 0;

for(int i = 1; i < MAXN; i++)

isprime[i] = 1;

for(int i = 2; i < MAXN; i++)

{

if(isprime[i])

{

prime[cnt++] = i; //记录素数

phi[i] = i - 1; //记录欧拉函数值 性质1

}

for(int j = 0; j<cnt&&prime[j]*i<MAXN; j ++) //素数筛

{

isprime[i*prime[j]] = 0; //合数

if(i%prime[j] == 0)

{

phi[i*prime[j]] = phi[i] * (prime[j]);

//如果a是n的多重质因子,则φ(n)=φ(n/a)*a

break;

}

else

{

phi[iprime[j]] = phi[i]( prime[j] - 1 );

//即如果a是n的单质因子,则φ(n)=φ(n/a)*(a-1)

}

}

}

}

//如何求一个数约数的个数

void getDiv()

{

for(int i = 1; i < MAXN; i++)

isprime[i] = 1;

for(int i = 2; i < MAXN; i++)

{

if(isprime[i])

{

prime[cnt++] = i;

minp[i] = 1; //质数的最小质因子为其本身,数量为1

divnum[i]= 2;//质数的约数为1和它本身

}

for(int j = 0; j < cnt&&i*prime[j] < MAXN; j++) //线性筛

{

isprime[i*prime[j]] = 0;

if(i%prime[j] == 0) //同样按照最小质因子*剩余因子积的策略

{

divnum[iprime[j]] = divnum[i] / (minp[i] + 1)(minp[i] + 2); //根据公式进行更新

minp[i*prime[j]] = minp[i] + 1; //最小质因子数多一个

break;

}

else //如果之前没有最小质因子,说明prime[j]为最小质因子,且数量为1

{

divnum[i*prime[j]] = divnum[i] * divnum[prime[j]]; //约数函数的可乘性

minp[i*prime[j]] = 1;

}

}

}

}


中国剩余定理


解决模数互质的同余方程

在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。

具体解法分三步:

  • 找到三个数:从3和5的公倍数中找出被7除余2的最小数30,从3和7的公倍数中找出被5除余3 的最小数63,最后从5和7的公倍数中找出除3余1的最小数140。

  • 然后把这三个数相加得到30 + 63 + 140 = 233满足以上所有条件。

  • 233满足以上所有条件,但是并不是最小的解, 用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PrDtvnUF-1628948300612)(C:\Users\86138\AppData\Roaming\Typora\typora-user-images\image-20210814203551473.png)]

//洛谷P3868

#include<bits/stdc++.h>

using namespace std;

#define LL long long

#define ld long double

#define maxn 1000

LL a[maxn],b[maxn];

void EX_gcd(LL a, LL b, LL &x, LL &y)

{

if(b == 0)//递归出口

{

x = 1;

y = 0;

return;

}

LL t;

EX_gcd(b, a%b, x, y);

t = x;

x = y;

y = t-(a/b)*y;

}

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例

MyBatis答案解析
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

大家看完有什么不懂的可以在下方留言讨论也可以关注。

觉得文章对你有帮助的话记得关注我点个赞支持一下!

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-KfRbOZLo-1711271478272)]
[外链图片转存中…(img-RyVB9O94-1711271478273)]
[外链图片转存中…(img-dMP6OJFa-1711271478273)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-2DKskoiV-1711271478273)]

最后

在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例
[外链图片转存中…(img-T2ziQJ5n-1711271478274)]

[外链图片转存中…(img-VwbbCqCD-1711271478274)]
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

大家看完有什么不懂的可以在下方留言讨论也可以关注。

觉得文章对你有帮助的话记得关注我点个赞支持一下!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值