【问题标题】:Checking whether a number is prime in Scala在Scala中检查一个数字是否是素数
【发布时间】:2016-08-21 07:05:33
【问题描述】:

我想检查一个数字是否是素数。我写了以下代码,但它没有返回任何值:

 def isprime(x:Int) = {
     | if (x==1) false
     | else {
     | for (i <- 2 to x-1) {
     | if (x % i == 0) false
     | else true
     | }
     | }
     | }

【问题讨论】:

  • 你在哪里运行它?你是如何运行它的?
  • 我正在使用 Scala REPL
  • @AlirezaIzadimehr 我阅读了那个链接,但我需要使用简单的 if-else 语句来完成它
  • 我认为答案虽然没有错,但并没有真正解决问题。 Amab,问题在于您的 for 循环没有产生任何值 - 您计算了一个真/假值,但随后不做任何事情。所以你的 else 子句不会产生任何值(事实上,它会产生Unit)。一旦找到除数,您就需要返回 false - 这样做的方法可能是 @eliasah 的答案中的exists

标签: scala


【解决方案1】:

您所做的是调用定义一个函数,因此显然它不会返回任何内容,事实上,该函数返回 AnyVal,这显然对您没有多大帮助。我怀疑您实际上需要返回一个布尔类型。

由于您使用的是 REPL,因此您需要定义您的函数来检查一个数字是否为素数。我称它为isPrime2,然后对其进行测试。

def isPrime2(i :Int) : Boolean = {
|     if (i <= 1)
|       false
|     else if (i == 2)
|       true
|     else
|       !(2 to (i-1)).exists(x => i % x == 0)
|   }
// isPrime2: (i: Int)Boolean

(1 to 10).foreach(i => if (isPrime2(i)) println("%d is prime.".format(i)))
// 2 is prime.
// 3 is prime.
// 5 is prime.
// 7 is prime.

如果您不在乎使用 if else 条件,我什至会建议一个更简单的版本:

def isPrime1(n: Int): Boolean = ! ((2 until n-1) exists (n % _ == 0))

这也返回一个布尔值。

编辑:

正如@TheArchetypalPaul 所说,这也是暗示的,问题是你的 for 循环没有产生任何值 - 你计算一个真/假值,但然后不做任何事情。所以你的 else 子句不会产生任何值(事实上,它会产生Unit)。您需要在找到除数后立即返回 false - 这样做的方法可能存在于 isPrime1 中。

【讨论】:

  • 适用于大数的进一步细化:def isPrime(n: Long): Boolean = !(2 +: (3 to Math.sqrt(n).toInt by 2) exists (n%_ == 0))
  • 正如@jwvh 的评论中所示,检查高于n 平方根的数字是没有意义的。跳过偶数也很好:如果你已经检查了2,那么检查它们没有意义。
  • @jwvh 刚刚注意到您的解决方案将为1 以及一些负数返回true。您需要对此进行明确检查。
  • @Sim,正确的。我的解决方案是作为建议的“更简单版本”的性能增强提供的。原版的边缘情况缺陷没有得到解决。
  • 我不了解 Amab,但对于我的应用程序,我需要 isPrime(-47)(例如)返回 true。当然,它只需要稍作调整。
【解决方案2】:

单线解决方案

def isPrime(num:Int):Boolean =
(num > 1) && !(2 to scala.math.sqrt(num).toInt).exists(x => num % x == 0)

【讨论】:

  • 在第二行中,我找到了从 1 到 num/2 的任意数字,除以给定的数字。如果是,则表示它不是素数。例如,10 -> 它将检查 2 到 3 并且 2 将除以 10,因此这不是素数。我只是把结果的否定带到了第二个。
【解决方案3】:

这个没有一个衬垫那么优雅,但速度更快。它使用了所有素数(除了 2 和 3)都必须在 6k±1 的事实。因此它会跳过可被 2 或 3 整除的测试数字。它只会测试 (5, 7)、(11, 13)、(17, 19) 等组。

