RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1551803
Accepted
Quester
Quester
Asked:2023-11-17 17:55:57 +0000 UTC2023-11-17 17:55:57 +0000 UTC 2023-11-17 17:55:57 +0000 UTC

如何计算算法的复杂度?

  • 772

我还在研究递归

任务:判断N是否为素数。

条件:仅使用递归。

我的解决方案(在评论中解释我的逻辑):

package main

func main() {
    rec(890, 1, 0) //Output: NO
    println()
}

// функция имеет допущение, что n>1(т.е рассчитывает на здравый смысл вызывающего)
// для n<=1 результат будет неправильным(YES).
func rec(n, d, c int) {
    // если делитель дошел до делимого,
    //то очевидно, что если они равны то делятся без остатка и
    //делимость проверять не нужно. Просто выходим из функции.
    //этот случай означает, что от d>1 до n делителей не найдено.
    //значит n делится только на себя и на 1
    if d > n {
        print("YES")
        return
    }
    //поиск делителя для n в диапазоне от 2 до n
    if n%d == 0 && d > 1 {
        //увеличиваем счетчик делителей
        c++
        //если d еще не дошел до n, а уже нашел делитель(c>0),
        //то число точно составное, дальнейшие проверки не имеют смысла
        //говорим что число составное и выходим из функции.
        if c > 1 || (n%2 == 0 && n > 2) {
            print("NO")
            return
        }
    n=int(math.Sqrt(float64(n))) //так код продолжает работать, но делает это быстрее, 
    //хоть и нет гарантии что правильно рассчитывает. Но пока что ни разу не ошибся
    }
    //рекурсивно проверяем является ли d+1 делителем n
    //рекурсивный вызов будет происходит до тех пор,
    //пока c<1 || (d<n && n%d!=0 && (n>2 && n%2!=0))
    rec(n, d+1, c)
}

问题1:是否可以不用那么多如果(据我能想到)?

Question2:如何计算这个算法的复杂度?如何使其非线性

问题3:如果数字太大,例如1284762193641112317堆栈溢出。问题是什么?我的猜测是递归调用太多了。但如何解决这个问题呢?

更新:

在代码中添加了一个片段

n = int(math.Sqrt(float64(n)))

并且代码继续有效。我在寻找一种减少递归调用次数的方法时想到了这个,并且通过这样的片段,在每次调用中 n 都减少到其根。

golang
  • 1 1 个回答
  • 66 Views

1 个回答

  • Voted
  1. Best Answer
    Stanislav Volodarskiy
    2023-11-17T23:53:44Z2023-11-17T23:53:44Z
    1. 你可以让你的代码更加传统、标准。

    2. 代码复杂度是线性的。如果n不是素数,则平方根线会有所帮助。否则,你需要进行大约n次递归调用。因此,复杂性与n成正比。

    3. 递归调用的次数受堆栈大小的限制。如果丢失,程序会因错误而崩溃。在我的机器上,为堆栈分配了 1 GB。简单的大约处理1000万个,1亿之后堆栈溢出。

    如果数字n恰好有两个约数:1 和它本身,那么它就是素数。如果该数字是合数,则其形式为n = pq。令p ≤ q,则p不超过n的平方根。因此,仅检查√n以内的除数就足够了。

    在下面的代码中, d ≤ √n的检查替换为d ≤ n / d。第二个表达式避免了根提取和溢出,这在检查d * d ≤ n的情况下是可能的。

    为了减少堆栈深度,仅尝试两个奇数作为除数。堆栈深度约为√n/2。

    package main
    
    import (
        "fmt"
    )
    
    func isPrimeIter(n, d uint) bool {
        if d > n / d {
            return true
        }
        if n % d == 0 {
            return false
        }
        return isPrimeIter(n, d + 2)
    }
    
    func isPrime(n uint) bool {
        if n < 2 {
            return false
        }
        if n == 2 {
            return true
        }
        return n % 2 != 0 && isPrimeIter(n, 3)
    }
    
    func main() {
        var n uint
        fmt.Scan(&n)
        println(isPrime(n))
    }
    
    $ time echo 1000000000000037 | go run is-prime.go
    true
    
    real  0m3.341s
    user  0m3.104s
    sys   0m0.312s
    

    有限的堆栈内存不允许处理整个数字范围uint。我还没有查出Go是否支持尾递归优化。有人说不是,有人说是。也许支持已经出现,但我有一个旧的编译器。如果你理解这个问题,请写一个评论 - 我的编译器版本go version go1.6.2 linux/amd64。无论如何,isPrimeIter它是以一种允许优化的风格编写的——递归调用是函数体中的最后一个,但堆栈仍然溢出。

    在不依赖编译器的情况下,您可以通过用树递归替换线性递归来减少所需的堆栈深度。在这种情况下,堆栈深度将减少到对数。

    该函数isPrimeIter(n, d1, d2 uint) bool检查n是否不能被半区间[d 1 , d 2 )中的任何约数整除。仅检查不超过√n 的奇数约数。如果区间 ( d 1 + 2 = d 2 )中只有一个奇数,则执行整除性测试。否则,该间隔大约被分成两半,并进行两次递归调用isPrimeIter。容易证明递归深度不超过log 2 (d 2 - d 1 )。的最大对数值uint为64。

    package main
    
    import (
        "fmt"
    )
    
    func ceilOdd(n uint) uint {
        return n + 1 - n % 2
    }
    
    func isPrimeIter(n, d1, d2 uint) bool {
        if d1 > n / d1 {
            return true
        }
        if d1 + 2 == d2 {
            return n % d1 != 0
        }
        d := ceilOdd((d1 + d2) / 2)
        return isPrimeIter(n, d1, d) && isPrimeIter(n, d, d2)
    }
    
    func isPrime(n uint) bool {
        if n < 2 {
            return false
        }
        if n == 2 {
            return true
        }
        return n % 2 != 0 && isPrimeIter(n, 3, ceilOdd(n / 2 + 2))
    }
    
    func main() {
        var n uint
        fmt.Scan(&n)
        println(isPrime(n))
    }
    
    $ time echo 1000000000000037 | go run is-prime.go
    true
    
    real  0m0.687s
    user  0m0.756s
    sys   0m0.004s
    

    该程序最难的数字是素数和素数平方。在这两种情况下,您都可以在一分钟内完成。

    最大质数< 2 64:

    $ time echo 18446744073709551557 | go run is-prime.go
    true
    
    real  0m59.858s
    user  0m59.144s
    sys   0m0.076s
    

    素数的最大平方< 2 64 ( 18446744030759878681 = 4294967291 2 ):

    $ time echo 18446744030759878681 | go run is-prime.go
    false
    
    real  0m59.607s
    user  0m58.996s
    sys   0m0.064s
    
    • 3

相关问题

  • windows上的protoc编译错误

  • 递归打印包依赖

  • Golang 算法 XTEA ECB 库“golang.org/x/crypto/xtea”

  • 如何将 IMEI 转换为字节并返回 golang

  • 如何创建文件并将其移动到新目录?

  • go中的函数参数中是否有cv-qualifier的类似物?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5