你在一个功能中做了很多事情。如果将代码分解为两个函数,一个用于创建素数列表,另一个用于测试特定数字是否为素数,则代码可能更容易理解:
function listPrimes( nPrimes ) {
var primes = [];
for( var n = 2; nPrimes > 0; n++ ) {
if( isPrime(n) ) {
primes.push( n );
--nPrimes;
}
}
return primes;
}
function isPrime( n ) {
var max = Math.sqrt(n);
for( var i = 2; i <= max; i++ ) {
if( n % i === 0 )
return false;
}
return true;
}
现在您可以在 Node 中运行它:
var fs = require('fs');
fs.writeFileSync( 'test.txt', listPrimes(100) );
或直接在浏览器控制台中:
listPrimes( 100 );
(我没有在 Node 中测试代码,只在浏览器中测试。)
一些相关的注释:
- 计算
sqrt()
被移到 in 的循环之外isPrime()
,因此不必为您正在测试的每个数字重新计算。
- 该
nPrimes
变量可让您生成所需的确切数量的素数,而无需542
hack。
编写了这个简单的版本后,看看可能的优化是很有趣的。一种是只检查先前生成的素数的可分性,而不是检查直到平方根的所有整数。你可以这样做:
function listPrimes( nPrimes ) {
var primes = [];
for( var n = 2; nPrimes > 0; n++ ) {
if( isPrime( n, primes ) ) {
primes.push( n );
--nPrimes;
}
}
return primes;
}
function isPrime( n, primes ) {
var max = Math.sqrt(n);
for( var i = 0; i < primes.length && primes[i] <= max; i++ ) {
if( n % primes[i] === 0 )
return false;
}
return true;
}
如果您要生成大量素数,这可能会更快,尽管对于其中的 100 个素数来说这并不重要,我倾向于坚持使用更简单的代码。
当然,如果你在谈论优化,总是值得考虑不同的算法。Eratosthenes的筛子很有趣,因为它快速、相当简单且易于理解。那篇维基百科文章很好地说明了它的工作原理。在 JavaScript 中,它可能看起来像这样:
function listPrimes( max ) {
// Start with an empty list of primes
var primes = [];
// Initialize the sieve - each number is prime unless proven otherwise
var sieve = new Array( max );
for( var i = 1; i <= max; i++ ) {
sieve[i] = true;
}
// Now check each number from 2 through max
for( var p = 2; p <= max; p++ ) {
if( sieve[p] ) {
// p is prime, save it in the output list
primes.push( p );
// Mark p * 2, p * 3, p * 4, etc. as non-prime
for( var t = p * 2; t <= max; t += p ) {
sieve[t] = false;
}
}
}
return primes;
}
是的,在建议将代码拆分为两个函数之后,我现在又回到了一个函数。:-)
Sieve 的一个区别是你不能真的说,“请给我前 N 个素数”;相反,你问它,“请给我所有小于 N 的素数”。但是如果 N 是一个很大的数,它比其他方法快得多。