• 数论从这里开始——一个数的因数


    数论又多又难,该从哪开始学呢?不如先思考一下下面这个问题:

      对于一个自然数n,它的因数是什么?
     
    一、因数的筛法
     
    1.最简单的暴力
      相信只要你有一点数学和编程基础的话,都会想到下面这个最简单暴力的方法:
    1 int find_factor(int x)
    2 {
    3     int i,cnt=0;
    4     for(i=1;i<=x;i++)
    5         if(x%i==0) cnt++;
    6     return cnt;
    7 }

    对,只要从1到n遍历一遍,看看是不是n的因数就解决了。但是这样的话有一个缺点,该算法的时间复杂度为O(n),数据稍微一大,就超时了。

     
    2.修改后的筛法
      那么我们能不能稍微改进一下这个算法呢?答案是可以的,我们都知道一个数的因数一定是成对出现的,那么对于每一组里小的那个因数,它的大小不会超过,利用这一点我们可以将我们的算法做如下改进:
     1 int find_factor(int x)
     2 {
     3     int i,cnt=0;
     4     for(i=1;i*i<=x;i++)
     5         if(x%i==0)
     6         {
     7             if(i*i==x) cnt++;    //当x=i*i时,计数器只需加一次
     8             else cnt+=2;
     9         }
    10     return cnt;
    11 }

    这样一来,我们的时间复杂度就变为了O(),可以解决更大范围的数据了。上面的筛法也是在大多数题目中适用的。

    3.加强版筛法

      但是还是有更加善良的出题人,想要继续考察我们,这就需要我们学习专业的数学知识了。我们先来看下面两个概念:

    ·分解质因数:把n表示为质数相乘的形式,如30=2×3×5。
    我们可以用下面的代码实现分解质因数(时间复杂度O()):
     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 void resolved()
     6 {
     7     int i,n,temp;
     8     scanf("%d",&n);
     9     printf("%d=",n);
    10     temp=n;
    11     if(n<2) return ;    //小于2的数不合法,若n为质数则输出它本身
    12     for(i=2;i*i<=temp;i++)    //根号n复杂度
    13         while(temp%i==0)
    14         {
    15             temp=temp/i;
    16             printf("%d",i);
    17             if(temp!=1) printf("*");
    18         }
    19     if(temp!=1) printf("%d",temp);    //当n为质数
    20     return ;
    21 }
    22 
    23 int main()
    24 {
    25     resolved();
    26     return 0;
    27 }
    分解质因数

    (还有一个时间复杂度为O()的算法,日后再补)

    既然这些因数都是质数,我们可以把一个数n的分解质因数表示为:
      
    其中,p1、p2、p3…pk是由小到大的质数,如:36=2×2×3×3=22×32
     
    ·约数个数定理:对于一个大于1正整数n可以分解质因数:
      
    则n的正约数的个数就是:
      
    其中a1、a2、a3…ak是p1、p2、p3…pk的指数。

    感觉不太好理解?我们来看下面几个实例:

    (1)10=2×5=21×51
    10的因子:1(20×50),2(21×50),5(20×51),10(21×51),
    因子的个数=4=(1+1)×(1+1);
     
    (2)36=2×2×3×3=22×32
    36的因子:1(20×30),2(21×30),4(22×30),3(20×31),6(21×31),12(22×31),9(20×32),18(21×32),36(22×32),
    因子的个数=9=(2+1)×(2+1);
     
    看完这几个例子,不知你感觉如何,接着我们用代码来实现这个过程:
     1 int find_factor(int x)
     2 {
     3     int cnt=1,p=2;    //p代表素数
     4         while(x!=1)
     5         {
     6             int t=1;   //t最终代表(a[i]+1)
     7             while(x%p==0)
     8             {
     9                 x=x/p;
    10                 t++;
    11             }
    12             p++;
    13             cnt*=t;
    14         }
    15     return cnt;
    16 }

    这个方法不光可用用来求一个数的因数的个数,还可以用来解与因数个数相关的问题。

    二、约数个数定理的应用

      我们来看下面几个问题:
     
    1.求1到n里面约数最多的数的约数个数(n<=1018)
      这个题注意数据!!!一看到这个数据我们就知道暴力是不可能实现的,那么又如何使用上面的方法呢?如果用上面的方法进行枚举也是不可取的。
    那么我们来简单分析下这个问题:
    ·我们知道每一个数都可以用质因子的积表示,而约数的个数只与指数有关!
    ·我们知道pn>...>p3>p2>p1,那么假设我们存在某一个ak>a1 那么我们交换pk与p1的指数,显然约数个数不变,但是数变小了!
      如:24=23×31和54=21×33
    ·也就是说对于任何n,m如果pn>pm那么an<am 要好一些。但是不是最优的,不确定,不过这已经为我们淘汰了许多不必要的情况了。这样使用dfs与之结合,枚举每一个质因子的质数,保证其指数递减,然后我们就可以写出代码了:
     
     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 int prime[25]={1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89};    //存一下质数
     6 long long n,ans;
     7 
     8 //pos代表第几个质数,num代表目前因子的个数,multi代表目前的数,len代表循环的次数
     9 void get_num_dfs(int pos,long long num,long long multi,int len)
    10 {
    11     int i;
    12     if(multi>n) return ;
    13     if(multi<=n) ans=max(ans,num);
    14     for(i=1;i<=len;i++)
    15     {
    16         long long temp=pow(prime[pos],i);
    17         if(multi>n/temp) break;    //用除法防止爆long long
    18         get_num_dfs(pos+1,num*(i+1),multi*temp,i);      //进行下一个状态
    19     }
    20     return ;
    21 }
    22 
    23 int main()
    24 {
    25     int t;
    26     scanf("%d",&t);
    27     while(t--)
    28     {
    29         ans=0;
    30         scanf("%lld",&n);
    31         get_num_dfs(1,1,1,64);
    32         printf("%lld
    ",ans);
    33     }
    34     return 0;
    35 }

    Author : Houge  Date : 2019.5.27

    Update log : 

    2019.5.30:修正了一处错误。

     
     

     

  • 相关阅读:
    第一只猫环境准备
    高阶Promise--async
    操作系统源码编译心得
    闭包的应用场景
    全栈微信小程序商城 学习笔记10.1 对更新收货地址接口做权限控制
    全栈微信小程序商城 学习笔记9.1 新建登录接口
    全栈微信小程序商城 学习笔记8.5 product分类商品接口编写
    全栈微信小程序商城 学习笔记8.4 category分类接口编写
    全栈微信小程序商城 学习笔记8.3 product最近新品接口编写
    全栈微信小程序商城 学习笔记8.2 theme详情接口编写
  • 原文地址:https://www.cnblogs.com/CSGOBESTGAMEEVER/p/10928399.html
Copyright © 2020-2023  润新知