pairing numbers
arrays
mathematical conditions
algorithmic problem-solving
number pairs

Pairing numbers a,b in an array such a way that a2 b

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

The title has clearly lost an operator, so the first job is to pin down the intended inequality before choosing an algorithm. The non-trivial version of this problem is usually: form as many disjoint pairs as possible such that 2 * a <= b, because that leads to a real matching problem; if the rule were 2 * a >= b and pair order were flexible on non-negative numbers, the answer would collapse to floor(n / 2).

The useful interpretation: maximize pairs with 2 * a <= b

Assume we have an array of non-negative integers and want the maximum number of disjoint pairs where the smaller value in each pair is at most half of the larger value. After sorting, the task becomes: for each candidate small value, find a later unused large value that is at least twice as big.

The key observation is that using the smallest available valid large value is always safe. If a small number can pair with several larger values, spending the smallest valid one leaves more flexibility for the remaining elements.

That leads to a greedy two-pointer solution.

Why sorting and two pointers work

After sorting the array, split your thinking into two regions:

  • the left side contains candidates for a
  • the right side contains candidates for b

Then advance through both sides:

  1. point i at the first unused element in the left half
  2. point j at the first candidate in the right half
  3. if 2 * arr[i] <= arr[j], record a pair and move both pointers
  4. otherwise move j until you find a large enough partner

Because the array is sorted, once arr[j] is too small for arr[i], it is also too small for every later left-side value that is at least as large as arr[i]. So skipping that j for the current i is not wrong; it simply means j is not a useful large partner yet.

Python implementation

python
1def max_pairs(values):
2    values = sorted(values)
3    n = len(values)
4    i = 0
5    j = (n + 1) // 2
6    pairs = []
7
8    while i < n // 2 and j < n:
9        if 2 * values[i] <= values[j]:
10            pairs.append((values[i], values[j]))
11            i += 1
12            j += 1
13        else:
14            j += 1
15
16    return pairs
17
18
19nums = [3, 1, 3, 4, 9, 10]
20result = max_pairs(nums)
21print(result)
22print("count:", len(result))

For the sample above, one valid output is [(1, 3), (3, 9), (4, 10)], giving three disjoint pairs.

The running time is dominated by sorting, so the algorithm is O(n log n). The pointer walk after sorting is linear.

Step-by-step example

Take the array [1, 2, 2, 3, 7, 8].

After sorting, it is already ordered. Start with i = 0 on 1 and j = 3 on 3.

  • '2 * 1 <= 3, so pair (1, 3)'
  • move to 2 on the left and 7 on the right
  • '2 * 2 <= 7, so pair (2, 7)'
  • move to the next 2 on the left and 8 on the right
  • '2 * 2 <= 8, so pair (2, 8)'

We get three pairs, which is optimal because all six elements are used.

This example shows why the right pointer starts in the second half. Any valid b must come from a later position than a, and starting too early only wastes comparisons.

What if the original condition was something else

This title might also have meant 2 * a >= b or even a^2 = b. Those are different problems.

If the rule is 2 * a >= b and you may swap elements inside each pair, then for non-negative numbers almost any two values can be made valid by naming the larger one as a. The problem stops being interesting.

If the rule is a^2 = b, the task becomes value matching, not greedy pairing by order. In that case you would count frequencies and look for squares. So before coding, confirm the intended operator.

Why this greedy strategy is correct

The algorithm never harms a future solution by pairing a small value with the smallest large value that works. Suppose a larger value was used instead. Then the smaller valid large value would still remain unused, but it could not help any earlier left element better than the one we are handling now. Using the smallest feasible partner preserves as many options as possible for later elements.

That is the same exchange argument used in many interval and matching greedies: if an optimal solution used a larger partner where a smaller valid one existed, we can swap them without reducing the number of pairs.

Common Pitfalls

A common mistake is solving the wrong inequality because the statement dropped an operator. Confirm the rule before implementing anything.

Another mistake is trying every possible pair with nested loops. That works for tiny arrays but is unnecessarily slow once sorting plus two pointers is available.

A third mistake is forgetting that pairs must be disjoint. Counting every valid comparison is not the same as building a maximum set of non-overlapping pairs.

Summary

  • The meaningful array-pairing version is usually to maximize disjoint pairs with 2 * a <= b.
  • Sort the array and use two pointers, one in the left half and one in the right half.
  • Pair each small value with the smallest valid large value.
  • The algorithm runs in O(n log n) time because sorting dominates.
  • If the original missing operator was different, the correct algorithm may change completely.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.