Skip to content

Kth Smallest Fraction

Select the k-th smallest fraction formed by pairs from a sorted array using heap or binary search.

Kth Smallest Fraction selects the k-th smallest value among fractions of the form:

A[i]A[j] \frac{A[i]}{A[j]}

where the array AA is sorted and:

0i<j<n 0 \le i < j < n

This problem appears in number theory and ranking problems. Direct enumeration gives O(n2)O(n^2) fractions, which is too large.

Problem

Given a sorted array AA of positive integers and an integer kk, return the k-th smallest fraction among all pairs (i,j)(i, j) with i<ji < j.

Algorithm 1: Min Heap

Treat each denominator as a column. Start with the smallest numerator for each denominator.

kth_smallest_fraction(A, k):
    H = empty min heap

    for j from 1 to n - 1:
        push (A[0] / A[j], i=0, j) into H

    repeat k - 1 times:
        (value, i, j) = pop minimum from H

        if i + 1 < j:
            push (A[i + 1] / A[j], i + 1, j) into H

    return top of H

Algorithm 2: Binary Search on Value

Search for a value xx such that exactly kk fractions are less than or equal to xx.

kth_smallest_fraction(A, k):
    low = 0.0
    high = 1.0

    while true:
        mid = (low + high) / 2

        count, best_num, best_den = count_less_equal(A, mid)

        if count == k:
            return (best_num, best_den)
        else if count < k:
            low = mid
        else:
            high = mid

Counting uses two pointers:

count_less_equal(A, x):
    count = 0
    best_num = 0
    best_den = 1

    j = 1

    for i from 0 to n - 2:
        while j < n and A[i] > x * A[j]:
            j = j + 1

        if j == n:
            break

        count = count + (n - j)

        if A[i] * best_den > best_num * A[j]:
            best_num = A[i]
            best_den = A[j]

    return count, best_num, best_den

Example

Let:

A=[1,2,3,5] A = [1, 2, 3, 5]

All fractions:

12,13,15,23,25,35 \frac{1}{2}, \frac{1}{3}, \frac{1}{5}, \frac{2}{3}, \frac{2}{5}, \frac{3}{5}

Sorted:

15,13,25,12,35,23 \frac{1}{5}, \frac{1}{3}, \frac{2}{5}, \frac{1}{2}, \frac{3}{5}, \frac{2}{3}

For k=3k = 3, the answer is:

25 \frac{2}{5}

Correctness

In the heap method, each denominator generates a sorted sequence of fractions. The heap merges these sequences, always extracting the next smallest fraction.

In the binary search method, the predicate “number of fractions x\le x is at least kk” is monotone. The counting step correctly counts valid pairs using the sorted structure.

Tracking the best fraction during counting ensures that the exact k-th fraction is returned.

Complexity

methodtimespace
heapO(klogn)O(k \log n)O(n)O(n)
binary searchO(nlogR)O(n \log R)O(1)O(1)

Here RR is the precision range for floating point search.

When to Use

Use the heap method when kk is small. Use binary search when nn is large and kk may be large.

The binary search method avoids storing many candidates and gives strong performance for large inputs.

Implementation

import heapq

def kth_smallest_fraction(a, k):
    n = len(a)
    heap = []

    for j in range(1, n):
        heapq.heappush(heap, (a[0] / a[j], 0, j))

    for _ in range(k - 1):
        _, i, j = heapq.heappop(heap)
        if i + 1 < j:
            heapq.heappush(heap, (a[i + 1] / a[j], i + 1, j))

    _, i, j = heapq.heappop(heap)
    return a[i], a[j]
func KthSmallestFraction(a []int, k int) (int, int) {
	type Node struct {
		value float64
		i, j  int
	}

	h := &MinHeapFraction{}
	heap.Init(h)

	n := len(a)

	for j := 1; j < n; j++ {
		heap.Push(h, Node{float64(a[0]) / float64(a[j]), 0, j})
	}

	for t := 0; t < k-1; t++ {
		node := heap.Pop(h).(Node)
		i, j := node.i, node.j

		if i+1 < j {
			heap.Push(h, Node{float64(a[i+1]) / float64(a[j]), i + 1, j})
		}
	}

	node := heap.Pop(h).(Node)
	return a[node.i], a[node.j]
}