首页 > 其他分享 >从小到大获取整数的所有因数

从小到大获取整数的所有因数

时间:2024-03-04 21:24:16浏览次数:30  
标签:mut 复杂度 算法 整数 因数 let sqrt println 从小到大

一种朴素的Rust语言的算法如下:

fn get_all_factors_normal(n: u64) -> Vec<u64> {
    let n_sqrt = (n as f64).sqrt().floor() as u64;
    let mut res = Vec::new();
    for i in 1..=n_sqrt {
        if n % i == 0 {
            //println!("{}", i);
            res.push(i);
            if i * i != n {
                res.push(n / i); //为了便于对比,没有把这个数插入到对应的位置,而是直接使用时间复杂度为O(1)的push
                //println!("{}", n / i);
            }
        }
    }
    res
}

这种算法时间复杂度为 \(O(\sqrt n)\)

如果能够先获得所有质因数及其数量,在B树中插入1,然后每次弹出B树的最小值,与所有可能的因数相乘,再插入B树中自动排序,那么也就能获得所有因数,代码如下:

fn get_all_factors(mut n: u64) -> Vec<u64> {
    use std::collections::BTreeMap;
    let n_sqrt = (n as f64).sqrt().floor() as u64;
    let mut vp: Vec<(u64, u32)> = Vec::new();
    for k in 2..=n_sqrt {
        if k > n {
            break;
        }
        while n % k == 0 {
            n /= k;
            if let Some(b) = vp.last_mut() {
                if b.0 == k {
                    *b = (b.0, b.1 + 1);
                } else {
                    vp.push((k, 1));
                }
            } else {
                vp.push((k, 1));
            }
        }
    }
    if n != 1 {
        vp.push((n, 1));
    }
    let empty = vec![0; vp.len()];
    let mut tr: BTreeMap<u64, Vec<u32>> = BTreeMap::new();
    tr.insert(1, empty);
    let mut res = Vec::new();
    while let Some((b, v)) = tr.pop_first() {
        //println!("{}", b);
        res.push(b);
        for (i, p) in vp.iter().enumerate() {
            if v[i] < p.1 {
                let mut new_v = v.clone();
                new_v[i] += 1;
                tr.insert(b * p.0, new_v);
            }
        }
    }
    res
}

获取所有质因数的算法的最好时间复杂度为\(O(\log(n))\) , 当\(n\)为质数时为最坏情况,时间复杂度为\(O(\sqrt n)\).

BTreeMap为B树,查找、插入、删除的时间复杂度都是\(O(\log(n))\)。为什么不用二叉堆,是因为Rust的BinaryHeap允许push重复的内容,导致产生很多重复的因数,使结果错误。

一个数的质因数数量平均有多少个?我用Mathematica粗略算了一下,就当做数量与\(\log(n)\) 成正比吧。以下是Mathematica代码及其结果:

ListPlot[Table[{n, Length[FactorInteger[n]]/Log10[n]}, {n, 2, 10000}]]

最好情况下:

获取所有质因数的算法的时间复杂度为\(O(\log(n))\) ,读取B树第一个值为\(O(\log(n))\),每次读取后,需乘所有可能的质因数(数量约为\(\log(n)\))并加入到B树。那么一共要读取多少次B树的第一个值呢?一个数的因数有多少个,就要读取多少次。一个数的因数的数量级为\(O(n^{\frac{1}{3}})\)。所以时间复杂度粗略估计为\(O(n^{\frac{1}{3}}\log(n))\)。

最坏情况下:

n为质数,时间复杂度为\(O(\sqrt n)\). 无需读取B树的值。

因此可得该算法的总的时间复杂度,最好情况为\(O(n^{\frac{1}{3}}\log(n))\),最坏情况为\(O(\sqrt n)\)

对两个算法进行测试,使用cargo run --release测试代码如下:

fn main() {
    use rand::Rng;
    use std::time::Instant;
    use std::time::Duration;
    let mut rng = rand::thread_rng();
    let test_time = 100;
    let test_num: Vec<u64> = (0..test_time).map(|_| rng.gen::<u64>()).collect();
    //println!("{:?}", test_num);
    let mut time1 = Duration::new(0, 0);
    let mut time2 = Duration::new(0, 0);
    for &i in test_num.iter() {
        println!("{}", i);
        let start_time1 = Instant::now();
        let r1 = get_all_factors(i);
        let end_time1 = Instant::now();
        let t1 = end_time1 - start_time1;
        println!("Time_me : {:?}", t1);
        let start_time2 = Instant::now();
        let r2 = get_all_factors_normal(i);
        let end_time2 = Instant::now();
        let t2 = end_time2 - start_time2;
        println!("Time_nor: {:?}", t2);
        println!("Result_me :{:?}", r1);
        println!("Result_nor:{:?}", r2);
        time1 += t1;
        time2 += t2;
        println!();
    }
    println!("\nResult: \nme :{:?} \nnor:{:?}", time1, time2);
}

