Distinct Prime Factors of Product of Array
MedPrompt
Given an array of positive integers nums, return the number of distinct prime factors in the product of the elements of nums.
Note that:
- A number greater than
1is called prime if it is divisible by only1and itself. - An integer
val1is a factor of another integerval2ifval2 / val1is an integer.
Example 1:
Input: nums = [2,4,3,7,10,6]
Output: 4
Explanation:
The product of all the elements in nums is: 2 * 4 * 3 * 7 * 10 * 6 = 10080 = 25 * 32 * 5 * 7.
There are 4 distinct prime factors so we return 4.Example 2:
Input: nums = [2,4,8,16]
Output: 1
Explanation:
The product of all the elements in nums is: 2 * 4 * 8 * 16 = 1024 = 210.
There is 1 distinct prime factor so we return 1.
Constraints:
1 <= nums.length <= 1042 <= nums[i] <= 1000
Approaches
2 approaches with complexity analysis and trade-offs.
This approach iterates through each number in the input array nums. For each number, it finds all its prime factors using trial division. A HashSet is used to store the distinct prime factors found across all numbers. The final answer is the size of this set.
Algorithm
- Initialize an empty
HashSet<Integer>nameddistinctPrimes. - Iterate through each integer
numin the input arraynums. - For each
num, find its prime factors:- Iterate with a divisor
dfrom 2 up tosqrt(num). - If
ddividesnum:- Add
dto thedistinctPrimesset. - Continuously divide
numbyduntil it is no longer divisible.
- Add
- After the loop, if
numis still greater than 1, it is a prime factor itself. Add it todistinctPrimes.
- Iterate with a divisor
- Return the final size of the
distinctPrimesset.
Walkthrough
The core idea is that the set of prime factors of the product of several numbers is the union of the sets of prime factors of each individual number. This avoids calculating the potentially huge product which would cause an overflow.
We initialize a HashSet to keep track of the unique prime factors. We then loop through each num in the nums array. For each num, we perform prime factorization by trial division. We start with a divisor d = 2 and check for divisibility. If d divides num, we add d to our HashSet and then divide num by d repeatedly until it's no longer divisible. This ensures we handle powers of a prime factor (like 8 = 2*2*2) correctly. We then increment d and repeat the process. We only need to check divisors up to the square root of the current num. If, after the loop, the remaining value of num is greater than 1, it must be a prime factor itself (e.g., when factorizing 14, after dividing by 2, we are left with 7). We add this remaining number to the set. Finally, the number of distinct prime factors is simply the size of the HashSet.
import java.util.HashSet;import java.util.Set; class Solution { public int distinctPrimeFactors(int[] nums) { Set<Integer> primeFactors = new HashSet<>(); for (int num : nums) { findPrimeFactors(num, primeFactors); } return primeFactors.size(); } private void findPrimeFactors(int n, Set<Integer> factors) { // Trial division to find prime factors for (int i = 2; i * i <= n; i++) { if (n % i == 0) { factors.add(i); while (n % i == 0) { n /= i; } } } // If n is a prime number greater than 1 after the loop if (n > 1) { factors.add(n); } }}Complexity
Time
O(N * sqrt(M)), where N is the number of elements in `nums` and M is the maximum value of an element in `nums`. For each of the N numbers, we perform trial division up to `sqrt(M)`.
Space
O(P_M), where P_M is the number of prime numbers less than or equal to M (the maximum value in `nums`). This space is used by the `HashSet`. Since M <= 1000, the number of primes is a small constant (168), so the space can be considered O(1).
Trade-offs
Pros
Simple to understand and implement.
Does not require any pre-computation.
Space efficient as it only stores the final prime factors.
Cons
Less efficient than approaches using pre-computation, as it repeatedly calculates factors for numbers.
The factorization step
O(sqrt(M))is slower than theO(log M)factorization possible with sieves.
This approach improves upon the brute-force method by pre-computing the smallest prime factor (SPF) for all numbers up to the maximum possible value in the input array (1000). This pre-computation is done using a Sieve. With the SPF array, finding the prime factors of any number becomes much faster, changing the complexity from O(sqrt(M)) to O(log M) per number.
Algorithm
- Pre-computation:
- Create an array
spf(Smallest Prime Factor) of sizeM+1, whereMis the maximum possible value ofnums[i](1000). - Use a Sieve-like algorithm to populate
spf. Initializespf[i] = i. Iterate fromp=2tosqrt(M). Ifpis prime (spf[p] == p), markpas the smallest prime factor for all its multiples.
- Create an array
- Main Logic:
- Initialize an empty
HashSet<Integer>nameddistinctPrimes. - Iterate through each integer
numin the input arraynums. - While
num > 1:- Find the smallest prime factor
p = spf[num]. - Add
pto thedistinctPrimesset. - Divide
numbyprepeatedly until it's no longer divisible to remove all occurrences of this factor.
- Find the smallest prime factor
- Initialize an empty
- Return the size of the
distinctPrimesset.
Walkthrough
The main bottleneck in the previous approach is the O(sqrt(M)) factorization step for each number. We can optimize this by pre-calculating information. Since all numbers are bounded by 1000, we can use a Sieve of Eratosthenes variant to build a "Smallest Prime Factor" (SPF) array.
Step 1: Pre-computation (Sieve)
We create an integer array spf of size 1001. We initialize spf[i] = i for all i. Then, we iterate from p = 2 up to sqrt(1000). If spf[p] is still p, it means p is a prime number. For each prime p, we iterate through its multiples and update their smallest prime factor to p.
Step 2: Factorization
With the spf array ready, we initialize an empty HashSet to store distinct prime factors. We iterate through each num in the nums array. To factorize num, we repeatedly find its smallest prime factor p = spf[num], add p to our set, and then divide num by p until it's no longer divisible. We continue this process until num becomes 1.
Step 3: Result
The size of the HashSet gives the final count of distinct prime factors.
import java.util.HashSet;import java.util.Set; class Solution { private static final int MAX_VAL = 1000; private static final int[] spf = new int[MAX_VAL + 1]; // Static block for one-time pre-computation static { for (int i = 2; i <= MAX_VAL; i++) { spf[i] = i; } for (int i = 2; i * i <= MAX_VAL; i++) { if (spf[i] == i) { // i is a prime for (int j = i * i; j <= MAX_VAL; j += i) { if (spf[j] == j) { // if spf[j] is not set yet spf[j] = i; } } } } } public int distinctPrimeFactors(int[] nums) { Set<Integer> distinctPrimes = new HashSet<>(); for (int num : nums) { int currentNum = num; while (currentNum > 1) { int p = spf[currentNum]; distinctPrimes.add(p); while (currentNum % p == 0) { currentNum /= p; } } } return distinctPrimes.size(); }}Complexity
Time
O(M log log M + N * log M). The sieve pre-computation takes `O(M log log M)`. Then, for each of the N numbers, factorization takes `O(log M)` time because in each step we divide the number by at least its smallest prime factor (which is >= 2).
Space
O(M), where M is the maximum value in `nums`. We need an array of size `M+1` for the sieve (`spf` array). The `HashSet` also uses space, but it's bounded by the number of primes up to M, which is less than M.
Trade-offs
Pros
Very efficient for the given constraints, as the factorization step is much faster (
O(log M)).The pre-computation is done only once, making it highly effective if the function is called multiple times with numbers within the same range.
Cons
Requires extra space
O(M)for the sieve array.More complex to implement due to the pre-computation step.
Solutions
Solution
class Solution { public int distinctPrimeFactors ( int [] nums ) { Set < Integer > s = new HashSet <>(); for ( int n : nums ) { for ( int i = 2 ; i <= n / i ; ++ i ) { if ( n % i == 0 ) { s . add ( i ); while ( n % i == 0 ) { n /= i ; } } } if ( n > 1 ) { s . add ( n ); } } return s . size (); } }class Solution { public: int distinctPrimeFactors ( vector < int >& nums ) { unordered_set < int > s ; for ( int & n : nums ) { for ( int i = 2 ; i <= n / i ; ++ i ) { if ( n % i == 0 ) { s . insert ( i ); while ( n % i == 0 ) { n /= i ; } } } if ( n > 1 ) { s . insert ( n ); } } return s . size (); } };class Solution : def distinctPrimeFactors ( self , nums : List [ int ]) -> int : s = set () for n in nums : i = 2 while i <= n // i : if n % i == 0 : s . add ( i ) while n % i == 0 : n //= i i += 1 if n > 1 : s . add ( n ) return len ( s )Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.