leetcode
leetcode 1101 ~ 1150
质数排列

质数排列

难度:

标签:

题目描述

代码结果

运行时间: 24 ms, 内存: 16.0 MB


// Solution for leetcode problem using Java Streams
// The task is to count the number of permutations of numbers from 1 to n where all prime numbers are at prime indices.
// A prime index is 1-based. We need to return the result modulo 10^9 + 7.

import java.util.stream.IntStream;

public class PrimeArrangementStream {
    private static final int MOD = 1000000007;

    // Function to check if a number is prime
    private static boolean isPrime(int num) {
        if (num <= 1) return false;
        if (num == 2 || num == 3) return true;
        if (num % 2 == 0 || num % 3 == 0) return false;
        for (int i = 5; i * i <= num; i += 6) {
            if (num % i == 0 || num % (i + 2) == 0) return false;
        }
        return true;
    }

    // Function to count primes up to n
    private static int countPrimes(int n) {
        return (int) IntStream.rangeClosed(1, n).filter(PrimeArrangementStream::isPrime).count();
    }

    // Function to calculate factorial modulo MOD
    private static long factorial(int num) {
        return IntStream.rangeClosed(2, num).asLongStream().reduce(1, (a, b) -> (a * b) % MOD);
    }

    public static int numPrimeArrangements(int n) {
        int primeCount = countPrimes(n);
        int nonPrimeCount = n - primeCount;
        long primeFactorial = factorial(primeCount);
        long nonPrimeFactorial = factorial(nonPrimeCount);
        return (int) ((primeFactorial * nonPrimeFactorial) % MOD);
    }

    public static void main(String[] args) {
        System.out.println(numPrimeArrangements(5));  // Output: 12
        System.out.println(numPrimeArrangements(100));  // Output: 682289015
    }
}

解释

方法:

题解的核心思想是将1到n的数字分为质数和非质数两部分,然后分别在质数的索引位置放置所有质数,非质数索引位置放置所有非质数。首先,算法通过遍历从2到n的所有数字,使用试除法判断每个数是否为质数。如果一个数是质数,质数计数器p递增;如果不是,非质数计数器c递增。之后,为了计算所有可能的排列方案,算法计算p个质数可以有多少种排列方式,即计算p的阶乘。同理,也计算非质数的c个数字的阶乘。最后,将这两个阶乘相乘得到总排列数,由于结果可能非常大,最后结果对10^9 + 7取模。

时间复杂度:

O(n√n)

空间复杂度:

O(1)

代码细节讲解

🦆
在判断一个数是否为质数时,你是如何确保在找到第一个使得i % j == 0的j后立即停止循环的?
在判断一个数是否为质数的循环中,如果在内层循环中发现某个数i可以被j整除(即`i % j == 0`),就会立即执行`break`语句。这个`break`语句会中断内层循环,从而不会继续检查更大的j值是否也能整除i。这确保了一旦找到第一个能使i整除的j,就停止进一步的检查,从而提高效率并减少不必要的计算。
🦆
在计算非质数数量时,为什么会有一个修正操作 `p -= 1`?这是否意味着在计算过程中有可能会错误地将某些数计为质数?
在题解中,`p -= 1`的操作出现是因为算法的设计错误。原本意图是在每次循环开始前默认将当前数i认为是质数,然后通过循环检验其是否真的是质数。如果发现i能被某个j整除,则应该将其视为非质数,并修正之前错误增加的质数计数。然而,这种实现方法容易引起混淆,并且增加了错误的机率,不是一种推荐的做法。更好的实现是只在确认i为质数后再将质数计数器p增加。
🦆
在计算阶乘部分,你采用了直接的连乘方式,这种方法在n较大时有没有可能导致整数溢出?如果有,如何避免?
在直接的连乘计算阶乘时,确实存在整数溢出的风险,尤其是当n较大时。为了避免整数溢出,可以在每次乘法操作后立即对结果进行模运算(模一个大素数,如10^9+7)。这样可以确保结果始终在整数表示范围内,防止溢出。此外,使用模运算还可以保证最终结果的正确性,因为乘法运算在模运算下是封闭的。
🦆
在最终求解时,你是如何处理阶乘结果可能非常大的情况的?是否在每次乘法操作后就进行了模运算?
在最终求解时,为了处理阶乘结果可能非常大的情况,确实在每次乘法操作后就进行了模运算。通过将中间结果每次乘法后都对10^9+7取模,不仅可以防止溢出,还可以保证整个运算过程中数据的大小被有效控制。这是处理大数运算常用的技术,特别是在组合数学和数论中频繁使用。

相关问题