Facebook Pixel

1726. Tuple with Same Product

MediumArrayHash TableCounting
Leetcode Link

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, and d 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. First, generate all possible pairs from the array and calculate their products
  2. Group pairs by their product value
  3. 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 Evaluator

Example 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:

  1. (2, 6, 3, 4) → 2 × 6 = 12, 3 × 4 = 12 ✓
  2. (2, 6, 4, 3) → 2 × 6 = 12, 4 × 3 = 12 ✓
  3. (6, 2, 3, 4) → 6 × 2 = 12, 3 × 4 = 12 ✓
  4. (6, 2, 4, 3) → 6 × 2 = 12, 4 × 3 = 12 ✓
  5. (3, 4, 2, 6) → 3 × 4 = 12, 2 × 6 = 12 ✓
  6. (3, 4, 6, 2) → 3 × 4 = 12, 6 × 2 = 12 ✓
  7. (4, 3, 2, 6) → 4 × 3 = 12, 2 × 6 = 12 ✓
  8. (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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More