def isPrime(number: Int): Boolean =
  if (number < 4) number > 1
  else if (number % 2 == 0 || number % 3 == 0) false
  else (5 to math.sqrt(number).toInt by 6).forall(i => number % i != 0 && number % (i + 2) != 0)

【讨论】:

  • 这很聪明。 Tysvm 用于发布。我刚刚更新了我的答案,表明你的答案比我的更有效。我还提供了对Long 的转换,因为这正是我所需要的。将Int 实现转换为Long 实现很容易出错。如果您还提供正确实施的Long 版本,我很乐意删除我的转换并指出您的答案。
【解决方案4】:

这里还有一个 1 行解决方案:

def isPrime(n: Int) = Range(2, n-1).filter(i => n % i == 0).length == 0 

甚至更短:

def isPrime(n: Int) = Range(2, n-1).filter(n % _ == 0).length == 0

【讨论】:

  • 当然,def isPrime(n: Int) = Range(2, n-1).exists(n % _ == 0)
【解决方案5】:

这是另一个班轮:

def isPrime(num: Int): Boolean = (2 to num) forall (x => num % x != 0)

forall 将检查谓词是否适用于该范围内的所有元素

我刚刚意识到上面的代码对于更大的数字有点慢,所以这里有一个改进的版本:

def isPrime(n: Int): Boolean = (2 to math.sqrt(n).toInt) forall (x => n % x != 0)

【讨论】:

  • 这些函数返回 true 表示 0 和 1 不是素数。
【解决方案6】:

这可能是一个解决方案。

def isPrime(integer: Int): Boolean = {
   if (integer == 1) false
   else {
      val domain = (2 to math.sqrt(integer).toInt).view
      val range = domain filter (isDivisibleBy(integer, _))
      range.isEmpty
   }
}

def isDivisibleBy(integer: Int, divisor: Int): Boolean = integer % divisor == 0

现在回到您编写的代码,您的代码返回 AnyVal 并且所需的返回类型应该是 Boolean。而这背后的原因在于 Scala(或任何函数式语言)中的 for 循环是 表达式 而不是 控制结构

