首页 > 其他分享 >求约数和的三重境界

求约数和的三重境界

时间:2022-09-02 16:44:25浏览次数:67  
标签:约数 筛法 境界 int sum 三重 primes sd

求约数和的三重境界

一、先上结论

数据量/办法 暴力\(O(N^2)\) 普通筛法\(O(N\cdot logN)\) 欧拉筛法\(O(N)\)
\(n=1e5\) \(13402ms\) \(4ms\) \(2ms\)
\(n=1e6\) 无法忍受,不能出结果 \(67ms\) \(15ms\)

考虑到\(O(N^2)\)的复杂度,按\(C++\)一秒\(1e8\)的运算能力,\(1e6*1e6=1e12\),应该是需要\(1e12 /1e8=1e4=10000\)秒,三个小时,去死吧!太垃圾了~

注意:

  • 普通筛法:代码极短,性能足够,推荐使用
  • 欧拉筛法:代码太长,性能优秀,不推荐使用

二、暴力法

//暴力大法 , 不包含自己
int bforce(int n) {
    int sum = 0;
    for (int i = 1; i < n; i++) {
        if (n % i == 0)
            sum = sum + i;
    }
    return sum;
}

时间复杂度\(O(N^2)\)

三、普通筛法

 for (int i = 1; i <= n; i++)         // O(n)
    for (int j = 2; j <= n / i; j++) // 调和级数O(logn) = n + n / 2 + n / 3 + ... + n/n = lnn + c
        sum2[i * j] += i;

时间复杂度\(O(N\cdot logN)\)

四、线性筛法

原理解析

int primes[N], cnt; // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉
int num[N];         // i的最小质因子构成的一个等比数列求和的结果
int sd[N];          //约数和
void get_primes(int n) {
    sd[1] = 1; // 1的约数和是1

    for (int i = 2; i <= n; i++) { //遍历 2~n,筛质数的同时,筛出每个数字的约数和
        if (!st[i]) {              //如果i是质数
            primes[cnt++] = i;     //记录i到质数数组中
            sd[i] = num[i] = 1 + i; //质数的约数和是1+自身
        }
        for (int j = 0; primes[j] * i <= n; j++) { 
            st[i * primes[j]] = true;              //记录primes[j]的整数倍(i倍)是合数

            if (i % primes[j]) { //如果i不包含primes[j]这个质数因子
                sd[i * primes[j]] = sd[i] * sd[primes[j]];
                num[i * primes[j]] = primes[j] + 1;
            } else { //如果i包含了primes[j]这个质数因子
                sd[i * primes[j]] = sd[i] / num[i] * (num[i] * primes[j] + 1);
                num[i * primes[j]] = num[i] * primes[j] + 1;
                break;
            }
        }
    }
}

五、附:测试对比的代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 1e6 + 10;
int sum1[N], sum2[N], sd[N];
int n = 1e5;

//暴力大法 , 不包含自己
int bforce(int n) {
    int sum = 0;
    for (int i = 1; i < n; i++) {
        if (n % i == 0)
            sum = sum + i;
    }
    return sum;
}

int primes[N], cnt; // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉
int num[N];         // i的最小质因子构成的一个等比数列求和的结果
int sd[N];          //约数和
void get_primes(int n) {
    sd[1] = 1; // 1的约数和是1

    for (int i = 2; i <= n; i++) {  //遍历 2~n,筛质数的同时,筛出每个数字的约数和
        if (!st[i]) {               //如果i是质数
            primes[cnt++] = i;      //记录i到质数数组中
            sd[i] = num[i] = 1 + i; //质数的约数和是1+自身
        }
        for (int j = 0; primes[j] * i <= n; j++) {
            st[i * primes[j]] = true; //记录primes[j]的整数倍(i倍)是合数

            if (i % primes[j]) { //如果i不包含primes[j]这个质数因子
                sd[i * primes[j]] = sd[i] * sd[primes[j]];
                num[i * primes[j]] = primes[j] + 1;
            } else { //如果i包含了primes[j]这个质数因子
                sd[i * primes[j]] = sd[i] / num[i] * (num[i] * primes[j] + 1);
                num[i * primes[j]] = num[i] * primes[j] + 1;
                break;
            }
        }
    }
}

