• 求约数和的三重境界


    求约数和的三重境界

    一、先上结论

    数据量/办法 暴力\(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;
    }
    
  • 相关阅读:
    Pod中spec的字段常用字段及含义
    day62-无名有名分组反向解析,路由分发,视图层等
    day61-数据增删改查,orm表关系,django请求生命周期流程图
    day60-静态文件,数据库,ORM
    day59-简易版本的web框架,django框架初识
    day58-jquery后续,前端BootSrtap,图书管理系统搭建
    day57-jQuery练习与操作,各种事件
    day56-js,jquery
    day55-js
    day54-js类型与对象
  • 原文地址:https://www.cnblogs.com/littlehb/p/16650465.html
Copyright © 2020-2023  润新知