A high-performance, O(P) data structure for weighted random sampling of binned probabilities, ideal for large-scale simulations.
Project description
DigitBinIndex
A DigitBinIndex is a tree-based data structure designed for efficient weighted random selection and removal from large collections of items. It is optimized for scenarios involving millions of items where probabilities are approximate and high performance is critical, such as simulations for Wallenius' noncentral hypergeometric distribution or Fisher's noncentral hypergeometric distribution.
This library provides high-performance solutions for both major types of noncentral hypergeometric distributions:
- Sequential Sampling (Wallenius'): Modeled by
select_and_remove, where items are selected and removed one at a time. - Simultaneous Sampling (Fisher's): Modeled by
select_many_and_remove, where a batch of unique items is selected and removed together.
The Core Problem
In simulations, forecasts, or statistical models (e.g., mortality models, Monte Carlo simulations, or machine learning sampling), managing a large, dynamic set of probabilities is common. A key task is to randomly select items based on their weights, often removing them afterward, and repeat this process efficiently. For datasets with millions of items, achieving high performance while maintaining reasonable accuracy is a significant challenge, especially for complex distributions like Wallenius' or Fisher's.
How It Works
DigitBinIndex is a radix tree that organizes items into bins based on the decimal digits of their rescaled probabilities, enabling fast weighted random selection and updates.
-
Digit-based Tree Structure: Each level of the tree corresponds to a decimal place of the rescaled weight. For example, a weight of
0.543at precision 3 is rescaled to543and placed by traversing the path:root -> child[5] -> child[4] -> child[3]. -
Roaring Bitmap Bins: Leaf nodes act as bins, storing item IDs in a Roaring Bitmap, a compressed data structure optimized for fast set operations. This enables efficient unique sampling, particularly for Fisher's distribution.
-
Accumulated Value Index: Each node tracks the
accumulated_value(sum of weights beneath it), supporting O(P) weighted random selection, where P is the configured precision (number of decimal places).
Features
- High Performance: Outperforms general-purpose data structures like Fenwick Trees for both sequential and simultaneous weighted sampling.
- Dual-Model Support: Optimized methods for Wallenius' (
select_and_remove) and Fisher's (select_many_and_remove) distributions. - O(P) Complexity: Core operations (add, remove, select) have a time complexity of O(P), where P is the fixed precision, effectively constant for a given configuration.
- Memory Efficiency: Combines a sparse radix tree with Roaring Bitmaps for efficient storage, especially for sparse or clustered weight distributions.
- Python Integration: Seamless Python bindings via
pyo3for cross-language support.
Performance
DigitBinIndex trades a small, controllable amount of precision by binning probabilities to achieve significant performance gains. Below are benchmarks comparing DigitBinIndex against an optimized Fenwick Tree implementation, run on a standard desktop (Intel i7, 16GB RAM, Rust 1.75) with typical weight distributions.
Wallenius' Draw (Sequential Selections)
Measures the total time for 1,000 select_and_remove operations, selecting and removing one item at a time. DigitBinIndex's O(P) complexity provides a significant advantage as dataset size grows.
| Number of Items (N) | DigitBinIndex Loop Time |
FenwickTree Loop Time |
Speedup Factor |
|---|---|---|---|
| 100,000 | ~0.46 ms | ~1.77 ms | ~3.9x faster |
| 1,000,000 | ~0.52 ms | ~13.58 ms | ~26.1x faster |
Fisher's Draw (Simultaneous Selections)
Measures the time to select a batch of unique items (1% of the total population, e.g., k=1,000 for N=100,000). DigitBinIndex uses batched rejection sampling, optimized for efficiency.
| Scenario (N items, draw k) | DigitBinIndex Time |
FenwickTree Time |
Speedup Factor |
|---|---|---|---|
| N=100k, k=1k | ~0.47 ms | ~1.87 ms | ~4.0x faster |
| N=1M, k=10k | ~5.48 ms | ~20.16 ms | ~3.7x faster |
Higher precision settings slightly increase runtime but remain O(P), independent of N. Memory usage depends on the weight distribution and precision, with sparse distributions benefiting most from Roaring Bitmaps.
When to Choose DigitBinIndex
Use DigitBinIndex when:
- You need high-performance sampling for Wallenius' or Fisher's distributions.
- Your dataset is large (N > 100,000).
- Probabilities are approximate, as is common in empirical data, simulations, or machine learning models.
- Performance is more critical than perfect precision.
Consider a Fenwick Tree if you require exact precision and your weights differ only at high decimal places (e.g., 0.12345 vs. 0.12346), though this comes at the cost of O(log N) complexity and higher memory usage for large datasets.
Choosing a Precision
The precision parameter controls the radix tree's depth, balancing accuracy, performance, and memory. Higher precision improves sampling accuracy but increases memory usage (up to 10x per additional level) and slightly impacts runtime.
The Rule of Thumb
A precision of 3 or 4 (default: 3) is recommended for most applications. This captures sufficient detail for typical weight distributions while maintaining excellent performance and low memory usage.
The Mathematical Intuition
Each decimal place contributes exponentially less to a weight’s value. For a weight of 0.12345:
- 1st digit (
1):0.1 - 2nd digit (
2):0.02 - 3rd digit (
3):0.003 - 4th digit (
4):0.0004
Truncating at 3 digits limits the error per item to <0.001. In large populations, these errors average out, minimally affecting the selection distribution.
Guidance
| Precision | Typical Use Case | Trade-offs |
|---|---|---|
| 1-2 | Maximum performance, minimal memory usage. | Best for coarse weights (e.g., 0.1, 0.5). Loses accuracy with fine-grained data. |
| 3-4 | Recommended Default. Optimal for most scenarios. | Captures sufficient detail for simulation or model data. Negligible performance/memory cost. |
| 5+ | High-fidelity scenarios with very close weights. | Distinguishes weights like 0.12345 vs. 0.12346. Increases memory (up to 10x per level) and slightly impacts performance. |
Usage & Installation
DigitBinIndex is available as a Python library on PyPI or as a Rust crate on Crates.io. Ensure Python 3.6+ for Python bindings or Rust 1.75+ for the Rust crate.
For Python 🐍
Install from PyPI:
pip install digit-bin-index
Example usage:
from digit_bin_index import DigitBinIndex
def main():
# Create an index with precision 3 (default).
index = DigitBinIndex(precision=3)
# Add items with IDs and weights.
index.add(id=101, weight=0.123) # Low weight
index.add(id=202, weight=0.800) # High weight
index.add(id=303, weight=0.755) # High weight
index.add(id=404, weight=0.110) # Low weight
# Sequential (Wallenius') Draw: Select and remove one item.
# Higher-weighted items (202, 303) are more likely.
selected_item = index.select_and_remove()
if selected_item:
print(f"Wallenius draw: ID {selected_item[0]}, Weight ~{selected_item[1]}")
print(f"Items remaining: {index.count()}") # 3
# Simultaneous (Fisher's) Draw: Select and remove 2 unique items.
selected_ids = index.select_many_and_remove(2)
if selected_ids:
print(f"Fisher's draw: {selected_ids}")
print(f"Items remaining: {index.count()}") # 1
if __name__ == "__main__":
main()
For Rust 🦀
Add to your Cargo.toml:
[dependencies]
digit-bin-index = "0.2.4" # Replace with the latest version from crates.io
rust_decimal = "1.37"
rust_decimal_macros = "1.37" # Optional, for convenient decimal literals
Example usage:
use digit_bin_index::DigitBinIndex;
use rust_decimal_macros::dec;
fn main() {
// Create an index with precision 3.
let mut index = DigitBinIndex::with_precision(3);
// Add items with IDs and weights.
index.add(101, dec!(0.123)); // Low weight
index.add(202, dec!(0.800)); // High weight
index.add(303, dec!(0.755)); // High weight
index.add(404, dec!(0.110)); // Low weight
// Sequential (Wallenius') Draw: Select and remove one item.
if let Some((id, weight)) = index.select_and_remove() {
println!("Wallenius draw: ID {}, Weight ~{}", id, weight);
}
println!("Items remaining: {}", index.count()); // 3
// Simultaneous (Fisher's) Draw: Select and remove 2 unique items.
if let Some(ids) = index.select_many_and_remove(2) {
println!("Fisher's draw: {:?}", ids);
}
println!("Items remaining: {}", index.count()); // 1
}
License
This project is licensed under the MIT License, a permissive open-source license allowing free use, modification, and distribution.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file digit_bin_index-0.2.4.tar.gz.
File metadata
- Download URL: digit_bin_index-0.2.4.tar.gz
- Upload date:
- Size: 28.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d649212ea5abc1a3a1d8b47a014103ca9ae9ca94e87d298fb971fce101d61e7
|
|
| MD5 |
7e2158ba855f6ad52254ebfbaa6bc81f
|
|
| BLAKE2b-256 |
7499480feb89770fbec201668680b018a59ff632c07e82e01a7cf3e07fe113d1
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-win_amd64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-win_amd64.whl
- Upload date:
- Size: 152.1 kB
- Tags: CPython 3.8+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62930e011ef64c6f23e92fcab0f2e5c101327414b686eec071088fe7a8770150
|
|
| MD5 |
435f2c7afb1a35140f88fac216f7e4e9
|
|
| BLAKE2b-256 |
ad1e74469229fb6ccfd99511247a40e7fd0be308aa765a5727f3448acababb5d
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-win32.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-win32.whl
- Upload date:
- Size: 151.0 kB
- Tags: CPython 3.8+, Windows x86
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ed8f1f8e2e7103072a778cf141970e04d6f2c010ff90fd06843ce24f0ed8f63
|
|
| MD5 |
6fcf5142978d6c7da78da519d06bded6
|
|
| BLAKE2b-256 |
128331ff6789f96d1a9bac1e27389d5a551b7e8fecaac5fa416e431fb26393bc
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 448.9 kB
- Tags: CPython 3.8+, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
402ef34f6e04ce3894783659356414c5fda9f7594bdad6b27a226a2dae718816
|
|
| MD5 |
081526caaa3b387f2c8bfb369746ccd7
|
|
| BLAKE2b-256 |
2fca5acadd231b95048f8c503fc42360fa8e0625a498406757df9dac1f7dbe6c
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_i686.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_i686.whl
- Upload date:
- Size: 476.7 kB
- Tags: CPython 3.8+, musllinux: musl 1.2+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9775acf87d6569e06b0a3c73d029778254721c7e2353b2221c41b9abacbb0655
|
|
| MD5 |
5348e7b54e6172e5ea4c9a5f94f0cf91
|
|
| BLAKE2b-256 |
8549ec819b3a9f124a7b53aa2ef2425957300de046e9deed3f31368e0f65791c
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_armv7l.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_armv7l.whl
- Upload date:
- Size: 551.9 kB
- Tags: CPython 3.8+, musllinux: musl 1.2+ ARMv7l
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3e63d249098a575109b59be5b1435b7ceece977d58e3f2743a130a0b0397482
|
|
| MD5 |
a14f1a786bb2be28be50aae275764b56
|
|
| BLAKE2b-256 |
ad815b7a8f0d2405606407e1e378f310464e336f58ab0d3e7886d3f1bac2b881
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 454.2 kB
- Tags: CPython 3.8+, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b21f2912ca0b3d051a6a20c7eb6e0da275437e0c4a4e91e1cda0077a63a8fd0e
|
|
| MD5 |
5a1078dd26572bf558cb64457e20fd01
|
|
| BLAKE2b-256 |
b24cbc69327032a5da294a6763e2343ea293169981332f2a6351ce5a59e5c556
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 278.1 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21dafb7a8e2b89e57ca7b9ba385370e9ee35e8843ed3604763bef24bf045ce76
|
|
| MD5 |
ec1833f6b38700bab0fe5e4dce78e4b3
|
|
| BLAKE2b-256 |
d3b37b28220784e8bdc79675a988cc8bed046ea00f3981274b0367bd1a8f6d7c
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
- Upload date:
- Size: 323.7 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ s390x
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac3f2a24790d3c7812698598185bee27493824c9b4533d42d45cab3bb2b04841
|
|
| MD5 |
6d6ca4665bc9360a783597eb7f17592e
|
|
| BLAKE2b-256 |
14dbc3def4aec9f8f5b0f58b54c1ac356535c09a236b006f23695f7f60fc1c40
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
- Upload date:
- Size: 311.8 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ ppc64le
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71d3f77a3df8b48aa39feef1fa6baf9711edbdf9865b9f129ce66a7d1524f7a0
|
|
| MD5 |
e7bbca526a331ee31e6d1dbb6057045a
|
|
| BLAKE2b-256 |
32abb7f00c0810265a0511cc5afde9a2d5ff16675c40659e173b051a888c1899
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
- Upload date:
- Size: 288.2 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ ARMv7l
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e4b261dda886ac29dc427d7ca115e0915459b8948f9a8c277c70cf222ec8d56
|
|
| MD5 |
4ed77bd7bbe0febdeb4f8d01e353cf54
|
|
| BLAKE2b-256 |
fb7062c4cbef6e1736e13281af00277ab53c725692a08a9c621f0af962abde34
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 274.3 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
359a71992140bea5ea4dc9bb6927ccac32e53e9a47be95da9f93a30fe49d4652
|
|
| MD5 |
4266577f43de6141ef0c48984f64ce4b
|
|
| BLAKE2b-256 |
13ec8ccd52e9e7076c979bf4da27e588d1e7f3e8601c9f8264ce38d6ac4179af
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl
- Upload date:
- Size: 298.7 kB
- Tags: CPython 3.8+, manylinux: glibc 2.5+ i686
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd0d998f754e97af79d55b97eddec9296d9f59bd1484152d7a03b6951ec2ff1f
|
|
| MD5 |
ba7ce46b04019b0f2cb735cde8888565
|
|
| BLAKE2b-256 |
eb289d903ada813c7ff925d19843a9027303ea2cc2e24f5179dad5152708e760
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 247.5 kB
- Tags: CPython 3.8+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd09858e492bcbd4735e811cfb85f4e24772eb894c81d14a5c90ff3f2350aa08
|
|
| MD5 |
b0ea0c3e93cbd6b7645a5fbc4657c225
|
|
| BLAKE2b-256 |
16ebbbc616b8db242793f3f1594a5fea127d8165620315f239d069cbd6ae60f9
|
File details
Details for the file digit_bin_index-0.2.4-cp38-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: digit_bin_index-0.2.4-cp38-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 258.1 kB
- Tags: CPython 3.8+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01da4aa2e320c91a5196983486d59aa98c44db50d8b0285cca8999b45185d424
|
|
| MD5 |
7e1986e26cc70c5f5abf595e6a1dd57e
|
|
| BLAKE2b-256 |
b4f5c522a9203d57d753cf6efb137a05c519d2facf4e4c699a7d252d94d6844f
|