第二十五章 数论——约数

本文详细介绍了数论中的约数概念及其求解方法,包括约数的试除法求解、约数个数及约数之和的计算,并通过算数基本定理给出算法思路。此外还介绍了最大公约数的欧几里德算法及其证明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是约数

如果a%b==0,那么b就叫做a的约数,a和b必须是整数。

二、约数的求解——试除法

1、问题

在这里插入图片描述

2、思路分析

我们只需要枚举1到n的所有数字即可,看看这个范围内的数字是否满足 a a a % b = = 0 b==0 b==0。这个过程的复杂度是 O ( n ) O(n) O(n)

那么我们如何优化呢?

约数必定是成对出现的,所以我们只需要算出一个,那么另外一个就可以利用公式: a / b a/b a/b计算出来。也就是说我们只需要找到一半的约数。

而这一半的约数必定是在 1 1 1 n \sqrt n n 的。

我们可以简单的证明一下,假设n存在一对约数,这对约数中的两个值都大于 n \sqrt n n ,那么此时二者的乘积必定大于 n n n,并不等于n,所以此时这两个数不是n的约数,故假设不成立。所以我们提出的结论是对的。

但是我们还需要避免重复输出的情况:

比如数字 9 9 9

9 = 3 ∗ 3 9=3*3 9=33,这两个约数其实是一个,所以需要特判一下,

3、代码实现

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n;
void f(int a)
{
    vector<int>v;
    for(int i=1;i<=a/i;i++)
    {
        if(a%i==0)
        {
            v.push_back(i);
            if(i!=a/i)
                v.push_back(a/i);
        }
    }
    sort(v.begin(),v.end());
    for(int i=0;i<v.size();i++)
    {
        cout<<v[i]<<" ";
    }
    puts("");
}
int main()
{
    cin>>n;
    while(n--)
    {
        int a;
        cin>>a;
        f(a);
    }
}

三、约数个数

1、问题描述

在这里插入图片描述

2、算法思路

根据算数基本定理
我们任何一个合数都可以写成有限个质数的乘积:
n = p 1 q ∗ p 2 w ∗ p 3 e . . . p n m n=p_1^q*p_2^w*p_3^e...p_n^m n=p1qp2wp3e...pnm

上面的式子中,我们任意取出几个 p p p,就能组成 n n n的约数

因此这就是一个排列组合的问题。

对于 p 1 p_1 p1而言,我们可以取出 [ 0 , q ] [0,q] [0,q]个,共 q + 1 q+1 q+1种取法。

后面的也同理,那么根据乘法原理

我们的约数的个数为:

( q + 1 ) ∗ ( w + 1 ) ∗ ( e + 1 ) . . . ( m + 1 ) (q+1)*(w+1)*(e+1)...(m+1) (q+1)(w+1)(e+1)...(m+1)

所以我们的目的就是求出一个数的所有质因数的指数,对于如何求解一个数的质因数,请看前面关于质数的文章。

质因数文章的传送门

由于质因数和指数的是紧密相关的。因此,我们可以利用质因数去索引指数,为了提高索引的效率,我们可以使用哈希表

3、代码实现

#include<iostream>
#include<unordered_map>
const int mod=1e9+7;
using namespace std;
unordered_map<int,int>primes;
int n,a;
void f(int a)
{
	//求解质因数
    for(int i=2;i<=a/i;i++)
    {
        if(a%i==0)
        {
            while(a%i==0)
            {
            	//记录质因数的指数
                primes[i]++;
                a/=i;
            }
        }
    }
    if(a>1)primes[a]++;
}

int main()
{
    cin>>n;
    while(n--)
    {
        cin>>a;
        f(a);
    }
    long long res = 1;
    //利用公式计算个数
    for (auto p : primes)
        res = res * (p.second + 1) % mod;
    cout<<res<<endl;
    return 0;
}

四、约数之和

1、问题描述

在这里插入图片描述

2、算法思路

我们同样采取算术基本定理的方式去求解:

