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
.
1
to n
must be used exactly once.10^9 + 7
.
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:
1
and n
(let's call this p
).p
primes among the p
prime indices: p!
ways.n - p
non-primes among the n - p
non-prime indices: (n - p)!
ways.p! * (n - p)!
.Let's break down the steps to solve the problem efficiently:
n
:
n
.p!
for the number of primes, and (n - p)!
for the non-primes.10^9 + 7
at each multiplication step to prevent overflow.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.
Let's consider n = 5:
1, 2, 3, 4, 5
2, 3, 5
(so p = 3
)1, 4
3! = 6
2! = 2
3! * 2! = 6 * 2 = 12
Thus, for n = 5
, the answer is 12
.
O(n!)
time and space, which is not feasible for large n
.
O(n \log \log n)
time and O(n)
space.
O(n)
time and O(1)
space if done iteratively.
O(n \log \log n)
time and O(n)
space.
This makes the optimized solution highly efficient and scalable for reasonably large n
.
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.
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;
};