int main() {
    //记录开始时间
    clock_t begin = clock();
    // 1、暴力计算 O(n^2)
    // for (int i = 1; i <= n; i++) sum1[i] = bforce(i);
    // for (int i = 1; i <= n; i++) printf("%d ", sum1[i]);
    puts("");
    //记录结束时间
    clock_t end = clock();
    cout << "Running time:" << (double)(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;

    // 2、通过筛法求出1到n的所有约数之和
    //记录开始时间
    begin = clock();
    for (int i = 1; i <= n; i++)         // O(n)
        for (int j = 2; j <= n / i; j++) // 调和级数O(logn) = n + n / 2 + n / 3 + ... + n/n = lnn + c
            sum2[i * j] += i;

    // for (int i = 1; i <= n; i++) printf("%d ", sum2[i]);
    puts("");
    //记录结束时间
    end = clock();
    cout << "Running time:" << (double)(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;

    // 3、利用欧拉定理筛出约数和
    //记录开始时间
    begin = clock();
    get_primes(n);
    // for (int i = 1; i <= n; i++) printf("%d ", sd[i]-i);
    puts("");
    //记录结束时间
    end = clock();
    cout << "Running time:" << (double)(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    return 0;
}

标签:约数,筛法,境界,int,sum,三重,primes,sd
From: https://www.cnblogs.com/littlehb/p/16650465.html

相关文章

  • 线性筛求 约数个数 与 约数和
    线性筛求约数个数与约数和线性筛,顾名思义,就是欧拉筛,在线性时间内可以求出答案,也就是\(O(N)\)的时间,非常牛\(X\)的效率。一、约数个数根据数字唯一分解定理,设\[\LARG......
  • 两个数的最小公倍数 与 最大公约数
    最小公倍数=两整数的乘积/最大公约数辗转相除法求最大公约数//3.辗转相除法(欧几里得算法)#include<stdio.h>intmain(){inta=0;intb=0;prin......
  • 编程语言的境界
    编程语言也有境界么?当然!初窥门径:掌握语言的基本语法,认真学习了市面上的几本流行教程书。小有所成:熟悉该语言的某些细节,可以利用语言独有的特性写出优雅的代码,用该语言写......
  • 4. [2001年NOIP普及组] 最大公约数和最小公倍数问题
    题目链接(码学堂,数据弱)题目链接(洛谷,数据极强)摘要:1.P,Q是正整数(unsigned)2.要求P,Q以x0为最大公约数,以y0为最小公倍数.试求:满足条件的所有可能的两个正整数的个数. ......
  • [2001年NOIP普及组] 最大公约数和最小公倍数问题
    p*q等于x0*y0。可以枚举到x0*y0中能被x0*y0整除的数,如果这个数与另一个这个数与它的积是x0*y0的数的最大公因数是x0或最小公倍数是y0,那这个数不是p就是q。每枚举出这样一个......
  • 算法竞赛进阶指南 0x65 负环与差分约数
    这里与最短路密切相关可以使用spfa,利用spfa的原理(cnt数组),如果发现一个点是通过了超过n-1条边更新而来,那么就说明存在负环AcWing361.观光奶牛给定一张L个点、P条边的......
  • 练习8:最大公约数和最小公倍数问题
    最大公约数的计算,用到辗转相除法例如:求gcd(24,10),可以转换为gcd(10,4),然后是gcd(4,2),然后是(2,0),最好得出结果是2方法1:functiongcd(a,b){vartempif......
  • [2001年NOIP普及组] 最大公约数和最小公倍数问题
     [2001年NOIP普及组]最大公约数和最小公倍数问题思路:可以运用暴力枚举法。先用两个数的乘积=他们的最大公约数*最小公倍数的公式求出乘积num,再在已知范围内暴力搜素能......
  • [2001年NOIP普及组] 最大公约数和最小公倍数问题
    输入二个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数条件:1.P,A是正整数2.要求P,Q以x0为最大公约数,以y0为最小公倍数.试求:满足条件的所有可......
  • [2001年NOIP普及组] 最大公约数和最小公倍数问题
    [2001年NOIP普及组]最大公约数和最小公倍数问题分析:根据题意,求最大公约数和最小公倍数,其中有一个点是两数乘积等于两数的最大公约数乘最小公倍数。知道这一点后,用for循......