第二十五章 数论——约数

news2025/2/21 21:55:13

第二十五章 数论——约数

  • 一、什么是约数
  • 二、约数的求解——试除法
    • 1、问题
    • 2、思路分析
    • 3、代码实现
  • 三、约数个数
    • 1、问题描述
    • 2、算法思路
    • 3、代码实现
  • 四、约数之和
    • 1、问题描述
    • 2、算法思路
    • 3、代码实现
  • 五、最大公约数——欧几里德算法
    • 1、问题描述
    • 2、算法思路
      • (1)算法内容
      • (2)算法证明
    • 3、代码实现

一、什么是约数

如果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;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端实现文件上传(点击+拖拽)

一、简介 之前在Vue项目中使用过element的上传组件&#xff0c;实现了点击上传拖拽上传的两种上传功能。然后我就在想是否可以通过原生的htmljs来实现文件的点击上传和拖拽上传&#xff0c;说干就干。 首先是点击获取上传文件自然没的说&#xff0c;只需要借助input标签即可&a…

Java面试题(六)RabbitMQ常见面试题

RabbitMQ常见面试题RabbitMQ架构设计RabbitMQ有哪些优点RabbitMQ事物机制RabbitMQ持久化机制持久化和非持久化什么时候需要持久化&#xff1f;落盘过程文件删除RabbitMQ如何保证消息不丢失RabbitMQ如何保证消息不被重复消费RabbitMQ死信队列&#xff0c;延时队列死信队列延时队…

如何使用 ChatGPT 进行教学,教师可以利用 ChatGPT 的 5 种方式

我们听说过很多关于学生如何使用 ChatGPT 撰写论文和布置家庭作业的信息。 我们一直在讨论围绕这个问题的担忧,并争先恐后地为 ChatGPT 寻找 AI 检测工具,据传 OpenAI 也在致力于此。 但是关于教师如何将 ChatGPT 用于他们自己的工作的讨论并不多。 在从教师的角度对 Chat…

20221222 Coppeliasim的视频导出功能

Video exporter CoppeliaSim的视频记录器可以对the page, scene hierarchy and model browser areas进行录屏&#xff0c;保存成视频文件。Dialogs, menu bar, toolbars and popup menus &#xff08;对话框、菜单栏、工具栏和弹出菜单&#xff09;不会被录进去. 默认情况下&am…

【C++11】包装器

目录 1.function包装器 1.1什么是函数包装器(function)&#xff1f; 1.2为啥使用函数包装器(function)&#xff1f; 2.bind包装器 2.1绑定普通函数和调整传参顺序 2.2绑定类成员函数 1.function包装器 头文件#include<functional> 1.1什么是函数包装器(function)&a…

2022年终总结

不知不觉就到了年末&#xff0c;感叹时间过的真快。 我自己坚持写了七年多的博客&#xff0c;但这其实是我第一次去写年终总结。也不知道怎么写&#xff0c;就简单聊聊。 写博客的初衷就是个人收获&#xff0c;学习的记录&#xff0c;分享出来如果能帮到别人那就更好了。毕竟…

(一)LTspice简介2

文章目录前言一、LTspice的仿真过程二、spice的模型三、LTspice的工具栏和快捷键四、LTspice中的数量级前言 上一节我们学习了LTspice的安装&#xff0c;很简单&#xff0c;无脑安装 &#xff08;一&#xff09;LTspice安装 这一节我们继续学习LTspice的简介&#xff0c;主要包…

题:A-B 数对(二分)

A-B 数对 - 洛谷 题目背景 出题是一件痛苦的事情&#xff01; 相同的题目看多了也会有审美疲劳&#xff0c;于是我舍弃了大家所熟悉的 AB Problem&#xff0c;改用 A-B 了哈哈&#xff01; 题目描述 给出一串正整数数列以及一个正整数 CC&#xff0c;要求计算出所有满足 A…

通过Wireshark分析Apache服务器的SSL协议工作过程

文章目录一、实验环境二、为Apache服务器启用SSL1.获取SSL证书2.修改httpd.conf配置文件3.修改httpd-ssl.conf配置文件4.启动Apache服务三、SSL/TLS工作过程分析一、实验环境 操作系统&#xff1a;macOS Ventura 13.0.1 Apache&#xff1a;Apache/2.4.54 (Unix)&#xff0c;此…

