Published on

找質數思路與算法

前言

現在解題好像也越來越競爭了,變成都要事先練過,然後比輸出速度,少了些慢慢思考凝聚想法的機會,只好來寫筆記。

思路與答案

題目就是給一個正整數 N,找出 1~N 中間有幾個質數。

一開始無腦算法就是從 1~N 每個數都除以從 N 到 1 的數,然後開始思考優化,基礎就是從 2 開始找,因為 1 跟 N 都可以整除 N,所以先去掉 2、3、5、7 這些常見的質數的倍數,因為質數 * 任意值必然不會是質數, 優化判斷一個數是否為質數?迴圈上限的部分:如果 number 有一個大於根號 number 的因數,則必然存在一個比根號 number 小的因數,所以在大於根號 number 的部分搜尋無意義可以省略。

const isPrime = (number) => {
  if (number < 2) return false
  for (let i = 2; i <= Math.sqrt(number); i++) {
    if (number % i === 0) {
      return false
    }
  }
  return true
}

const countPrimesInRange = (N) => {
  const primesCount = Array.from({ length: N }, (_, index) => index + 1)
    .slice(1) // Skip 1, as 1 is not a prime number
    .reduce((count, currentNumber) => {
      if (isPrime(currentNumber)) {
        return count + 1
      }
      return count
    }, 0)

  return primesCount
}

const result = countPrimesInRange(10)
console.log(result) // 4

再優化的方式為埃拉托斯特尼篩法,簡稱埃氏篩,先建立一個 1~N 的陣列,將所有當前找到的質數的倍數標記為合數,合數也必然不是質數,令找到的質數為 p,標記時可直接從 p^2 開始標記,因為如果存在比 p^2 更小的 p * m 則 m 必然小於 p,所以在 m 或 m 的因數標記時已經標記,最後 filter 標記完的結果則可以得到解答。

const sieveOfEratosthenes = (N) => {
  const isPrime = Array.from({ length: N + 1 }, (_, index) => index > 1)

  for (let i = 2; i <= Math.sqrt(N); i++) {
    if (isPrime[i]) {
      for (let j = i * i; j <= N; j += i) {
        isPrime[j] = false
      }
    }
  }

  const primesCount = isPrime.slice(2).filter((value) => value).length

  return primesCount
}

const result = sieveOfEratosthenes(10)
console.log(result) // 4

參考資料:

維基百科-埃拉托斯特尼篩法