n = p 1 q ∗ p 2 w ∗ p 3 e . . . p n m n=p_1^q*p_2^w*p_3^e...p_n^m n=p1qp2wp3e...pnm

而我们将约数之和记作: s u m sum sum

那么

s u m = sum= sum=
( p 1 0 + p 1 1 + p 1 2 . . . p 1 q ) ∗ ( p 2 0 + p 2 1 + p 2 2 . . . p 2 w ) ∗ ( p 3 0 + p 3 1 + p 3 2 . . . p 3 e ) . . . ( p n 0 + p n 1 + p n 2 . . . p n m ) (p_1^0+p_1^1+p_1^2...p_1^q)*(p_2^0+p_2^1+p_2^2...p_2^w)*(p_3^0+p_3^1+p_3^2...p_3^e)...(p_n^0+p_n^1+p_n^2...p_n^m) (p10+p11+p12...p1q)(p20+p21+p22...p2w)(p30+p31+p32...p3e)...(pn0+pn1+pn2...pnm)

想要证明上述的公式其实很简单,直接将上面的多项式展开就行了。作者这里就不展开了。

那么这道题的思路就很明确了,依旧是求一个数的质因数以及对应的指数。然后按照上面的公式求解。

此时我们需要再介绍一个算法,秦九韶算法

对于式子:
p 1 0 + p 1 1 + p 1 2 . . . p 1 q p_1^0+p_1^1+p_1^2...p_1^q p10+p11+p12...p1q
我们令
t = 1 t=1 t=1
然后接下来循环 q q q
t = t ∗ p 1 + 1 t=t*p_1+1 t=tp1+1

第一次循环: t = 1 + p 1 1 t=1+p_1^1 t=1+p11

第二次循环 t = t ∗ p 1 + 1 = 1 + p 1 1 + p 1 2 t=t*p1+1=1+p_1^1+p_1^2 t=tp1+1=1+p11+p12

第三次循环 t = 1 + p 1 1 + p 1 2 + p 1 3 t=1+p_1^1+p_1^2+p_1^3 t=1+p11+p12+p13

第q次循环: t = 1 + p 1 1 + p 1 2 . . . p 1 q t=1+p_1^1+p_1^2...p_1^q t=1+p11+p12...p1q

3、代码实现

#include<iostream>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
int n,a;
unordered_map<int,int>primes;
int main()
{
    cin>>n;
    while(n--)
    {
        cin>>a;
        for(int i=2;i<=a/i;i++)
        {
            while(a%i==0)
            {
                primes[i]++;
                a/=i;
            }
        }
        if(a>1)primes[a]++;
    }
    long long sum=1;
    for(auto x:primes)
    {
        long long t=1;
        int p=x.first,e=x.second;
        while(e--)t=(t*p+1)%mod;
        sum=sum*t%mod;
    }
    cout<<sum<<endl;

}

五、最大公约数——欧几里德算法

1、问题描述

在这里插入图片描述

2、算法思路

(1)算法内容

a a a b b b的最大公约数等于 a a a a a a m o d mod mod b b b的最大公约数。
表达式为:

g c d ( a , b ) = g c d ( b , a    m o d    b ) gcd(a,b)=gcd(b,a\;mod\;b) gcd(a,b)=gcd(b,amodb)

(2)算法证明

我们先明确这样一个表示方法:

如果 a   m o d   b = 0 a\ mod \ b=0 a mod b=0,就说明 b b b能够整除 a a a,记作 b ∣ a b|a ba

若 d ∣ a , d ∣ b ,则 d ∣ ( k a + m b ) 若d|a,d|b,则d|(ka+mb) dadb,则d(ka+mb)


证明:
a / d = x 1 a/d=x_1 a/d=x1 b / d = x 2 b/d=x_2 b/d=x2

那么

( k a + m b ) / d = ( k x 1 d + m x 2 d ) / d = k x 1 + m x 2 (ka+mb)/d=(kx_1d+mx_2d)/d=kx_1+mx_2 (ka+mb)/d=(kx1d+mx2d)/d=kx1+mx2