喜提JDK的BUG一枚、多线程的情况下请谨慎使用这个类的stream遍历。

前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候&#xff0c;看到一个 pr&#xff0c;虽说是在 RocketMQ 的地盘上发现的&#xff0c;但是这个玩意吧&#xff0c;其实和 RocketMQ 没有任何关系。 纯纯的就是 JDK 的一个 BUG。 我先问你一个问题&#xff1a;LinkedBlockingQueue…

vue3 antd table表格的样式修改(二)利用rowClassName给table添加行样式

vue3 antd项目实战——修改ant design vue组件中table表格的默认样式&#xff08;二&#xff09;知识调用场景复现修改table表格的行样式一、rowClassName添加行样式二、表格的不可控操作写在最后知识调用 文章中可能会用到的知识链接vue3ant design vuets实战【ant-design-vu…

Autosar MCAL-SPI配置及使用

文章目录前言SPI协议基础Autosar SPI专有名词SpiDriverSpiChannelSpiChannelIdSpiChannelTypeSpiDataWidthSpiDefaultDataSpiEbMaxLengthSpiIbNBuffersSpiTransferStartSpiExternalDeviceSpiBaudrateSpiAutoCalcBaudParamsSpiCsIdentifierSpiCsPolaritySpiCsSelectionSpiDataSh…

前端工程师可以分成 4 种,你属于哪一种?

在这篇文章中&#xff0c;探讨四种常见的前端工程&#xff0c;1&#xff09;产品工程师&#xff0c;2&#xff09;UI 基建工程师&#xff0c;3&#xff09;设计师&#xff0c;4&#xff09;工具基建工程师&#xff0c;你属于哪一种&#xff1f; 产品工程师 产品工程师负责公司…

6.s081 学习实验记录(二)xv6 and unix utilities

文章目录一、boot xv6二、sleep三、pingpong四、primes五、find六、xargs该实验主要用来熟悉xv6以及其系统调用 一、boot xv6 实验目的&#xff1a; 启动xv6系统&#xff0c;并使用提供的命令ls&#xff0c;列出系统所有的文件ctrl p&#xff0c;打印当前运行的进程ctrl a…

Ubuntu22.04使用kubeadm安装k8s 1.26版本高可用集群

目录阿里云ACK集群的架构ACK实例的创建过程如下安装前的准备主机规划基线准备所有k8s master、worker节点安装kubeadmkubectlkubelet创建集群负载均衡器HAproxy安装keepalived 和haproxy配置haproxy配置keepalivedkubeadm部署第一台master节点Calico网络组件一键安装安装完成阿…

mPEG-Biotin,甲氧基-聚乙二醇-生物素科研实验用试剂

​​ 英文名称&#xff1a;mPEG-Biotin 中文名称&#xff1a;甲氧基-聚乙二醇-生物素 mPEG生物素可通过与链霉亲和素和抗生物素结合进行聚乙二醇化&#xff0c;具有高亲和力和特异性。生物素通过稳定的酰胺连接物与线性PEG结合。 提示&#xff1a;避免频繁的溶解和冻干&…

node.js+uni计算机毕设项目基于微信的同学会小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

GraphQL在Django中的使用

简介 特点 请求你所要的数据&#xff0c;不多不少获取多个资源&#xff0c;只用一个请求描述所有的可能&#xff0c;类型系统几乎所有语言支持 谁在用 文档 Graphene-PythonGraphQL | A query language for your API 背景 传统restful的接口定义类型多&#xff0c;试图简…

WordPress 添加百度主动推送代码,加速网站收录保护原创文章

WordPress是世界上使用人数最多开源程序之一&#xff0c;它的优点有很多&#xff0c;譬如&#xff0c;简单易用、样式丰富&#xff0c;模板众多&#xff0c;安全性能高&#xff0c;对搜索引擎友好&#xff0c;收录快、扩展性强&#xff0c;功能强大等等&#xff0c;其中我最喜欢…

JVM的类加载机制

一、类加载机制 类的加载指的是将类的.class文件中的二进制数据读入到内存中&#xff0c;将其放在运行时数据区的方法区内****&#xff0c;然后在堆区创建一个java.lang.Class对象&#xff0c;用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象&…