08/27
2018
看到了一个 JavaScript 面试题,编写一个函数 isPrime,检查输入的参数是否是质数。
质数又称素数,定义为在大于1的自然数中,除了 1 和它本身以外不再有其他因数。也就是只能被 1 和它自身整除。
质数列表如: 2 3 5 7 11 13 17 19 23 ……
要点:
1.质数只能是正整数,所以负数、0、小数都不是质数。
2.质数是从 2 开始的,2 比较特殊,是质数里唯一一个偶数。对 2 要单独处理。
3.大于 2 的偶数都不是质数,因为可以被 2 整除。所以我们可以直接排除偶数,提高效率。
4.把奇数开平方,让奇数依次除以 3、5、7……一直到它的平方根,看有没有能整除的,如果有那就不是质数。
开平方这一步可以大大提高效率,它的数学原理我也不是很懂,据我猜测大概如下:
599/3=199.66666666666666 599/199=3.0100502512562812
用 599 除以 3,或者除以 199,其实就相当于把 除数 和 商 颠倒了一下,所以没必要两个都验证,只验证 3 就可以了。
数字 599 的平方根约为 24,我们验证到 24 就可以停了,因为验证 24 之后的数字,其实就类似上面的情况,没有必要。
如果不考虑对输入的数据做判断,就很简单,大概这样:
function isPrime(num) { // 排除不在质数范围内的数字(处理负数、0、1) if (num < 2) { return false; } // 2 是特例,单独处理(处理2) if (num === 2) { return true; } // 如果是偶数,则直接输出结果(处理大于2的偶数)。这样可以提高效率 if (num % 2 === 0) { return false; } // 处理大于2的奇数 let sq = Math.sqrt(num); // 开平方,验证时数字不大于平方 // 从除以 3 开始验证,只验证奇数,到平方根停止。 for (let index = 3; index <= sq; index += 2) { if (num % index === 0) { // 未通过检查 return false; } } // 检查通过 return true; }
但是数据验证还是必要的,我自己琢磨了琢磨。
比如输入值为 true、 null 这种,可以强制转换成数字 0,但这根本和数字没半毛钱关系,所以判断时不要用强制转换。
比如要排除带小数点的数字,但我发现对于很大的数字,JavaScript 无法判断它有没有小数点。
对于很大的数字,就算不考虑小数点,JavaScript 也根本无法正确的计算。
这些关于数字的验证坑了我不少时间。最后看参考答案,丫根本不做验证的,坑爹啊
// 质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。 // 不要使用超出 JavaScript 精确整数范围的数字(−9007199254740992 至 9007199254740992,即正负2的53次方),否则无法正确处理。 // 检查数字是否是质数。参数只允许数字和字符串形式的数字 function isPrime(num) { // 排除不希望接收的的参数 * 0 if (isNaN(parseInt(num))) { console.error('不是数字'); return false; } // 转换成数字 * 1 num = Number(num); // 判断数字范围 if (num < -9007199254740992 || num > 9007199254740992) { console.log('%c超出处理范围', 'color:#EB3941;'); return false; } // 排除带小数的 * 2 if (num.toString().indexOf('.') > -1) { console.log('%c有小数点', 'color:#EB3941;'); outputResult(false); return false; } // 排除不在质数范围内的数字(处理负数、0、1) if (num < 2) { outputResult(false); return false; } // 2 是特例,单独处理(处理2) if (num === 2) { outputResult(true); return true; } // 如果是偶数,则直接输出结果(处理大于2的偶数)。这样可以提高效率 if (num % 2 === 0) { outputResult(false, 2); return false; } // 处理大于2的奇数 let sq = Math.sqrt(num); // 开平方,验证时数字不大于平方 // 从除以 3 开始验证,只验证奇数,到平方根停止。 for (let index = 3; index <= sq; index += 2) { if (num % index === 0) { // 未通过检查 outputResult(false, index); return false; } } // 检查通过 outputResult(true); return true; // 输出结果的函数 function outputResult(bool, find) { if (bool) { console.log('%c是质数', 'color:green;'); } else { if (find) { console.log(`%c可以被 ${find} 整除`, 'color:#666;'); } console.log('%c不是质数', 'color:#EB3941;'); } } } // 测试 isPrime(5415646465464617); /** * 0: 判断参数时为什么使用 parseInt 而不是 Number,因为它们的转换结果有区别 : 比如我们不想接受 true、false、null、[]、'' 等非数字内容: 使用 parseInt() 它们会被转换成 NaN,这样可以排除这些参数。 如果使用 Number(),它们会被强制转换成数字 0 或 1,不能起到排除效果。 * 1: 参数通过检查后,转换成数字开始使用时,使用 Number()比较好。因为 parseInt() 处理的上限不够大,Number() 的范围更大: parseInt(5415646465464654456465646546545646) // 5 这个结果是错误的 Number(5415646465464654456465646546545646) // 5.415646465464654e+33 这个错的没那么严重,其实也不对 它相当于只能表示到 5415646465464654 ,后面那部分数字被舍弃了。因为这个完整的数字超出了 9007199254740992 。 对于超出 JavaScript 精确整数范围的数字,使用 Number() 比 parseInt() 好一点,错的没那么严重。但其实都不准确了,这里只是提一下。 * 2: 这个方法判断小数时,把数字转换成字符串,查找带不带小数点。但是对于科学计数法的数字,以及超出处理范围的数字来说,都无法准确判断。 如 5415646465464654456465646546545646 没有小数,但转换成字符串是科学计数法的 "5.415646465464654e+33",这样就有了点 . ,会被判断成小数。 有时候,即使数字本身带小数,但超出处理范围,就无法分辨了。如 JavaScript 认为 5415646465464654456465646546545646.5 和它不带小数时是一样的(全等)。 这种超大数字也许用其他编程语言处理更合适。如这个查素数的网站,就是把数字传到服务器上计算的,而不是用 JavaScript 直接计算。 http://www.haomeili.net/ZhiShu/IsZhiShu */
如果算大于平方根的数的话就差不多是重复前面(平方根)的过程了~
反证一波~假设某数x可由两个素数相乘所得,则x不为素数;若这两个素数相等,就是平方根的情况;若不等,由乘法交换律,小乘大等于大乘小,一样的。如果在比平方根小的半区检验不出,那如果在大的半区检验到了符合条件的素数(奇数)b,那对应的奇数a就在小半区,与前设条件矛盾。所以。。。
╮(╯_╰)╭
脑袋抽风,这条评论画风不对