阅读 97

算法经典面试题 -- 质数排列(JS)

题目

请你帮忙给从 1n 的数设计排列方案,使得所有的「质数」都应该被放在「质数索引」(索引从 1 开始)上;你需要返回可能的方案总数。

让我们一起来回顾一下「质数」:质数一定是大于 1 的,并且不能用两个小于它的正整数的乘积来表示。

由于答案可能会很大,所以请你返回答案 模 mod 10^9 + 7 之后的结果即可。

 

示例 1:

输入:n = 5
输出:12
解释:举个例子,[1,2,5,4,3] 是一个有效的排列,
      但 [5,2,3,4,1] 不是,因为在第二种情况里质数 5 被错误地放在索引为 1 的位置上。复制代码

示例 2:

输入:n = 100
输出:682289015复制代码

提示:

  • 1 <= n <= 100

题解

首先我们需要理解题目。

题目本质上是排序方案计算总数,例如: n = 5 ,排序总数就是 5 * 4 * 3 * 2 * 1

但题目中提出质数必须在质数的位置,例如: n = 5 ,其中 2、3、5 是质数,也就是 2、3、5 只能在索引 2、3、5 的位置出现。

那么我们排序方案应该分为两种

  • 质数的排列数量

  • 非质数的排列数量

最后两数相乘取其值。

那么我们的代码思路应该如下:

  1. 计算出质数的个数

  2. 计算质数排序方案种类

  3. 计算非质数(总数 - 质数)方案种类

  4. 两者相乘取其值

计算出质数个数函数:

// 判断是否是质数
const isPrimeNumber = (n) => {
    let cnt = 0
    for (let i = 2; i < n; i++) {
        if (n % i === 0) return false
    }
    return true
}

// 获取质数数量
const getPrimeNumberCnt = (n) => {
    let cnt = 0
    for (let i = 2; i <= n; i++) {
        if (isPrimeNumber(i)) {
            cnt++
        }
    }
    return cnt
}

const primeNumberCnt = getPrimeNumberCnt(n)复制代码

计算方案种类函数:

// 阶乘计算
const getFactorial = (n) => {
    let factorial = 1
    for (let i = 2; i <= n; i++) {
        factorial *= i
    }
    return factorial
}复制代码

计算质数排序方案种类:

// 质数排序数量
const pCnt = getFactorial(primeNumberCnt)复制代码

计算非质数拍寻方案种类:

// 非质数排序数量
const oCnt = getFactorial(n - primeNumberCnt)复制代码

计算总个数并取模:

const mod = 10**9 + 7
return (pCnt * oCnt) % mod复制代码

问题

由于众所周知的原因(最后会解释),JSNumber 的最大值是 2 ** 53 -1 ,超出该值会丢失精度,我们写的代码大概率是跑的起来但结果错误的。

解决该问题有两种方案:

  • 使用 BigInt

  • 每次阶乘都 取模

使用 BigInt

// 阶乘计算
const getFactorial = (n) => {
    let factorial = BigInt(1)
    for (let i = 2; i <= n; i++) {
        factorial *= BigInt(i)
    }
    return factorial
}

const pCnt = getFactorial(primeNumberCnt)
const oCnt = getFactorial(n - primeNumberCnt)
const MOD = BigInt(10**9 + 7)
return (pCnt * oCnt) % MOD复制代码

每次阶乘都 取模

const MOD = 10**9 + 7

const getFactorial = (base, n) => {
    let factorial = base
    for (let i = 2; i <= n; i++) {
        factorial *= i
        factorial = factorial % MOD
    }
    return factorial
}

const pCnt = getFactorial(1, primeNumberCnt)
const max = getFactorial(pCnt, n - primeNumberCnt)
return max % MOD复制代码

方案无优劣,自行选取。

答案

/**
 * @param {number} n
 * @return {number}
 */
var numPrimeArrangements = function (n) {
    var MOD = BigInt(10 ** 9 + 7)

    // 判断是否是质数
    const isPrimeNumber = (n) => {
        let cnt = 0
        for (let i = 2; i < n; i++) {
            if (n % i === 0) return false
        }
        return true
    }

    // 获取质数数量
    const getPrimeNumberCnt = (n) => {
        let cnt = 0
        for (let i = 2; i <= n; i++) {
            if (isPrimeNumber(i)) {
                cnt++
            }
        }
        return cnt
    }

    // 阶乘计算
    const getFactorial = (n) => {
        let factorial = BigInt(1)
        for (let i = 2; i <= n; i++) {
            factorial *= BigInt(i)
        }
        return factorial
    }

    const primeNumberCnt = getPrimeNumberCnt(n)
    const pCnt = getFactorial(primeNumberCnt)
    const oCnt = getFactorial(n - primeNumberCnt)
    return (pCnt * oCnt) % MOD
};复制代码

解释

JSNumber 的最大值为什么是 2 ** 53 -1

Number,遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 位,其中1位用来表示符号位,11位用来表示指数,剩下52位表示尾数,就是这个Number.MAX_SAFE_INTEGER,又称为最大安全数,其值为2^53 -1,大于 9007199254740992 的可能会丢失精度

数组的最大长度是2的32次方减1,这个不是指数组只能存2^32-1位数据,也不是说数组的下标最大值就是2^32-1,而是指数组的length属性最大值为2^32-1。

ECMAScript 标准约定number数字需要被当成 64 位双精度浮点数处理,但事实上,一直使用 64 位去存储任何数字实际是非常低效的,所以 JavaScript 引擎并不总会使用 64 位去存储数字,引擎会在内部采用其他内存表示方式,如 32 位。

取模为什么是 10 ** 9 + 7

来源:知乎作者(EntropyIncreaser) 链接:www.zhihu.com/question/49…

其实不止1e9+7,还有1e9+9和998244353。这三个数都是一个质数,同时小于 [公式] 。所以有什么好处呢?

  1. 所有模过之后的数在加法操作在int范围内不会溢出,即 [公式]

  2. 在乘法操作后在long long范围内不会溢出,即 [公式]

  3. 对于 [公式] 中的整数,可进行mod P乘法群意义下的除法,即可以使用扩展欧几里得算法求得 [公式] 中的a。

(另:其实也可以不mod一个质数,那样的话除法就复杂一些,需要进行素因子分解之后帮助除法和中国剩余定理来还原最终答案。)


作者:sincenir
链接:https://juejin.cn/post/7025900704782876708


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