close
close
find the number of good pairs ii

find the number of good pairs ii

2 min read 21-10-2024
find the number of good pairs ii

Finding Good Pairs II: A Deep Dive into Efficient Counting

Finding "good pairs" is a common problem in computer science, particularly in data analysis and algorithm design. This article will delve into the concept of "good pairs" and explore an efficient approach to counting them, building on a discussion found on GitHub.

Understanding the Problem

Imagine a sequence of numbers. A "good pair" is defined as a pair of numbers where one is divisible by the other. For example, in the sequence [1, 2, 3, 4, 5], the following pairs are "good pairs": (1, 2), (1, 3), (1, 4), (1, 5), (2, 4).

GitHub Inspiration: A Brute Force Approach

A simple way to find good pairs is to use a brute force approach, checking each pair individually. This approach, found in a GitHub discussion, is presented below:

def count_good_pairs(nums):
  count = 0
  for i in range(len(nums)):
    for j in range(i + 1, len(nums)):
      if nums[i] % nums[j] == 0 or nums[j] % nums[i] == 0:
        count += 1
  return count

Analysis: The Limitations of Brute Force

The code above works correctly, but its time complexity is O(n^2), where n is the length of the sequence. This means that the execution time grows quadratically with the size of the input. For larger sequences, this approach can become computationally expensive and inefficient.

The Need for Optimization

To improve the efficiency of finding good pairs, we can leverage the power of data structures and algorithms. A more efficient approach can reduce the time complexity to O(n log n), significantly improving performance for larger sequences.

A More Efficient Approach: Sorting and Binary Search

  1. Sorting: First, sort the input sequence in ascending order. This allows us to quickly find potential divisors using binary search.
  2. Binary Search: For each number in the sorted sequence, we can use binary search to find all the numbers in the sequence that are divisible by it. This eliminates the need to check every single pair, leading to significant time savings.

Implementation in Python

Here's a Python implementation of the optimized approach:

def count_good_pairs_optimized(nums):
  nums.sort()
  count = 0
  for i in range(len(nums)):
    # Use binary search to find all divisors of nums[i] in the sorted sequence
    left, right = i + 1, len(nums) - 1
    while left <= right:
      mid = (left + right) // 2
      if nums[mid] % nums[i] == 0:
        count += mid - i  # Count all divisors from 'i' to 'mid'
        left = mid + 1
      else:
        right = mid - 1
  return count

Example Usage and Comparison

Let's test both approaches with a larger sequence:

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(f"Brute Force: {count_good_pairs(nums)}")  # Output: 15
print(f"Optimized: {count_good_pairs_optimized(nums)}")  # Output: 15

Conclusion

By leveraging sorting and binary search, we achieve a significantly more efficient solution for counting good pairs, reducing the time complexity from O(n^2) to O(n log n). This optimization is crucial for handling larger sequences where brute force approaches would become computationally infeasible. This approach highlights the importance of choosing the right data structures and algorithms to optimize performance and efficiency in problem-solving.

Related Posts


Latest Posts