因为 k x 1 + m x 2 kx_1+mx_2 kx1+mx2 是整数,所以结论成立。


接着:

a   m o d   b = a − [ a / b ] ∗ b a\ mod\ b=a-[a/b]*b a mod b=a[a/b]b,其中 f ( x ) = [ x ] f(x)=[x] f(x)=[x]为取整函数(高斯函数)。

那么这个式子可以简写为:
a   m o d   b = = a − c ∗ b a\ mod\ b==a-c*b a mod b==acb
所以
g c d ( b , a   m o d   b ) = g c d ( b , a − c ∗ b ) gcd(b,a\ mod \ b)=gcd(b,a-c*b) gcd(b,a mod b)=gcd(b,acb)

现在假设:

d ∣ a , d ∣ b d|a,d|b da,db

所以
d ∣ ( a − c ∗ b ) d|(a-c*b) d(acb)
即:
d ∣ ( a   m o d   b ) d|(a\ mod\ b) d(a mod b)

其中,因为: d ∣ a , d ∣ b d|a,d|b da,db,所以 d d d a a a b b b的公约数。

又因为 d ∣ b , d ∣ ( a   m o d   b ) d|b,d|(a\ mod\ b) db,d(a mod b),所以 d d d b b b ( a   m o d   b ) (a\ mod\ b) (a mod b)的公约数。

所以,二者最大公约数相同。

即:
g c d ( a , b ) = g c d ( b , a    m o d    b ) gcd(a,b)=gcd(b,a\;mod\;b) gcd(a,b)=gcd(b,amodb)

