sampler

package
v0.5.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 25, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package sampler 提供一系列高效能的加權抽樣演算法與工具。

本檔案 (weightitem.go) 定義了加權排序與抽樣所需的內部輔助結構。

設計目的:

  • 提供一個輕量的容器,封裝原始索引 (Index) 與計算後的隨機分數 (Score)。
  • 支援 WeightedShuffle 與 WeightedReservoirSample 中的排序與堆積操作。

結構說明:

  • weightItem: 基本單元。
  • weightHeap: 實作 heap.Interface 的 Max-Heap,用於K抽樣的動態維護。

注意:如果某個weights中某一個weight = 0 ,則在WeightedShuffle當中會被排到最後,但K抽樣則永不入選

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Shuffle

func Shuffle[T any](c *core.Core, src []T)

Shuffle 使用 Fisher-Yates (亦稱 Knuth Shuffle) 演算法 對泛型切片進行「就地 (In-place)」隨機重排。

演算法特性:

  1. 公平性 (Unbiased): 此算法保證所有可能的 N! 種排列組合出現的機率是嚴格相等的 (1/N!)。 這解決了傳統 "Naive Shuffle" (每個位置都隨機跟任意位置交換) 導致的機率偏差問題。

  2. 效能 (High Performance): - 時間複雜度:O(N),只需要對陣列進行一次線性掃描。 - 空間複雜度:O(1),直接在原記憶體位置交換,實現零配置 (Zero Allocation)。

使用場景:

適用於任何需要公平洗牌的邏輯,如撲克牌洗牌、滾輪符號隨機排列、抽獎池打亂等。

func WeightedSample

func WeightedSample(c *core.Core, weights []int, k int) []int

WeightedSample 加權不放回抽樣 - 只取前 K 個 (Weighted Reservoir Sampling)

演算法:Efraimidis-Spirakis Algorithm A-Res

核心邏輯:

維護一個容量為 K 的「領獎台」(Reservoir),裡面存放著目前分數最小的 K 個元素。
使用 Max-Heap 來實作這個領獎台,讓我們能以 O(1) 找到這 K 個人裡面「分數最大」(最該被淘汰) 的人。

處理流程:

  1. 遍歷所有元素,權重 < 0 Panic,權重 == 0 跳過。
  2. 維護一個大小不超過 K 的 Heap。
  3. 最終從 Heap 彈出結果。

相比 WeightedShuffle 的優勢:

  1. 空間複雜度僅為 O(K):不需要分配 N 大小的記憶體,對 GC 極度友善 (當 N=10000, K=3 時差異巨大)。
  2. 時間複雜度為 O(N log K):當 K << N 時,比全排序快得多。

適用場景:選取少量項目,且容易抽不到。

func WeightedShuffle

func WeightedShuffle(c *core.Core, weights []int) []int

WeightedShuffle 加權不放回抽樣 - 全排列 (Weighted Shuffle without Replacement)

演算法:Efraimidis-Spirakis Algorithm A-ExpJ 參考文獻:2006, "Weighted random sampling with a reservoir"

核心邏輯:

  1. 為每個元素 i 生成一個特徵分數 $k_i = U_i^(1/w_i)$。 為了數值穩定與效能,實作上使用 Log 轉換: $Score_i = -ln(U_i) / w_i$。 其中 $-ln(U_i)$ 即為標準指數分佈 (ExpFloat64)。
  2. 權重 $w_i$ 越大,分母越大,分數 $Score_i$ 越小。
  3. 將所有元素按 Score 由小到大排序。
  4. 排序後的順序即為加權隨機排列的結果。

特殊處理:

  • 權重 < 0:Panic (視為錯誤)。
  • 權重 == 0:分數設為 +Inf,這保證它們會被排在列表的最後面。

適用場景:

  • 需要取得完整的隨機排列(如:洗牌、滾輪條生成)。
  • 抽取的數量 K 接近總數 N。

複雜度:

  • 時間:O(N log N) (瓶頸在排序)
  • 空間:O(N) (需要存儲所有元素的分數)

func WeightedShuffleWithFilter

func WeightedShuffleWithFilter(c *core.Core, weights []int) []int

WeightedShuffleWithFilter 加權不放回抽樣 - 全排列但過濾零權重

這是 WeightedShuffle 的變體,專門用於需要「排除無法選中項目」的場景。

行為差異:

  • WeightedShuffle: 回傳長度 N,權重為 0 者排在最後。
  • WeightedShuffleWithFilter: 回傳長度 M (M <= N),僅包含權重 > 0 的項目。

適用場景:

  • 抽樣結果不應包含無效項目(例如:只列出有中獎的獎項順序)。

Types

type AliasTable

type AliasTable struct {
	Prob    []int `json:"prob"`
	Aliases []int `json:"aliases"`
	Size    int   `json:"size"`
	Total   int   `json:"total"`
}

AliasTable 是 Vose Alias Method 的一種 O(1) 加權抽樣結構,適用於從離散分布中快速抽樣。 此版本為「整數版本」,使用整數 scaling 來避免浮點數計算中的精度問題與誤差累積。

結構欄位說明: - Prob: 存放每個元素的「調整後機率」,整數形式,經過 scaling。 - Aliases: 別名索引,用於處理機率不足的元素,指向補足機率的元素。 - Size: 欄位數量,即元素數量。 - Total: 權重總和,用於 scaling 與抽樣判斷。

演算法原理:

  • 將任意離散分佈轉換為均勻分佈的組合。
  • 每個槽位 (Bucket) 只存放「自己」和「別名 (Alias)」兩個選項。
  • 抽樣時先選槽位,再根據機率決定是自己還是別名。