最终结果:

计算随机100个u64整数的所有因数用时:

我的算法:507.7144501s
普通算法:699.5194274s

在大部分的情况下,由于对因数有很多拷贝操作,所以我的算法比普通算法慢一点点,但是如果一个数的质因数的次数非常大,比如\(2^{10}11^{5}\),那么我的算法甚至能比普通算法快一千倍。

如果加个素数表,会更快,但作为答题肯定是消耗空间过大的。

当然,这种很容易想到的算法肯定早有人做了,只是我在网上搜索现成的代码搜不到,所以写出来放在这里,如果各位有更好的算法,可以留个链接。本文如有错误,欢迎各位指正。

标签:mut,复杂度,算法,整数,因数,let,sqrt,println,从小到大
From: https://www.cnblogs.com/mariocanfly/p/18052712

相关文章

  • 整数二分算法(自用)
    1.思想对于一个已排序数组,找到一个点,使得数组被分为两部分,即此点左部和右部(点在左部或右部中的一个),比如数组中小于等于某数x的部分与大于的部分;对于整数二分而言两个范围之间是没有空隙的,即左部分的边界x的下一个数一定在右部分。我们可以根据题目选择多种方法二分数组,大类上分......
  • 基于FPGA的9/7整数小波变换和逆变换verilog实现,包含testbench
    1.算法运行效果图预览 将测试结果导入到matlab显示   2.算法运行软件版本vivado2019.2,matlab2022a 3.算法理论概述      小波变换是一种在信号处理中广泛应用的数学工具,它能够提供信号在不同尺度和位置上的信息。在图像处理、数据压缩、噪声消除等领域,小......
  • 基本数据类型【整数类型】【浮点类型】【字符类型】【布尔类型】
    @目录基本数据类型计算机中的二进制表示整数类形浮点类型字符类型布尔类型源码:Giteehttps://gitee.com/drip123456/java-seGIthubhttps://github.com/Drip123456/JavaSE专栏:JavaSE笔记专栏基本数据类型我们的程序中可能需要表示各种各样的数据,比如整数、小数、字符等等,这......
  • 12. 整数转罗马数字(中)
    目录题目题解题目罗马数字包含以下七种字符:I,V,X,L,C,D和M。字符数值I1V5X10L50C100D500M1000例如,罗马数字2写做II,即为两个并列的1。12写做XII,即......
  • day40 动态规划part3 代码随想录算法训练营 343. 整数拆分
    题目:343.整数拆分我的感悟:题目很难,但我动力十足!!理解难点:如何拆分为什么要保留dp[i]听课笔记:代码示例:classSolution:defintegerBreak(self,n:int)->int:#思路:#dp[i]是到目前为止能拆分取的最大值#dp[i]可以拆成j*(集合)......
  • 从右边开始寻找整数的第k位
    从右边开始寻找整数的第k位Implementmatch_k,whichtakesinanintegerkandreturnsafunctionthattakesinavariablexandreturnsTrueifallthedigitsinxthatarekapartarethesame.Forexample,match_k(2)returnsaoneargumentfunctionthattake......
  • vue3 ts用正则表达式校验两位小数和校验整数的方法
    <el-col:span="12"><el-form-itemlabel="贷款金额"prop="loanAmount"><el-input-numberv-model="props.loanAmount":min="0"@change="checkIntegerNumber('loanAmount')"controls......
  • 无符号整数和浮点数的文法
    目录无符号整数的文法浮点数的文法在编写无符号整数(UnsignedInteger)和浮点数(FloatingPointNumber)的文法时,我们通常使用BNF(巴科斯-瑙尔范式)或EBNF(扩展巴科斯-瑙尔范式)等描述形式语言的工具。这些工具提供了一种简洁的方式来定义语法规则。以下是无符号整数和浮点数的一种可能......
  • 整数乘法的长征
    可以点开pdf.问题和计算模型整数乘法无疑是再熟悉不过的问题了,但是出于严谨性,我们先在本文开头明确一下问题以及计算模型.输入为两个非负整数\(a,b\),它们都是\(n\)位的二进制数.我们的目标是计算它们的乘积\(a\cdotb\),也是按顺序输出它的各位数码.原则上说,......
  • 请编写函数fun,它的功能是:求出1到100之内能被7或者11整除, 但不能同时被7和11整除的所有
    /2.请编写函数fun,它的功能是:求出1到100之内能被7或者11整除,但不能同时被7和11整除的所有整数,并将他们放在a所指的数组中,通过n返回这些数的个数/#include<stdio.h>#include<string.h>intfun(int*buf){inti=1,j=0;for(i=1;i<100;i++){if(i%7==......