1. Go to this page and download the library: Download niktomo/weighted-sample library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
niktomo / weighted-sample example snippets
use WeightedSample\Pool\WeightedPool;
// Associative array — any key names work
$pool = WeightedPool::of(
[
['name' => 'SSR', 'weight' => 1],
['name' => 'SR', 'weight' => 9],
['name' => 'R', 'weight' => 90],
],
static fn (array $item): int => $item['weight'],
);
$result = $pool->draw(); // returns one item — 90% chance of R, 9% SR, 1% SSR
use WeightedSample\Pool\WeightedPool;
// Object with a method
$pool = WeightedPool::of($prizes, static fn (Prize $p): int => $p->rarityWeight());
// Scalar — item IS the weight
$pool = WeightedPool::of([10, 30, 60], static fn (int $w): int => $w);
use WeightedSample\Pool\BoxPool;
// static fn (array $item): int => weight, static fn (array $item): int => stock
$pool = BoxPool::of(
$items,
static fn (array $item): int => $item['weight'], // draw probability
static fn (array $item): int => $item['stock'], // how many times it can be drawn
);
use WeightedSample\Pool\BoxPool;
$items = [
['name' => 'A', 'weight' => 10, 'stock' => 1],
['name' => 'B', 'weight' => 20, 'stock' => 3],
['name' => 'C', 'weight' => 20, 'stock' => 3],
['name' => 'D', 'weight' => 20, 'stock' => 3],
['name' => 'E', 'weight' => 30, 'stock' => 5],
['name' => 'F', 'weight' => 40, 'stock' => 10],
['name' => 'G', 'weight' => 30, 'stock' => 5],
['name' => 'H', 'weight' => 30, 'stock' => 5],
['name' => 'I', 'weight' => 40, 'stock' => 10],
['name' => 'J', 'weight' => 40, 'stock' => 10],
];
$newBox = static fn (): BoxPool => BoxPool::of(
$items,
static fn (array $item): int => $item['weight'],
static fn (array $item): int => $item['stock'],
);
// The box holds 55 prizes. A player draws 50 at a time, 3 rounds.
//
// Round 1 (draws 1– 50): 50 succeed — 5 remain in the current box
// Round 2 (draws 51– 55): box empties after 5 draws — a fresh box is opened (55 prizes)
// (draws 56–100): 45 draws from the new box — 10 remain
// Round 3 (draws 101–110): box empties after 10 draws — a fresh box is opened (55 prizes)
// (draws 111–150): 40 draws from the new box — 15 remain
$pool = $newBox();
for ($round = 1; $round <= 3; $round++) {
$drawn = [];
for ($i = 0; $i < 50; $i++) {
if ($pool->isEmpty()) {
$pool = $newBox(); // open a fresh box and continue drawing
}
$drawn[] = $pool->draw();
}
// process $drawn for this round
}
use WeightedSample\Pool\BoxPool;
$pool = BoxPool::of($items, static fn (array $i): int => $i['weight'], static fn (array $i): int => $i['stock']);
$prizes = $pool->drawMany(10); // up to 10 items; fewer if pool runs dry
use WeightedSample\Pool\WeightedPool;
// weight=0 items are excluded automatically — no exception thrown
$pool = WeightedPool::of($items, static fn (array $item): int => $item['weight']);
use WeightedSample\Filter\StrictValueFilter;
use WeightedSample\Pool\WeightedPool;
// Throws InvalidArgumentException if any item has weight ≤ 0
$pool = WeightedPool::of(
$items,
static fn (array $item): int => $item['weight'],
filter: new StrictValueFilter(),
);
use WeightedSample\Filter\CompositeFilter;
use WeightedSample\Filter\ItemFilterInterface;
use WeightedSample\Filter\PositiveValueFilter;
use WeightedSample\Pool\WeightedPool;
$enabledFilter = new class implements ItemFilterInterface {
public function accepts(mixed $item, int $weight): bool
{
return $item['enabled'] === true;
}
};
$pool = WeightedPool::of(
$items,
static fn (array $item): int => $item['weight'],
filter: new CompositeFilter([new PositiveValueFilter(), $enabledFilter]),
);
use WeightedSample\Filter\CountedItemFilterInterface;
use WeightedSample\Pool\BoxPool;
$activeFilter = new class implements CountedItemFilterInterface {
public function accepts(mixed $item, int $weight): bool
{
return $item['enabled'] === true && $weight > 0;
}
public function acceptsWithCount(mixed $item, int $weight, int $count): bool
{
return $this->accepts($item, $weight) && $count > 0;
}
};
$pool = BoxPool::of(
$items,
static fn (array $item): int => $item['weight'],
static fn (array $item): int => $item['stock'],
filter: $activeFilter,
);
use InvalidArgumentException;
use WeightedSample\Exception\EmptyPoolException;
use WeightedSample\Pool\WeightedPool;
// Construction-time: InvalidArgumentException when no items survive the filter.
// PositiveValueFilter (the default) excludes any item whose weight is 0.
try {
$pool = WeightedPool::of($items, static fn (array $item): int => $item['weight']);
} catch (InvalidArgumentException $e) {
// Bad input — every item was excluded. Check data or filter config.
return;
}
// Runtime: EmptyPoolException when the pool is exhausted.
try {
while (true) {
$winner = $pool->draw();
}
} catch (EmptyPoolException $e) {
// Pool is exhausted — all items have been drawn.
}
use WeightedSample\Pool\WeightedPool;
// Read from a database cursor without buffering all rows into an array first
function itemsFromDb(\PDOStatement $stmt): \Generator
{
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
yield $row;
}
}
$pool = WeightedPool::of(itemsFromDb($stmt), static fn (array $row): int => $row['weight']);
use WeightedSample\Pool\WeightedPool;
use WeightedSample\Randomizer\SeededRandomizer;
$pool = WeightedPool::of(
$items,
static fn (array $item): int => $item['weight'],
randomizer: new SeededRandomizer(42), // fixed seed
);
// Always produces the same sequence for seed=42
$result = $pool->draw();
// Bind the seed to a specific entity for per-entity reproducibility.
// The same userId + campaignId always produces the same draw sequence:
$seed = (int) hexdec(substr(hash('sha256', $userId . ':' . $campaignId), 0, 8));
// If you need a one-off reproducible sequence, generate the seed once, persist it,
// then reuse it to replay the exact same draws later:
$seed = unpack('N', random_bytes(4))[1]; // store this value — it is the key to replay
// Note: if replay is not needed, use SecureRandomizer (the default) instead.
use WeightedSample\Builder\FenwickSelectorBundleFactory;
use WeightedSample\Filter\StrictValueFilter;
use WeightedSample\Pool\BoxPool;
use WeightedSample\Pool\WeightedPool;
use WeightedSample\Randomizer\SeededRandomizer;
use WeightedSample\Selector\AliasTableSelectorFactory;
// Change the randomizer (e.g. for tests)
$pool = WeightedPool::of($items, static fn (array $i): int => $i['weight'],
randomizer: new SeededRandomizer(42),
);
// Change the filter
$pool = WeightedPool::of($items, static fn (array $i): int => $i['weight'],
filter: new StrictValueFilter(),
);
// Change the selector algorithm (WeightedPool)
$pool = WeightedPool::of($items, static fn (array $i): int => $i['weight'],
selectorFactory: new AliasTableSelectorFactory(),
);
// Change the builder strategy (BoxPool)
$pool = BoxPool::of($items, static fn (array $i): int => $i['weight'], static fn (array $i): int => $i['stock'],
bundleFactory: new FenwickSelectorBundleFactory(), // default; shown for clarity
);
use WeightedSample\Pool\WeightedPool;
use WeightedSample\Pool\BoxPool;
use WeightedSample\Selector\AliasTableSelectorFactory;
use WeightedSample\Builder\FenwickSelectorBundleFactory;
use WeightedSample\Builder\RebuildSelectorBundleFactory;
// WeightedPool with many repeated draws — use Alias for O(1) pick
$pool = WeightedPool::of(
$items,
static fn (array $item): int => $item['weight'],
selectorFactory: new AliasTableSelectorFactory(),
);
// BoxPool with large N — FenwickSelectorBundleFactory is the default (O(n log n) total)
$pool = BoxPool::of(
$items,
static fn (array $item): int => $item['weight'],
static fn (array $item): int => $item['stock'],
bundleFactory: new FenwickSelectorBundleFactory(),
);
// BoxPool with AliasTable pick (O(1)) — use RebuildSelectorBundleFactory
$pool = BoxPool::of(
$items,
static fn (array $item): int => $item['weight'],
static fn (array $item): int => $item['stock'],
bundleFactory: new RebuildSelectorBundleFactory(new AliasTableSelectorFactory()),
);