特性:

  • 建表時間:O(N),線性時間。
  • 抽樣時間:O(1),穩定且高效。 *固定作2次IntN亂數*
  • 空間複雜度:O(N),與選項數量成正比,**與權重總和無關**。

適用場景:

  • 權重總和非常大 (如 > 100,000) 或權重差異懸殊。
  • 選項數量較多。
  • 需要穩定的記憶體開銷 (不會因為企劃調整權重數值而暴增記憶體)。

實作細節:

  • 採用全整數運算 (Integer Scaling),避免浮點數精度誤差 (0.999... != 1.0)。
  • 內建溢位檢查 (Safe Multiply),確保在大數權重下安全運作。

func BuildAliasTable

func BuildAliasTable(weights []int) *AliasTable

BuildAliasTable 根據輸入的權重(weights)建立 AliasTable。

輸入 weights 說明: - weights 為任意非負整數權重陣列,不需事先正規化。 - 權重可為零,但全部為零會 panic。

處理流程說明:

- 計算 total 為所有權重之和,若有負權重或 total == 0 則 panic。 - 檢查 weights 長度與 total 相乘是否會溢位,避免 int64 overflow。 - 使用兩個 bucket:small 與 large,分別存放 scaled 權重小於 total 及大於等於 total 的索引。 - 透過 small 與 large 兩桶交互調整 prob 與 aliases,完成 alias table 建立。

演算法流程條列: 1) 將每個權重 w 乘以 n(元素數量)做整數 scaling,得到 prob。 2) 分類索引到 small 或 large,依 prob[i] 與 total 比較。 3) 從 small 和 large 各取一個元素 s, l,將 l 指派為 s 的 alias,並調整 l 的 prob。 4) 重複直到 small 或 large 空。 5) 返回建好的 AliasTable 結構。

func (*AliasTable) Pick

func (at *AliasTable) Pick(c *core.Core) int

Pick 從 AliasTable 中抽取一個索引,若表為空則回傳 -1。

抽樣步驟說明:

1) 使用 c.IntN(Size) 隨機選擇一個欄位 idx。

2) 使用 c.IntN(Total) 隨機投票,判斷是否直接選擇 idx,或使用其 alias。

3) 判斷條件為 IntN(Total) < Prob[idx],此為整數版的機率比較。

數學推導簡述:

  • Prob[idx] = weight[idx] * Size,為整數 scaling 後的機率值。
  • 浮點版本為 U < p[idx],U 為 [0,1) 均勻隨機數,p[idx] 為機率。
  • 將 U 與 p[idx] 放大為整數比較,避免浮點誤差。

此方法完全用整數運算,不經過 float64 浮點計算,避免原演算法的誤差累積,確保抽樣正確性。

type AliasTableF64 added in v0.5.3

type AliasTableF64 struct {
	Prob    []float64 `json:"prob"`
	Aliases []int     `json:"aliases"`
	Size    int       `json:"size"`
}

func BuildAliasTableF64 added in v0.5.3

func BuildAliasTableF64(weights []float64) *AliasTableF64

func (*AliasTableF64) Pick added in v0.5.3

func (at *AliasTableF64) Pick(c *core.Core) int

type Floaters

type Floaters interface {
	~float32 | ~float64
}

Floaters 定義所有底層實現為浮點數型別的集合

type Integers

type Integers interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

Integers 定義所有底層實現為整數型別的集合

type LUT

type LUT []int

Look-Up Table (LUT) 加權抽樣

LUT 是「以空間換取時間」的加權抽樣: 建表時直接展開所有權重,抽樣時只做一次 IntN

LUT 的時間/空間特性:

  • 建表時間 O(sum(weights)),抽樣 O(1)。

  • 記憶體消耗與權重總和成正比,sum(weights) 很大時不建議使用。

構建函數: BuildLookUpTable(src []Integers) LUT

舉例 :

三個物品,對應權重分別為[3,5,0]

i.e. 權重總和為 8

抽到第一個物品(idx = 0)的機率為 3/8 抽到第二個物品的機率為 5/8 抽到第三個物品的機率為 0/8

LUT 轉換展開 -> [0,0,0,1,1,1,1,1] 這樣只要從Slice當中直接取一個值,就符合抽樣

建議使用情境:

  • LUT:權重總和(acc)較小、元素數量不大,且需要非常快速的抽樣時使用。
  • AliasTable:權重總和可能很大、需要穩定 O(1) 抽樣且避免巨大切片時,優先選用 AliasTable。

基本判斷原則: 總和在 100_000 以下建議使用LUT,超過則建議AliasTable

func BuildLUT

func BuildLUT[T Integers](src []T) LUT

BuildLUT 根據權重列表建立查找表。

src 為任意非負整數權重列表(支援各種 Integers 約束),若遇到負權重會 panic。

建表流程: 1. 先累加 acc 取得權重總和,用來預先配置 lut 容量。 2. 對每個元素 i,將其索引重複寫入 lut v 次(v 為權重)。

LUT 的時間/空間特性:

  • 建表時間 O(sum(weights)),抽樣 O(1)。
  • 記憶體消耗與權重總和成正比,sum(weights) 很大時不建議使用。

func (LUT) Pick

func (l LUT) Pick(c *core.Core) int

Pick 會透過 Core 的 RNG 從 LUT 中隨機位置取一個值 若 lut 為空,回傳 -1 LUT 抽樣與是 O(1)

type Numbers

type Numbers interface {
	Integers | Floaters
}

Numbers 定義所有底層實現為數值型別的集合(整數與浮點數)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL