Radix select in Python
Find the element of a given rank by examining keys digit by digit and narrowing the candidate buckets.
- File
radix_select.py, 39 lines- Level
- I0 not checked yet
- Toolchain
- Python 3.14, pytest 9
- Test framework
- pytest
- Run the tests
python3 -m pytest
# SPDX-License-Identifier: MIT
# Part of https://algorithm.tamnd.com/radix-select/. The authors are listed in prov/ZPSQHH.jsonl.
from typing import Optional, Sequence
def radix_select(a: Sequence[int], k: int, r: int = 8, w: Optional[int] = None) -> int:
"""Return the item of rank k (1-based) of a list of nonnegative integers.
Looks at the keys r bits at a time, from the most significant digit of a
w-bit key down, and keeps only the bucket that holds rank k. If w is not
given, it is the bit length of the largest key. Uses no comparisons
between keys.
"""
n = len(a)
if not 1 <= k <= n:
raise ValueError("k must be between 1 and len(a)")
if r < 1:
raise ValueError("r must be at least 1")
if any(x < 0 for x in a):
raise ValueError("keys must be nonnegative")
if w is None:
w = max(max(a).bit_length(), 1)
mask = (1 << r) - 1
s = list(a) # @line L1
d = -(-w // r) # @line L1
for j in range(d - 1, -1, -1): # @line L2
if len(s) == 1: # @line L3
return s[0] # @line L3
shift = r * j
count = [0] * (mask + 1) # @line L4
for x in s: # @line L5
count[(x >> shift) & mask] += 1 # @line L5
b = 0 # @line L6
while k > count[b]: # @line L7
k -= count[b] # @line L7
b += 1 # @line L7
s = [x for x in s if (x >> shift) & mask == b] # @line L8
return s[0] # @line L9
test_radix_select.py 36 lines
# SPDX-License-Identifier: MIT
# Part of https://algorithm.tamnd.com/radix-select/. The authors are listed in prov/ZPSQHH.jsonl.
import random
import pytest
from radix_select import radix_select
def test_example() -> None:
a = [31, 41, 59, 26, 53, 58, 97, 93, 23, 84]
assert radix_select(a, 5, r=4, w=7) == 53
assert [radix_select(a, k) for k in range(1, 11)] == sorted(a)
def test_edges() -> None:
assert radix_select([0], 1) == 0
assert radix_select([0, 0, 0], 2) == 0
assert radix_select([7, 7, 3, 7], 1, r=1) == 3
assert radix_select([2**64 - 1, 0, 2**63], 2, r=16) == 2**63
with pytest.raises(ValueError):
radix_select([1, 2], 3)
with pytest.raises(ValueError):
radix_select([1, -2], 1)
def test_random_against_sorted() -> None:
rng = random.Random(4096)
for _ in range(1500):
n = rng.randint(1, 80)
bits = rng.choice([1, 3, 8, 20, 40])
a = [rng.randrange(1 << bits) for _ in range(n)]
k = rng.randint(1, n)
r = rng.choice([1, 2, 3, 4, 8])
assert radix_select(a, k, r=r) == sorted(a)[k - 1]
What the next level needs
- the tests did not run in this build