【讨论】:

    【解决方案7】:

    评论前一个答案(@jwvh 除外):没有必要多次使用偶数。经过2的测试,必须使用域名3 to math.sqrt(integer).toInt by 2

    我开发了一个加速尾递归版本。它会跳过偶数值,并一次测试两个值。

    亲自查看,在浏览器中运行Scastie (remote JVM)

    import java.lang.System.currentTimeMillis
    
    import scala.annotation.tailrec
    import scala.collection.immutable.TreeSet
    import scala.io.Source
    
    // Primality by trial division
    
    object PrimesTestery extends App {
      val oeisPrimes = TreeSet(oeisPrimesText.map(_.toInt): _*)
    
      def oeisPrimesText = rawText.getLines.takeWhile(_.nonEmpty).map(_.split(" ")(1)).toList
    
      def rawText = Source.fromURL("https://oeis.org/A000040/a000040.txt")
    
      def isPrime(n: Long) = {
        val end = math.sqrt(n.toDouble).toInt
    
        @tailrec
        def inner(d: Int): Boolean = {
          (d > end) || (n % d != 0 && n % (d + 2) != 0) && inner(d + 6)
        }
    
        n > 1 && ((n & 1) != 0 || n == 2) && (n % 3 != 0 || n == 3) && inner(5)
      }
    
      println(s"Found ${oeisPrimes.size} primes on OEIS , last is ${oeisPrimes.last}.")
      for (i <- (0 to oeisPrimes.last))
        assert(isPrime(i.toLong) == oeisPrimes.contains(i), s"Wrong $i")
    
      println(s"✔ Successfully completed without errors. [total ${currentTimeMillis - executionStart} ms]")
    
    }
    

    可以肯定的是,此代码针对素数和合数的已知素数列表进行测试。

    【讨论】:

      【解决方案8】:

      此问题的答案中的许多解决方案似乎都不起作用,没有涵盖明显的边缘情况,和/或明显效率低下。

      以下是我的回答,它有效,涵盖所有边缘情况,并且在这个特定领域中尽可能高效;即它检查尽可能少的值,如果数字不是素数,它会尽早失败。

      def isPrime(long: Long): Boolean =
        if (long > 8L) {
          !(((long % 2L) == 0L)
            || (3L to math.sqrt(long).toLong by 2L).exists(n => (long % n) == 0L))
        } else
          (long > 1L) && ((long == 2L) || ((long % 2L) != 0L))
      

      更新(2019 年 9 月 18 日): 我对我的解决方案“在这个特定领域尽可能高效”表示纠正。 solution provided by @kanbagoly 实际上更有效,因为它的检查更少。他是使用Int 实现的。这是他使用Long 重新实现的解决方案,以防万一您只想进行复制/粘贴,而不是遇到愚蠢的IntLong 问题:

      def isPrime(long: Long): Boolean =
        if (long < 4L)
          long > 1L
        else if (((long % 2L) == 0L) || ((long % 3L) == 0L))
          false
        else
          (5L to math.sqrt(long).toLong by 6L).forall(n => ((long % n) != 0L) && ((long % (n + 2L)) != 0L))
      

      【讨论】:

      • 我在某处读到(long &amp; 1) == 0 可能具有效率优势。
      • 在 JVM 的早期(2005 年之前)曾经需要这种技术。但是,Java 编译器和 HotSpot 编译器现在都具有针对所有基本位操作的显式转换/映射,非常类似于您所描述的(以及更多)。
      【解决方案9】:
      def isPrime(n: Int) = (2 until n) forall(x => n % x !=0)
      

      【讨论】:

      • 欢迎来到 StackOverflow!请编辑您的帖子以包含对您的代码的解释。这将使您的答案更有用,更有可能被点赞
      【解决方案10】:

      你也可以使用这个递归函数:

      def isPrime(n: Int):Boolean = {
        def isPrimeUntil(t: Int): Boolean =
          if (t<=1) true
          else n%t != 0 && isPrimeUntil(t-1)
      
          isPrimeUntil(n/2)
      }
      

      【讨论】:

        【解决方案11】:

        这也适用于负数和大数。 质数从 2、3、5、...开始

        def isPrime(n: Int): Boolean = (n > 1) && ! Range(2, n-1).exists(n % _ == 0)
        

        还有

        @tailrec
        def isPrime(n: Int): Boolean = {
           def isPrimeTailrec(divisor: Int): Boolean = {
             if(divisor > Math.sqrt(Math.abs(n))) true
               else n % divisor != 0 && isPrimeTailrec(divisor + 1)
             }
             if(n < 2) false
             else isPrimeTailrec(2)
           }
        }
        

        【讨论】:

          【解决方案12】:

          def primeNumber(range: Int): Unit ={

          val primeNumbers: immutable.IndexedSeq[AnyVal] =
          
            for (number :Int <- 2 to range) yield{
          
              val isPrime = !Range(2, Math.sqrt(number).toInt).exists(x => number % x == 0)
          
              if(isPrime)  number
            }
          
          for(prime <- primeNumbers) println(prime)
          

          }

          【讨论】:

          • 这并不能直接回答问题,并且显示出非常糟糕的 Scala 风格。您是否阅读过(一年多前)已经发布的任何其他答案?
          • 实际上我从今天开始学习 Scala,这是我编写的第一个程序,所以我认为它可能对初学者有所帮助。谢谢回复
          • 当一个问题这么老,并且有许多其他答案时,您应该解释为什么要发布一个新的和不同的答案。在这种情况下,您的 isPrime 计算与其他发布的答案没有显着差异,并且您的其余代码为初学者树立了一个坏榜样。
          猜你喜欢
          • 2015-09-15
          • 1970-01-01
          • 1970-01-01
          • 2011-05-06
          • 1970-01-01
          • 2022-01-17
          • 2017-09-03
          • 1970-01-01
          相关资源
          最近更新 更多