Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1175. Prime Arrangements - Leetcode Solution

Problem Description

The Prime Arrangements problem asks you to determine, for a given integer n, how many ways you can arrange the numbers from 1 to n such that all prime numbers are only placed at prime indices (positions), and non-prime numbers are only placed at non-prime indices. The answer should be returned modulo 10^9 + 7.

  • Each number from 1 to n must be used exactly once.
  • A "prime index" is any position in the arrangement that is a prime number (e.g., positions 2, 3, 5, 7, etc.).
  • Primes must go into prime indices, non-primes into non-prime indices.
  • Return the total number of valid arrangements modulo 10^9 + 7.

Thought Process

At first glance, this problem seems to involve generating all possible permutations and filtering out the invalid ones. However, generating all n! permutations is computationally infeasible for large n.

Instead, notice that the constraints are based on the positions (indices). If we count the number of prime numbers between 1 and n, these primes must be placed at the prime indices. Similarly, non-primes must be placed at the non-prime indices. The arrangement within each group is independent.

Therefore, the problem reduces to:

  • Counting the number of primes between 1 and n (let's call this p).
  • Arranging the p primes among the p prime indices: p! ways.
  • Arranging the remaining n - p non-primes among the n - p non-prime indices: (n - p)! ways.
  • The total is p! * (n - p)!.

Solution Approach

Let's break down the steps to solve the problem efficiently:

  1. Count the number of primes up to n:
    • Use the Sieve of Eratosthenes algorithm to efficiently count primes up to n.
  2. Calculate factorials:
    • Compute p! for the number of primes, and (n - p)! for the non-primes.
    • Since the answer can be very large, use modulo 10^9 + 7 at each multiplication step to prevent overflow.
  3. Return the result:
    • Multiply p! and (n - p)! together, take the result modulo 10^9 + 7, and return it.

This approach is both time and space efficient, and avoids unnecessary computation by leveraging properties of primes and permutations.

Example Walkthrough

Let's consider n = 5:

  • Numbers: 1, 2, 3, 4, 5
  • Prime numbers up to 5: 2, 3, 5 (so p = 3)
  • Prime indices: positions 2, 3, 5 (since indices start from 1)
  • Non-prime numbers: 1, 4
  • Non-prime indices: positions 1, 4
  • Ways to arrange the 3 primes at 3 prime indices: 3! = 6
  • Ways to arrange the 2 non-primes at 2 non-prime indices: 2! = 2
  • Total arrangements: 3! * 2! = 6 * 2 = 12

Thus, for n = 5, the answer is 12.

Time and Space Complexity

  • Brute-force approach: Generating all permutations would take O(n!) time and space, which is not feasible for large n.
  • Optimized approach (current):
    • Prime counting (Sieve of Eratosthenes): O(n \log \log n) time and O(n) space.
    • Factorial calculation: O(n) time and O(1) space if done iteratively.
    • Total: O(n \log \log n) time and O(n) space.

This makes the optimized solution highly efficient and scalable for reasonably large n.

Summary

The Prime Arrangements problem is elegantly solved by recognizing the independence of arranging primes and non-primes at their respective index types. By counting the primes, calculating the factorials of the two groups, and multiplying their possibilities, we avoid brute-force permutations and achieve an efficient solution. The Sieve of Eratosthenes ensures quick prime counting, while modular arithmetic keeps computations manageable. This approach demonstrates the power of mathematical insight in algorithm design.

Code Implementation

MOD = 10 ** 9 + 7

def numPrimeArrangements(n):
    # Sieve of Eratosthenes to count primes up to n
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False
    count = 0
    for i in range(2, n+1):
        if is_prime[i]:
            count += 1
            for j in range(i*i, n+1, i):
                is_prime[j] = False

    def factorial(x):
        res = 1
        for i in range(2, x+1):
            res = res * i % MOD
        return res

    return factorial(count) * factorial(n - count) % MOD
      
class Solution {
public:
    int numPrimeArrangements(int n) {
        const int MOD = 1e9 + 7;
        vector<bool> is_prime(n + 1, true);
        is_prime[0] = is_prime[1] = false;
        int count = 0;
        for (int i = 2; i <= n; ++i) {
            if (is_prime[i]) {
                ++count;
                for (int j = i * 2; j <= n; j += i) {
                    is_prime[j] = false;
                }
            }
        }
        long long res = 1;
        for (int i = 2; i <= count; ++i) {
            res = res * i % MOD;
        }
        for (int i = 2; i <= n - count; ++i) {
            res = res * i % MOD;
        }
        return (int)res;
    }
};
      
class Solution {
    public int numPrimeArrangements(int n) {
        final int MOD = 1000000007;
        boolean[] isPrime = new boolean[n + 1];
        Arrays.fill(isPrime, true);
        isPrime[0] = isPrime[1] = false;
        int count = 0;
        for (int i = 2; i <= n; i++) {
            if (isPrime[i]) {
                count++;
                for (int j = i * 2; j <= n; j += i) {
                    isPrime[j] = false;
                }
            }
        }
        long res = 1;
        for (int i = 2; i <= count; i++) {
            res = res * i % MOD;
        }
        for (int i = 2; i <= n - count; i++) {
            res = res * i % MOD;
        }
        return (int)res;
    }
}
      
var numPrimeArrangements = function(n) {
    const MOD = 1e9 + 7;
    let isPrime = Array(n + 1).fill(true);
    isPrime[0] = isPrime[1] = false;
    let count = 0;
    for (let i = 2; i <= n; ++i) {
        if (isPrime[i]) {
            count++;
            for (let j = i * 2; j <= n; j += i) {
                isPrime[j] = false;
            }
        }
    }
    function factorial(x) {
        let res = 1;
        for (let i = 2; i <= x; ++i) {
            res = res * i % MOD;
        }
        return res;
    }
    return factorial(count) * factorial(n - count) % MOD;
};