3、代码实现

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a,b;
        cin>>a>>b;
        cout<<gcd(a,b)<<endl;
    }
}
<think>嗯,这个问题是要找最小的正整数n,使得n的四次方有45个正约数。那我得先回忆一下关于约数个数和质因数分解的知识。 首先,根据数论中的定理,一个数的约数个数是由它的质因数分解形式决定的。比如说,如果一个数分解质因数后是p₁^a * p₂^b * … p_k^c,那么它的约数个数就是(a+1)(b+1)...(c+1)。所以,题目中的n^4的质因数分解形式必须满足各个指数加1后的乘积等于45。 那接下来我需要将45分解因数,找出可能的组合。因为n的四次方的质因数指数形式是原n的质因数的指数乘以4后的结果。比如,假n的质因数分解是p1^a * p2^b,那么n^4就是p1^(4a) * p2^(4b),对应的约数个数是(4a+1)(4b+1)...,这个乘积要等于45。 现在分解45的可能因数组合。45=45×1,或者15×3,或者9×5,或者3×3×5。但要注意,这些组合中的每个元素都要满足是形如4k+1的形式,因为原来的指数是n的各个质因数的指数乘以4后的结果,所以每个4a+1必须是分解后的因数中的一个。 例如,假n的质因数分解是单一的质数,那么4a+1=45,这时候a=(45-1)/4=11,但是45-1=44,44除以4得11,是整数。这样对应的n就是p^11,但这样p是质数,取最小的质数2的话,n=2^11=2048。这可能是一个候选答案,但可能还有更小的组合。 另外,如果分解成多个质因数的情况,比如,45=9×5。这时候,每个因数对应的是不同的质数的指数。例如,对于两个质数的情况,假他们的指数分别为a和b,那么4a+1=9,4b+1=5。解得a=(9-1)/4=2,b=(5-1)/4=1。那么对应的n就是p1^2 * p2^1,其中p1和p2是两个不同的质数。这时候取最小的质数组合,比如p1=2,p2=3,那么n=2^2*3^1=4*3=12。这时候n^4=12^4=(2^2*3)^4=2^8*3^4。其约数个数是(8+1)(4+1)=9*5=45,符合条件。这时候n=12,显然比之前的2048小很多,所以可能这是更小的解。 那还有没有更小的组合呢?比如分解成三个因数的情况。例如45=3×3×5。这时候对应的三个因数都是3、3、5。那么每个4a_i+1等于这些因数中的一个。例如,假三个质数的指数分别为a、b、c,那么4a+1=3,4b+1=3,4c+1=5。解得a=(3-1)/4=0.5,这显然不是整数,所以这种情况不可能。所以分解成三个质因数的情况不可行。 再看另一种分解方式,比如45=5×3×3,同样会遇到同样的问题,因为其中一个因数3对应的指数需要满足4k+1=3,k=0.5,不行。所以这种情况无法实现。 那其他可能的分解方式呢?比如45=15×3,这时候对应的两个因数,第一个15,第二个是3。于是4a+1=15,解得a=(15-1)/4=14/4=3.5,不是整数,所以这种情况也不行。 而另一个分解方式45=5×9,已经检查过对应的两个质因数的情况可行。或者有没有其他的分解方式? 比如,检查是否还有其他可能的因数分解。例如,45=45×1,这时候对应的两个情况:一个质因数的情况,即指数是44/4=11,如前所述的n=2^11=2048。或者分解成两个质因数,其中一个是45,另一个1。但4a+1=1的话,a=0,这表示该质因数不存在,所以可能只有单个质因数的情况。此时得到的n=2^11=2048,而另一种情况是两个质因数,其中一个1的情况,但显然不行。 所以目前看来,可行的分解方式只有两种可能:一种是单个质因数,指数为11,得到n=2^11=2048;另一种是两个质因数,指数分别为2和1,得到n=2^2*3=12。显然,12更小,所以正确的答案应该是12? 那现在需要确认当n=12的时候,n^4的约数个数是否是45。 计算n=12时,n的四次方是12^4。分解质因数:12=2^2*3^1,所以12^4=2^(2*4)*3^(1*4)=2^8*3^4。约数的个数是(8+1)*(4+1)=9*5=45。确实符合条件。那这时候n=12,显然比2048小很多。所以正确的答案应该是12。 那这个时候,问题的最小n是12吗? 接下来,有没有比12更小的可能? 比如,是否存在其他质因数组合,使得n更小? 比如,考虑三个质因数的情况是否能满足条件。比如分解45=3×3×5,但此时每个因子对应的4a+1必须是3、3、5。但4a+1=3的话,a=0.5,不行。所以三个质因数的情况不可行。 或者有没有其他分解方式,比如两个质因数中的其中一个是更高的指数? 比如,如果分解45=5×9,对应的情况是4a+1=5,4b+1=9。解得a=(5-1)/4=1,b=(9-1)/4=2。那么对应的n应该是p1^1 * p2^2。此时,最小的质数组合是p1=2,p2=3。那么n=2^1 *3^2=2*9=18。这时候n^4的质因数分解是2^4 *3^8,约数个数是(4+1)(8+1)=5*9=45。同样符合条件。这时候n=18,比12大,所以12更优。 或者另一个顺序,p1=3,p2=2的话,得到n=3*4=12,和之前的结果一样。 哦,原来当分解为4a+1=9和4b+1=5时,对应的指数是a=2,b=1。这时候n的结构是p1^2 * p2^1,而如果取p1=2,p2=3,则n=4*3=12。而如果是分解为4a+1=5,4b+1=9的话,指数是a=1,b=2,那么n的结构是p1^1 *p2^2,此时最小的质数组合是2^1*3^2=2*9=18,比12大。所以显然,当分解为9*5时,两种排列方式对应的n的值不同,其中较小的是12。 所以现在确定,n的最小可能值是12。 那现在如何验证是否存在更小的n? 比如,n=6,是否满足条件? n=6的四次方是6^4=(2*3)^4=2^4*3^4。约数个数是(4+1)(4+1)=25,不是45。不符合。 n=8,即2^3。四次方是2^12。约数个数是12+1=13,也不符合。 n=12的四次方约数个数是45,符合条件。所以目前看来,12是可能的最小值。 那有没有其他组合方式?比如n的质因数分解有三个质数? 比如,如果分解45为三个因数的乘积,例如3×3×5,但如前所述,这会要求每个因数对应4k+1的形式,而3对应的k必须是0.5,所以无法。 所以,结论是,最小的n是12。 现在需要编写C语言代码来找这个数。但是直接计算的话,可能需要遍历可能的n,计算n^4的约数个数,直到找到第一个符合条件的。但这种方法效率可能不高,尤其是当n较大时。但在这个问题中,答案可能较小,所以或许可以接受。 不过更高效的方法是根据约数个数的公式,构造可能的质因数分解形式,然后找到对应的n的最小值。 可能的质因数分解形式对应的n的结构: 根据前面的分析,n的质因数分解应该满足,当每个质数的指数乘以4之后加1乘积等于45。即,假n的分解是p1^a * p2^b *…,那么(4a+1)*(4b+1)*...=45。所以我们需要找到所有可能的指数组合,使得对应的乘积等于45,然后计算对应的n的大小,并找出最小的那个。 可能的指数组合: 将45分解为多个数相乘的形式,每个数都必须是4k+1的形式。例如: 情况一:单质因数,指数为11。对应的n=2^11=2048。 情况二:两个质因数,指数分别为2和1。对应的n=2^2 *3^1=12。或者3^2 *2^1= 9*2=18。显然前者更小。 情况三:是否还有其他可能的分解方式? 例如,将45分解为3*15。但15=4k+1 →k=(15-1)/4=3.5,不是整数,所以不行。或者分解为5*9,其中5=4*1+1,9=4*2+1。对应的指数是1和2,对应的n的结构是p1^1 *p2^2,当p1=2,p2=3时,n=2^1*3^2=2*9=18,而p1=3,p2=2时,n=3*4=12。哦,这其实和之前的第二种情况是一样的,只是顺序不同。所以这种情况对应的n的最小是12。 所以,综上所述,最小的n是12。 所以,编写代码的话,直接输出12即可。或者需要动态计算? 但题目可能希望用程序的方式找到这个结果。不过对于这个特定的问题,答案可能较小,所以可以直接给出。 那C语言的代码可以很简单: #include <stdio.h> int main() { printf("12\n"); return 0; } 但可能需要更通用的解法,比如找出所有可能的指数组合,并计算对应的n的值,然后取最小的那个。 不过在这种情况下,由于问题较为简单,直接返回12即可。但需要确认是否存在更小的可能。 比如,有没有其他质因数组合?例如,三个质因数的情况,但之前分析过不可能。 所以,正确的答案应为12,对应的C语言代码即输出12。</think>要找到最小的正整数 \( n \),使得 \( n^4 \) 恰好有45个正约数,我们需利用质因数分解和约数个数公式。 \( n = p_1^{a} \cdot p_2^{b} \cdot \ldots \),则 \( n^4 = p_1^{4a} \cdot p_2^{4b} \cdot \ldots \)。其约数个数为 \( (4a+1)(4b+1)\ldots = 45 \)。 分解45的可能组合: - **单质因数**:\( 45 = 45 \),此时 \( 4a+1 = 45 \) → \( a = 11 \),对应 \( n = 2^{11} = 2048 \)。 - **双质因数**:\( 45 = 9 \times 5 \),此时 \( 4a+1 = 9 \), \( 4b+1 = 5 \),解得 \( a = 2 \), \( b = 1 \)。取最小质数 \( p_1 = 2 \), \( p_2 = 3 \),则 \( n = 2^2 \cdot 3^1 = 12 \)。验证 \( 12^4 = 2^8 \cdot 3^4 \),其约数个数为 \( (8+1)(4+1) = 45 \),符合条件。 对比可知,\( n = 12 \) 是最小解。 ```c #include <stdio.h> int main() { // 直接输出最小解12 printf("%d\n", 12); return 0; } ``` **代码解释**: 通过分析,确定最小 \( n \)12。代码直接输出结果,无需复杂计算。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值