<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          重溫斐波那契數(shù)列,再看時(shí)間復(fù)雜度的重要性

          共 5807字,需瀏覽 12分鐘

           ·

          2023-08-22 19:48

          • ? 開題引入斐波那契

            • ? 代碼演示:遞歸、循環(huán)

          • ? 遞歸 vs 循環(huán)

            • ? 時(shí)間復(fù)雜復(fù)高,指數(shù)型O(2^n);推導(dǎo)過程

            • ? 占用線程堆棧, 可能導(dǎo)致棧滿異常

          • ? 壓測(cè)演示

            ?

          打入門軟件開發(fā),斐波那契數(shù)列便是繞不過去的簡(jiǎn)單編程算法。

          一個(gè)老生常談的思路是遞歸,另外是循環(huán),今天借此機(jī)會(huì)回顧并演示時(shí)間復(fù)雜度在編程中的重要性。

          斐波那契 遞歸算法 1,1,2,3,5,8,13,21,34,55

          遞歸算法的應(yīng)用場(chǎng)景是:

          • ? 將大規(guī)模問題,拆解成幾個(gè)小規(guī)模的同樣問題

          • ? 拆解具備終止場(chǎng)景

          func Fibonacci(n int) (r int) {
              if n == 1 || n == 2 {
                  r = 1
                  return
              } else {
                  return Fibonacci(n-1) + Fibonacci(n-2)
              }
          }

          為什么能想到循環(huán), 斐波那契數(shù)組也有循環(huán)的含義:
          第n個(gè)數(shù)字是循環(huán)指針i從第1個(gè)數(shù)字移動(dòng)到第n-2個(gè)數(shù)字時(shí), 第n-2個(gè)數(shù)字pre和第n-1個(gè)數(shù)字next的和。

          func Fibonacci2(n int) (r int) {
             if n==1 || n==2  {
               return 1
             }
             pre,next int :=1,1
             for i:=0; i<=n-1-2; i++ {
                 tmp:= pre+next
                 pre = next
                 next = tmp
             }
          }

          單元測(cè)試如下:

          func TestFibonacci(t *testing.T) {
              t.Logf("Fibonacci(1) = %d, Fibonacci2(1)= %d ", Fibonacci(1), Fibonacci2(1))
              t.Logf("Fibonacci(3) = %d, Fibonacci2(3)= %d ", Fibonacci(3), Fibonacci2(3))
              t.Logf("Fibonacci(8) = %d, Fibonacci2(8)= %d ", Fibonacci(8), Fibonacci2(8))
          }

          go test ./ -v
          === RUN   TestFibonacci
              m_test.go:8: Fibonacci(1) = 1, Fibonacci2(1)= 1 
              m_test.go:9: Fibonacci(3) = 2, Fibonacci2(3)= 2 
              m_test.go:10: Fibonacci(8) = 21, Fibonacci2(8)= 21 
          --- PASS: TestFibonacci (0.00s)
          PASS
          ok      example.github.com/test 3.359s

          遞歸的問題在于:

          (1) 函數(shù)調(diào)用存在壓棧過程,會(huì)在線程棧(一般2M)上留下棧幀,斐波那契遞歸算法:是函數(shù)自己調(diào)用自己,在終止條件后棧幀開始收斂,但是在此之前有可能已經(jīng)撐爆線程棧。

          棧幀中維持著函數(shù)調(diào)用所需要的各種信息,包括函數(shù)的入?yún)ⅰ⒑瘮?shù)的局部變量、函數(shù)執(zhí)行完成后下一步要執(zhí)行的指令地址、寄存器信息等。

          (2) 斐波那契遞歸調(diào)用存在重復(fù)計(jì)算,時(shí)間復(fù)雜度是O(2^n),隨著n的增加,需要計(jì)算的次數(shù)陡然增大(業(yè)內(nèi)稱為指數(shù)型變化)。

           f(n) = f(n-1)+f(n-2)                 // 1次計(jì)算
                = f(n-2)+f(n-3)+f(n-3)+f(n-4)   // 3次計(jì)算
                = f(n-3)+f(n-4)+f(n-4)+f(n-5)+f(n-4)+f(n-5)+f(n-5)+f(n-6)     // 7次計(jì)算                          // 7次計(jì)算
                =......
                = f(1)+......                   //  2^n-1次計(jì)算
                
           故為斐波那契遞歸時(shí)間復(fù)雜度為 O(2^n)     

          而我們的循環(huán)算法不存在以上問題, 第n個(gè)數(shù)字需要n -2次計(jì)算, 時(shí)間復(fù)雜度是O(n)

          有些童鞋可能沒意識(shí)到指數(shù)型的威力,舉個(gè)例子, 斐波那契遞歸算法,第20個(gè)數(shù)字需要2^20次運(yùn)算;循環(huán)算法只要18次運(yùn)算。

          使用基準(zhǔn)測(cè)試壓測(cè):

          func BenchmarkF1(b *testing.B) {
              for i := 1; i < b.N; i++ {
                  Fibonacci(20) //  時(shí)間復(fù)雜度  O(2^n)
              }
          }

          func BenchmarkF2(b *testing.B) {
              for i := 1; i < b.N; i++ {
                  Fibonacci2(20) // 時(shí)間復(fù)雜度 O(n)
              }
          }


          go test  -bench=.  -benchmem  ./
          goos: darwin
          goarch: amd64
          pkg: example.github.com/test
          cpu: Intel(R) Core(TM) i5-8257U CPU @ 1.40GHz
          BenchmarkF1-8              55039             20740 ns/op               0 B/op          0 allocs/op
          BenchmarkF2-8           196663548                6.080 ns/op           0 B/op          0 allocs/op
          PASS
          ok      example.github.com/test 3.744s

          單次執(zhí)行效率(ns/op指標(biāo))相形見絀,甚至斐波那契遞歸n>50+ 不一定能計(jì)算出來。


          ok, that'all.  本次快速記錄了遞歸算法相比循環(huán)的兩點(diǎn)劣勢(shì),這里面很重要的常見時(shí)間復(fù)雜度變化曲線[1], 需要程序員必知必會(huì)。https://adrianmejia.com/most-popular-algorithms-time-complexity-every-programmer-should-know-free-online-tutorial-course/

          全文原創(chuàng),見字如面,若有不同見解或發(fā)散思維,文末可留言或直接微信噴我。

          引用鏈接

          [1] 常見時(shí)間復(fù)雜度變化曲線: https://adrianmejia.com/most-popular-algorithms-time-complexity-every-programmer-should-know-free-online-tutorial-course/


          自古以來,同步/異步都是八股文第一章

          自古以來,反射也是兵家必爭(zhēng)之地

          Go編程快閃之logrus日志庫

          流量調(diào)度、微服務(wù)可尋址性和注冊(cè)中心

          摸魚快報(bào):golang net/http中的雕蟲小技

          Go語言正/反向代理的姿勢(shì)

          兩將軍問題和TCP三次握手

          點(diǎn)“戳“在看?

          瀏覽 250
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费黄色一级视频 | 豆花官网进入免费操逼 | 久久久精品少妇 | 操新娘网 | 大香蕉综合 |