乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      如何用算法高效尋找素?cái)?shù)?

       華府九五二七 2019-11-15

      預(yù)計(jì)閱讀時(shí)間:5 分鐘

      素?cái)?shù)的定義很簡(jiǎn)單,如果一個(gè)數(shù)如果只能被 1 和它本身整除,那么這個(gè)數(shù)就是素?cái)?shù)。

      不要覺(jué)得素?cái)?shù)的定義簡(jiǎn)單,恐怕沒(méi)多少人真的能把素?cái)?shù)相關(guān)的算法寫(xiě)得高效。本文就主要聊這樣一個(gè)函數(shù):

      // 返回區(qū)間 [2, n) 中有幾個(gè)素?cái)?shù) 
      int countPrimes(int n)

      // 比如 countPrimes(10) 返回 4
      // 因?yàn)?nbsp;2,3,5,7 是素?cái)?shù)

      你會(huì)如何寫(xiě)這個(gè)函數(shù)?當(dāng)然可以這樣寫(xiě):

      int countPrimes(int n) {
          int count = 0;
          for (int i = 2; i < n; i++)
              if (isPrim(i)) count++;
          return count;
      }

      // 判斷整數(shù) n 是否是素?cái)?shù)
      boolean isPrime(int n) {
          for (int i = 2; i < n; i++)
              if (n % i == 0)
                  // 有其他整除因子
                  return false;
          return true;
      }

      這樣寫(xiě)的話時(shí)間復(fù)雜度 O(n^2),問(wèn)題很大。首先你用 isPrime 函數(shù)來(lái)輔助的思路就不夠高效;而且就算你要用 isPrime 函數(shù),這樣實(shí)現(xiàn)也是存在計(jì)算冗余的

      先來(lái)簡(jiǎn)單說(shuō)下如果你要判斷一個(gè)數(shù)是不是素?cái)?shù),應(yīng)該如何寫(xiě)算法。只需稍微修改一下上面的 isPrime 代碼中的 for 循環(huán)條件:

      boolean isPrime(int n) {
          for (int i = 2; i * i <= n; i++)
              ...
      }

      換句話說(shuō),i不需要遍歷到n,而只需要到sqrt(n)即可。為什么呢,我們舉個(gè)例子,假設(shè)n = 12

      12 = 2 × 6
      12 = 3 × 4
      12 = sqrt(12× sqrt(12)
      12 
      4 × 3
      12 = 6 × 2

      可以看到,后兩個(gè)乘積就是前面兩個(gè)反過(guò)來(lái),反轉(zhuǎn)的分界點(diǎn)就在sqrt(n)

      換句話說(shuō),如果在[2,sqrt(n)]這個(gè)區(qū)間之內(nèi)沒(méi)有發(fā)現(xiàn)可整除因子,就可以直接斷定n是素?cái)?shù)了,因?yàn)樵趨^(qū)間[sqrt(n),n]也一定不會(huì)發(fā)現(xiàn)可整除因子。

      這樣,isPrime函數(shù)的時(shí)間復(fù)雜度降為了 O(sqrt(N)),但是我們實(shí)現(xiàn)countPrimes函數(shù)其實(shí)并不需要這個(gè)函數(shù),以上只是希望讀者明白sqrt(n)的含義,因?yàn)榈葧?huì)還會(huì)用到。

      高效實(shí)現(xiàn) countPrimes

      高效解決這個(gè)問(wèn)題的核心思路是和上面的常規(guī)思路反著來(lái):

      首先從 2 開(kāi)始,我們知道 2 是一個(gè)素?cái)?shù),那么 2 × 2 = 4, 3 × 2 = 6, 4 × 2 = 8… 都不可能是素?cái)?shù)了。

      然后我們發(fā)現(xiàn) 3 也是素?cái)?shù),那么 3 × 2 = 6, 3 × 3 = 9, 3 × 4 = 12… 也都不可能是素?cái)?shù)了。

      看到這里,你是否有點(diǎn)明白這個(gè)排除法的邏輯了呢?先看我們的第一版代碼:

      int countPrimes(int n) {
          boolean[] isPrim = new boolean[n];
          // 將數(shù)組都初始化為 true
          Arrays.fill(isPrim, true);

          for (int i = 2; i < n; i++) 
              if (isPrim[i]) 
                  // i 的倍數(shù)不可能是素?cái)?shù)了
                  for (int j = 2 * i; j < n; j += i) 
                          isPrim[j] = false;

          int count = 0;
          for (int i = 2; i < n; i++)
              if (isPrim[i]) count++;

          return count;
      }

      圖片來(lái)自 Wikimedia

      如果上面這段代碼你能夠理解,那么你已經(jīng)掌握了整體思路,但是還有兩個(gè)細(xì)微的地方可以優(yōu)化。

      首先,回想剛才判斷一個(gè)數(shù)是否是素?cái)?shù)的isPrime函數(shù),由于因子的對(duì)稱性,其中的 for 循環(huán)只需要遍歷[2,sqrt(n)]就夠了。這里也是類似的,我們外層的 for 循環(huán)也只需要遍歷到sqrt(n)

      for (int i = 2; i * i < n; i++) 
          if (isPrim[i]) 
              ...

      除此之外,很難注意到內(nèi)層的 for 循環(huán)也可以優(yōu)化。我們之前的做法是:

      for (int j = 2 * i; j < n; j += i) 
          isPrim[j] = false;

      這樣可以把i的整數(shù)倍都標(biāo)記為false,但是仍然存在計(jì)算冗余。

      比如i = 4時(shí)算法會(huì)標(biāo)記 4 × 2 = 8,4 × 3 = 12 等等數(shù)字,但是 8 和 12 已經(jīng)被i = 2i = 3的 2 × 4 和 3 × 4 標(biāo)記過(guò)了。

      我們可以稍微優(yōu)化一下,讓ji的平方開(kāi)始遍歷,而不是從2 * i開(kāi)始:

      for (int j = i * i; j < n; j += i) 
          isPrim[j] = false;

      這樣,素?cái)?shù)計(jì)數(shù)的算法就高效實(shí)現(xiàn)了。其實(shí)這個(gè)算法有一個(gè)名字,叫做 Sieve of Eratosthenes??聪峦暾淖罱K代碼:

      int countPrimes(int n) {
          boolean[] isPrim = new boolean[n];
          Arrays.fill(isPrim, true);
          for (int i = 2; i * i < n; i++) 
              if (isPrim[i]) 
                  for (int j = i * i; j < n; j += i) 
                      isPrim[j] = false;

          int count = 0;
          for (int i = 2; i < n; i++)
              if (isPrim[i]) count++;

          return count;
      }

      該算法的時(shí)間復(fù)雜度比較難算,顯然時(shí)間跟這個(gè)嵌套 for 循環(huán)有關(guān),其操作數(shù)應(yīng)該是:

         n/2 + n/3 + n/5 + n/7 + …
      = n × (1/2 + 1/3 + 1/5 + 1/7…)

      括號(hào)中是素?cái)?shù)的倒數(shù)和。其最終結(jié)果是 O(N * loglogN),有興趣的讀者可以查一下該算法的時(shí)間復(fù)雜度證明。

      以上就是素?cái)?shù)算法相關(guān)的全部?jī)?nèi)容。怎么樣,是不是看似簡(jiǎn)單的問(wèn)題卻有不少細(xì)節(jié)可以打磨呀?

      歷史文章:

      動(dòng)態(tài)規(guī)劃之 KMP 算法詳解

      這個(gè)問(wèn)題不簡(jiǎn)單:尋找缺失元素

      如何高效對(duì)有序數(shù)組/鏈表去重?

      點(diǎn)擊這里進(jìn)入留言板

      反向思考方能出其不意!

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多