zoukankan      html  css  js  c++  java
  • 数论——素数和反素数

    素数

    素数一般判定方法

    bool isPrime(a) {
      if (a < 2) return 0;
      for (int i = 2; i * i <= a; ++i)
        if (a % i) return 0;
      return 1;
    }

    但对于long long int的数,O(√n)的复杂度还是会时间超限。

    此时要用Miller-Rabin 素性测试,但也只是极为可能是素数。

    复杂度O(klog3n)。

    #include <iostream>
    using namespace std ;
    #define rd(x) (rand()%(x))
    typedef unsigned long long ll;
    
    ll pow_mod(ll a,ll b,ll r)
    {
        ll ans=1,buff=a;
        while(b)
        {
            if(b&1)
                ans=(ans*buff)%r;
            buff=(buff*buff)%r;
            b>>=1;
        }
        return ans;
    }
    
    bool test(ll n,ll a,ll d)
    {
        if(n==2) return true;
        if(n==a) return false;
        if(!(n&1)) return false;
        while(!(d&1)) d>>=1;
        ll t = pow_mod(a,d,n);
        while(d!=n-1&&t!=n-1&&t!=1){
            t = t*t%n;//下面介绍防止溢出的办法,对应数据量为10^18次方;
            d<<=1;
        }
        return t == n-1||(d&1)==1;//要么t能变成n-1,要么一开始t就等于1
    }
    
    bool isprime(ll n)
    {
        int a[] = {2,3,5,7};//或者自己生成[2,N-1]以内的随机数rand()%(n-2)+2
        for(int i = 0; i <= 3; ++i){
            if(n==a[i]) return true;
            if(!test(n,a[i],n-1)) return false;
        }
        return true;
    }
    int main()
    {
        int t,ans=0;
        ll N;  
        for(cin >> t;t;t--){
            cin >> N;
            cout << ((isprime(N))?"Yes":"No") <<endl;
        }
    }

    注意:代码中longlong*longlong可能溢出,可以优化成快速乘。

    反素数

    定义:如果某个正整数 n满足如下条件,则称为是反素数: 任何小于 n的正数的约数个数都小于n的约数个数。

    将该数进行质因子分解,我们可以得出n=p1k1p2k2...pnkn,所以因子个数为(k1+1)*(k2+1)..*(kn+1)

    因为反素数的定义,可以得出反素数的两个特点

     所以可以通过枚举,去确定反素数。

    经典题型1:求因子数一定的最小数

    题目链接: https://codeforces.com/problemset/problem/27/E

    对于这种题,我么只要以因子数为 dfs 的返回条件基准,不断更新找到的最小值就可以了

    #include <stdio.h>
    #define ULL unsigned long long
    #define INF ~0ULL
    ULL p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
    
    ULL ans;
    ULL n;
    
    // depth: 当前在枚举第几个素数。num: 当前因子数。
    // temp: 当前因子数量为 num
    // 的时候的数值。up:上一个素数的幂,这次应该小于等于这个幂次嘛
    void dfs(ULL depth, ULL temp, ULL num, ULL up) {
      if (num > n || depth >= 16) return;
      if (num == n && ans > temp) {
        ans = temp;
        return;
      }
      for (int i = 1; i <= up; i++) {
        if (temp / p[depth] > ans) break;
        dfs(depth + 1, temp = temp * p[depth], num * (i + 1), i);
      }
    }
    
    int main() {
      while (scanf("%llu", &n) != EOF) {
        ans = INF;
        dfs(0, 1, 1, 64);
        printf("%llu
    ", ans);
      }
      return 0;
    }

    经典题型2:求 n 以内因子数最多的数

    题目链接http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562

    思路同上,只不过要改改 dfs 的返回条件。注意这样的题目的数据范围,我一开始用了 int,应该是溢出了,在循环里可能就出不来了就超时了。

    上代码,0ms 过。注释就没必要写了上面写的很清楚了。

    #include <cstdio>
    #include <iostream>
    #define ULL unsigned long long
    
    int p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
    ULL n;
    ULL ans, ans_num;  // ans 为 n 以内的最大反素数(会持续更新),ans_sum 为 ans
                       // 的因子数。
    
    void dfs(int depth, ULL temp, ULL num, int up) {
      if (depth >= 16 || temp > n) return;
      if (num > ans_num) {
        ans = temp;
        ans_num = num;
      }
      if (num == ans_num && ans > temp) ans = temp;
      for (int i = 1; i <= up; i++) {
        if (temp * p[depth] > n) break;
        dfs(depth + 1, temp *= p[depth], num * (i + 1), i);
      }
      return;
    }
    
    int main() {
      while (scanf("%llu", &n) != EOF) {
        ans_num = 0;
        dfs(0, 1, 1, 60);
        printf("%llu
    ", ans);
      }
      return 0;
    }

     两个题目都是利用n=p1k1p2k2...pnkn,所以因子个数为(k1+1)*(k2+1)..*(kn+1)这一性质进行枚举。

  • 相关阅读:
    foreach
    if
    注意事项
    Maven测试
    课程评价
    个人总结
    HTML表格CSS美化
    让多个输入框对齐
    CSS样式写在JSP代码中的几种方法
    日常
  • 原文地址:https://www.cnblogs.com/2462478392Lee/p/12364294.html
Copyright © 2011-2022 走看看