1726. Tuple with Same Product
Problem Description
You are given an array nums
containing distinct positive integers. Your task is to find the number of tuples (a, b, c, d)
where all four elements are from nums
and satisfy the equation a * b = c * d
.
The key constraints are:
- All four elements
a
,b
,c
, andd
must be different from each other - The elements must satisfy the product equality:
a * b = c * d
- The order of elements in the tuple matters (so
(1, 2, 3, 4)
is different from(2, 1, 3, 4)
)
For example, if nums = [2, 3, 4, 6]
, one valid tuple would be (2, 6, 3, 4)
because 2 * 6 = 12
and 3 * 4 = 12
.
The solution works by first finding all pairs of numbers and calculating their products. It uses a hash table to count how many pairs share the same product. When multiple pairs have the same product, they can be combined to form valid tuples.
For each product that appears n
times (meaning n
different pairs multiply to give that product), we can choose any 2 pairs from these n
pairs, giving us C(n, 2) = n * (n-1) / 2
combinations. Since each combination of two pairs can be arranged in 8 different ways as tuples (considering the order and position of elements), the final count is multiplied by 8.
The expression << 3
in the code is a bitwise left shift by 3 positions, which is equivalent to multiplying by 2^3 = 8
.
Intuition
The key insight is recognizing that if a * b = c * d
, we're essentially looking for pairs of numbers that produce the same product. Instead of checking all possible 4-element combinations (which would be very expensive), we can transform this into a simpler problem.
Think about it this way: if we have two pairs (a, b)
and (c, d)
where a * b = c * d
, then these two pairs share the same product value. So the problem becomes: find all pairs of pairs that have equal products.
We can break this down into steps:
- First, generate all possible pairs from the array and calculate their products
- Group pairs by their product value
- For each product value that has multiple pairs, count how many ways we can choose 2 pairs from that group
Why does this work? Because if we have, say, 3 pairs that all multiply to 12: (2,6)
, (3,4)
, and (1,12)
, we can pick any 2 of these pairs to form valid tuples. The number of ways to choose 2 pairs from n
pairs is the combination formula C(n, 2) = n * (n-1) / 2
.
The final trick is realizing that once we have two pairs (a, b)
and (c, d)
with equal products, we can arrange them into tuples in exactly 8 different ways:
(a, b, c, d)
,(a, b, d, c)
(b, a, c, d)
,(b, a, d, c)
(c, d, a, b)
,(c, d, b, a)
(d, c, a, b)
,(d, c, b, a)
This is why we multiply our combination count by 8 at the end (using << 3
which is a fast way to multiply by 8).
Solution Approach
The solution uses a combination counting approach with a hash table to efficiently count tuples with equal products.
Step 1: Generate all pairs and their products
We iterate through all possible pairs of numbers from the array using two nested loops. For each pair (nums[i], nums[j])
where j < i
, we calculate their product.
for i in range(1, len(nums)):
for j in range(i):
x = nums[i] * nums[j]
Step 2: Count occurrences of each product
We use a defaultdict(int)
to count how many pairs produce each product value. The key is the product, and the value is the count of pairs that multiply to that product.
cnt = defaultdict(int)
cnt[x] += 1
Step 3: Calculate combinations for each product
For each product that appears v
times (meaning v
different pairs multiply to give that product), we can choose any 2 pairs from these v
pairs. The number of ways to do this is given by the combination formula:
C(v, 2) = v * (v - 1) / 2
This tells us how many ways we can select 2 pairs that have the same product.
Step 4: Multiply by 8 to get all tuple arrangements
Each combination of two pairs (a, b)
and (c, d)
can form exactly 8 different tuples that satisfy the problem requirements. This is because:
- We can choose which pair goes first (2 choices)
- For each pair, we can swap the elements (2 × 2 = 4 arrangements per choice)
- Total: 2 × 2 × 2 = 8 arrangements
The final calculation becomes:
sum(v * (v - 1) // 2 for v in cnt.values()) << 3
The << 3
operation is a bitwise left shift by 3 positions, equivalent to multiplying by 2^3 = 8
, which accounts for all possible tuple arrangements.
Time Complexity: O(n^2)
where n
is the length of the array, as we examine all pairs.
Space Complexity: O(n^2)
in the worst case for storing products in the hash table.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [2, 3, 4, 6]
.
Step 1: Generate all pairs and their products
We'll create pairs from the array and calculate their products:
- Pair (2, 3): product = 6
- Pair (2, 4): product = 8
- Pair (2, 6): product = 12
- Pair (3, 4): product = 12
- Pair (3, 6): product = 18
- Pair (4, 6): product = 24
Step 2: Count occurrences of each product
Using our hash table cnt
:
- Product 6: appears 1 time (from pair (2, 3))
- Product 8: appears 1 time (from pair (2, 4))
- Product 12: appears 2 times (from pairs (2, 6) and (3, 4))
- Product 18: appears 1 time (from pair (3, 6))
- Product 24: appears 1 time (from pair (4, 6))
Step 3: Calculate combinations for each product
For products that appear multiple times, we can form valid tuples:
- Product 12 appears 2 times, so we can choose 2 pairs from 2: C(2, 2) = 2 × 1 / 2 = 1 combination
The pairs (2, 6) and (3, 4) both multiply to 12, so they can form valid tuples.
Products that appear only once (6, 8, 18, 24) contribute 0 combinations since we need at least 2 pairs with the same product.
Total combinations: 0 + 0 + 1 + 0 + 0 = 1
Step 4: Multiply by 8 for all arrangements
From the pair combination ((2, 6), (3, 4)), we can create 8 different tuples:
- (2, 6, 3, 4) → 2 × 6 = 12, 3 × 4 = 12 ✓
- (2, 6, 4, 3) → 2 × 6 = 12, 4 × 3 = 12 ✓
- (6, 2, 3, 4) → 6 × 2 = 12, 3 × 4 = 12 ✓
- (6, 2, 4, 3) → 6 × 2 = 12, 4 × 3 = 12 ✓
- (3, 4, 2, 6) → 3 × 4 = 12, 2 × 6 = 12 ✓
- (3, 4, 6, 2) → 3 × 4 = 12, 6 × 2 = 12 ✓
- (4, 3, 2, 6) → 4 × 3 = 12, 2 × 6 = 12 ✓
- (4, 3, 6, 2) → 4 × 3 = 12, 6 × 2 = 12 ✓
Final answer: 1 × 8 = 8 tuples
The calculation using our formula: sum(v * (v - 1) // 2 for v in cnt.values()) << 3
gives us (2 * 1 // 2) << 3 = 1 << 3 = 8
.
Solution Implementation
1from typing import List
2from collections import defaultdict
3
4class Solution:
5 def tupleSameProduct(self, nums: List[int]) -> int:
6 # Dictionary to count frequency of each product
7 product_count = defaultdict(int)
8
9 # Calculate product for every pair of numbers
10 for i in range(1, len(nums)):
11 for j in range(i):
12 product = nums[i] * nums[j]
13 product_count[product] += 1
14
15 # For each product that appears k times, we can form k*(k-1)/2 pairs
16 # Each pair can form 8 different tuples (2 * 2 * 2 permutations)
17 result = 0
18 for frequency in product_count.values():
19 # Number of ways to choose 2 pairs from 'frequency' pairs
20 pairs_count = frequency * (frequency - 1) // 2
21 # Each pair combination generates 8 valid tuples
22 result += pairs_count * 8
23
24 return result
25
1class Solution {
2 public int tupleSameProduct(int[] nums) {
3 // Map to store the frequency of each product
4 // Key: product of two numbers, Value: count of pairs with this product
5 Map<Integer, Integer> productFrequency = new HashMap<>();
6
7 // Calculate products for all pairs (i, j) where j < i
8 for (int i = 1; i < nums.length; i++) {
9 for (int j = 0; j < i; j++) {
10 // Calculate product of current pair
11 int product = nums[i] * nums[j];
12
13 // Increment the frequency count for this product
14 productFrequency.merge(product, 1, Integer::sum);
15 }
16 }
17
18 // Count the number of valid tuples
19 int totalTuples = 0;
20
21 // For each product that appears multiple times
22 for (int frequency : productFrequency.values()) {
23 // Calculate combinations: choose 2 pairs from 'frequency' pairs
24 // This gives us C(frequency, 2) = frequency * (frequency - 1) / 2
25 totalTuples += frequency * (frequency - 1) / 2;
26 }
27
28 // Each combination of 2 pairs can form 8 different tuples
29 // (a,b,c,d), (a,d,c,b), (b,a,d,c), (b,c,d,a),
30 // (c,b,a,d), (c,d,a,b), (d,a,b,c), (d,c,b,a)
31 // So multiply by 8 (equivalent to left shift by 3)
32 return totalTuples << 3;
33 }
34}
35
1class Solution {
2public:
3 int tupleSameProduct(vector<int>& nums) {
4 // Map to store the frequency of each product
5 unordered_map<int, int> productCount;
6
7 // Calculate all possible products of pairs
8 // Iterate through all unique pairs (i, j) where j < i
9 for (int i = 1; i < nums.size(); ++i) {
10 for (int j = 0; j < i; ++j) {
11 // Calculate product of current pair
12 int product = nums[i] * nums[j];
13 // Increment the count for this product
14 ++productCount[product];
15 }
16 }
17
18 // Count the number of valid 8-tuples
19 int result = 0;
20 for (auto& [product, frequency] : productCount) {
21 // For each product that appears 'frequency' times,
22 // we can choose 2 pairs from these 'frequency' pairs
23 // Number of ways = C(frequency, 2) = frequency * (frequency - 1) / 2
24 result += frequency * (frequency - 1) / 2;
25 }
26
27 // Each valid pair of pairs can form 8 different tuples
28 // (a,b,c,d), (a,b,d,c), (b,a,c,d), (b,a,d,c),
29 // (c,d,a,b), (c,d,b,a), (d,c,a,b), (d,c,b,a)
30 // So multiply the result by 8 (equivalent to left shift by 3)
31 return result << 3;
32 }
33};
34
1/**
2 * Counts the number of tuples (a, b, c, d) where a * b = c * d
3 * @param nums - Array of distinct positive integers
4 * @returns Number of tuples that satisfy the condition
5 */
6function tupleSameProduct(nums: number[]): number {
7 // Map to store product values and their frequency
8 const productFrequencyMap: Map<number, number> = new Map();
9
10 // Calculate all possible products from pairs of numbers
11 for (let i = 1; i < nums.length; i++) {
12 for (let j = 0; j < i; j++) {
13 const product = nums[i] * nums[j];
14 // Increment the frequency count for this product
15 productFrequencyMap.set(product, (productFrequencyMap.get(product) ?? 0) + 1);
16 }
17 }
18
19 // Calculate the total number of valid tuples
20 let tupleCount = 0;
21
22 // For each product that appears multiple times, calculate combinations
23 for (const [_, frequency] of productFrequencyMap) {
24 // Choose 2 pairs from 'frequency' pairs: C(frequency, 2) = frequency * (frequency - 1) / 2
25 tupleCount += (frequency * (frequency - 1)) / 2;
26 }
27
28 // Each combination of 2 pairs can form 8 different tuples
29 // For pairs (a,b) and (c,d), we can form: (a,b,c,d), (a,b,d,c), (b,a,c,d), (b,a,d,c),
30 // (c,d,a,b), (c,d,b,a), (d,c,a,b), (d,c,b,a)
31 return tupleCount << 3; // Multiply by 8 using bit shift
32}
33
Time and Space Complexity
The time complexity is O(n^2)
, where n
is the length of the array. This is because the code uses two nested loops - the outer loop runs from index 1 to n-1
, and the inner loop runs from index 0 to i-1
. The total number of iterations is (n-1) + (n-2) + ... + 1 = n(n-1)/2
, which simplifies to O(n^2)
. Each iteration performs constant time operations (multiplication, dictionary access, and increment).
The space complexity is O(n^2)
. In the worst case, when all products nums[i] * nums[j]
are distinct, the dictionary cnt
will store n(n-1)/2
unique products, which is O(n^2)
space. Even though the actual number of unique products might be less in practice, the worst-case space complexity remains O(n^2)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Counting Tuple Arrangements
One of the most common mistakes is miscounting how many valid tuples can be formed from two pairs with the same product. Developers often forget that order matters in tuples or incorrectly calculate the permutations.
Incorrect approach:
# Wrong: Only counting 4 arrangements instead of 8 result += pairs_count * 4
Why it's wrong: Given two pairs (a, b) and (c, d) where ab = cd, there are actually 8 distinct tuples:
- (a, b, c, d)
- (a, b, d, c)
- (b, a, c, d)
- (b, a, d, c)
- (c, d, a, b)
- (c, d, b, a)
- (d, c, a, b)
- (d, c, b, a)
Solution: Always multiply by 8, or use bitwise shift << 3
for efficiency.
2. Using the Same Element Multiple Times
Another pitfall is accidentally allowing the same element to appear multiple times in a tuple when iterating through pairs.
Incorrect approach:
# Wrong: This could use the same index twice
for i in range(len(nums)):
for j in range(len(nums)):
if i != j: # Still wrong - doesn't prevent reuse across pairs
product = nums[i] * nums[j]
Solution: Ensure proper iteration bounds and that all four indices are distinct:
for i in range(1, len(nums)):
for j in range(i): # j < i ensures different indices
product = nums[i] * nums[j]
3. Integer Overflow with Large Products
When dealing with large numbers in the array, the product might exceed integer limits in some languages (though Python handles big integers automatically).
Potential issue in other languages:
# In languages with fixed integer sizes, this could overflow product = nums[i] * nums[j] # Could exceed INT_MAX
Solution for Python: No action needed as Python handles arbitrary precision integers. For other languages, consider using long long or checking for overflow.
4. Inefficient Product Storage
Storing products as floating-point numbers or using inefficient data structures can lead to precision issues or poor performance.
Incorrect approach:
# Wrong: Using regular dict with get() calls product_count = {} product = nums[i] * nums[j] product_count[product] = product_count.get(product, 0) + 1
Solution: Use defaultdict(int)
for cleaner, more efficient code:
product_count = defaultdict(int)
product_count[product] += 1
5. Off-by-One Error in Combination Calculation
Miscalculating the number of ways to choose 2 pairs from n pairs is a subtle but critical error.
Incorrect approach:
# Wrong formulas for combinations pairs_count = frequency * (frequency + 1) // 2 # Wrong pairs_count = frequency * frequency // 2 # Wrong
Solution: Use the correct combination formula C(n, 2) = n × (n-1) / 2:
pairs_count = frequency * (frequency - 1) // 2
What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!