开发者问题收集

测试素数程序

2019-10-04
312

我正在尝试用 JS 编写一个程序,该程序接受输入并使用递归测试它是否为素数。

在我的代码中,我创建了函数 isPrime 。作为我的“基础”,如果 x==1 ,则返回 false ,如果 x==2 ,则返回 true ,因为 2 是第一个素数。

之后,我有一个 if 语句,用于测试 x 是否为素数。
但是,当我执行代码时,我的控制台返回 Uncaught RangeError: Maximum call stack size reached
我不确定程序返回错误代码的具体原因。

let x = prompt("Enter a number to test as a prime number: ");
let result = isPrime(x, 2);

if (result) {
  alert("x is prime");
} else {
  alert("x is not prime");
}

function isPrime(number, divisor) {
  if (number == 1) {
    return false;
  } else if (number == 2) {
    return true;
  } else {
    return isPrime(number, ++divisor);
  }

  if (number % divisor == 0) {
    return false;
  } else if (divisor ** 2 >= number) {
    return true;
  } else {
    return isPrime(number, ++divisor);
  }

}
3个回答

当前代码存在问题

您的函数存在两个实际问题。首先,您有一堆无法访问的代码:

function isPrime(...) {
  if (...) {
    return false
  } else if (...) {
    return true
  } else {
    return isPrime(...)
  }

  // Everything from here down cannot be reached.  You already returned from this
  // function in one branch or another in the block above.

}

其次,尽管您确实正确地包含了基本情况,但您从未朝着它们前进:

function isPrime(number, divisor) {
  if (number == 1) {                    // Base case, testing on `number`
    return false;
  } else if (number == 2) {             // Base case, testing on `number`
    return true;
  } else {
    return isPrime(number, ++divisor);  // Recursive case, doesn't change `number`
  }

  // Even if you could reach this section, it has the same problem.
}

要使递归起作用,您的递归情况必须以某种方式朝着基本情况前进。进展可能很复杂,但如果您无法证明每个连续调用都以某种方式更接近基本情况,那么您甚至不知道您的程序是否可以完成,更不用说它是否正确了。

为什么选择递归?

但我有一个更基本的问题。您为什么要在这里使用递归?这对您来说是一项学习练习,是家庭作业还是您自己的个人学习?如果是这样,那很好。这是一个完全合法的学习问题。

但递归和迭代同样强大。很容易证明,任何你能用一个完成的事情,你都可以用另一个完成。因此,选择通常归结为你的数据结构是否更适合递归或迭代。对于许多结构来说,递归显然是最佳选择。例如,如果你正在处理树结构,递归几乎总是更干净、更优雅。(性能是一个单独的问题,将涉及尾部递归之类的问题。)如果你正在处理完整的项目列表,递归或迭代可能更好。

但这里的问题是一个寻找任何除数的问题,并在第一个除数处停止。您可以使用简单的迭代来实现这一点: 它能被 2 整除吗? 它能被 3 整除吗? 它能被 4 整除吗? ,...,在第一次 时停止。递归并没有增加任何使它特别清晰的东西。

(请注意,一个重要的性能改进是,只要我们通过目标值的平方根,就立即停止,因为如果有因数,其中一个必须小于平方根。)

递归解决方案

话虽如此,我们当然 可以 使用递归来实现这一点。我可能会这样写:

const isPrime = (n, d = 2) =>
  d * d > n
    ? true
  : n % d == 0
    ? false
  : isPrime (n, d + 1)

console .log (isPrime (40)) //=> false
console .log (isPrime (41)) //=> true

或者,按照您当前代码的样式,

function isPrime(n, d = 2) {
  if (d * d > n) {
    return true;
  } else  if (n % d == 0) {
    return false;
  } else {
    return isPrime(n , d + 1)
  }
}

并且,虽然我们 也可以 将其写为

const isPrime = (n, d = 2) =>
  d * d > n || (n % d !== 0 && isPrime (n, d + 1))

我认为这使理解递归变得更加困难。

一个更好的递归练习问题

最后,如果这是为了帮助您学习递归而设计的,那么让我建议一个相关问题,其中递归更可能合适。编写一个函数来查找 n 的所有素因数:

primeFactors(17)    //=> [17]
primeFactors(18)    //=> [2, 3, 3]
primeFactors(19)    //=> [19]
primeFactors(20)    //=> [2, 2, 5]

primeFactors(55440) //=> [2, 2, 2, 2, 3, 3, 5, 7, 11]

同样,这可以迭代或递归地完成,但我认为递归解决方案可能是更好的代码。

Scott Sauyet
2019-10-05

此处的逻辑代码用于检查数字是否为素数

let x = prompt("Enter a number to test as a prime number: ");
    let result = isPrime(x);


    if (result) {
      alert("x is prime");
    } else {
      alert("x is not prime");
    }


function isPrime(n)
{

  if (n===1)
  {
    return false;
  }
  else if(n === 2)
  {
    return true;
  }
  else
  {
    for(var x = 2; x < n; x++)
    {
      if(n % x === 0)
      {
        return false;
      }
    }
    return true;  
  }
} 

在您的代码中

if (number == 1) {
    return false;
  } else if (number == 2) {
    return true;
  } else {
    return isPrime(number, ++divisor); // this is infinite recursion acurring 
  }
Ravi Makwana
2019-10-04

要了解原因,一个好的做法是记录传递给函数的参数并限制递归。尝试使用较小的 x (例如 10),然后思考发生了什么。

var iterations = 20

let x = prompt("Enter a number to test as a prime number: ");
let result = isPrime(x, 2);

if (result) {
  alert("x is prime");
} else {
  alert("x is not prime");
}

function isPrime(number, divisor) {
  console.log(JSON.stringify(Array.from(arguments)));
  // stop infinite recursion
  if (iterations-- < 0)
    return;

  if (number == 1) {
    return false;
  } else if (number == 2) {
    return true;
  } else {
    return isPrime(number, ++divisor);
  }

  if (number % divisor == 0) {
    return false;
  } else if (divisor ** 2 >= number) {
    return true;
  } else {
    return isPrime(number, ++divisor);
  }

}
גלעד ברקן
2019-10-04