List<Question> 即可。| 方案 | SQL/代碼 | 優點 | 缺點 |
|---|---|---|---|
| 內存 Fisher-Yates 洗牌 (小題庫) | | 極快、無數據庫壓力、支持複雜邏輯(去重、均衡知識點) | 題庫大時內存壓力 |
| SQL TABLESAMPLE (PostgreSQL/SQL Server) | | 數據庫原生、無需全表排序 | 非真隨機(頁級采樣)、小表不準 |
| 隨機 ID 範圍 + 索引查找 (大題庫通用) | | 精確隨機、利用主鍵索引、極快 | ID 不連續時需調整(如 OFFSET 采樣) |
| ORDER BY RAND() (MySQL 小表) | | 寫法最簡單 | 全表掃描+臨時表排序,**大表極慢** |
public class ExamService {
private readonly List _allQuestions; // 啟動時載入/緩存
private readonly Random _rnd = new Random();
public List GeneratePaper(int count, string[] categories = null) {
IEnumerable pool = _allQuestions;
if (categories != null)
pool = pool.Where(q => categories.Contains(q.Category));
// Fisher-Yates 部分洗牌,只洗前 count 位
var list = pool.ToList();
int n = list.Count;
for (int i = 0; i < count && i < n; i++) {
int j = _rnd.Next(i, n);
(list[i], list[j]) = (list[j], list[i]);
}
return list.Take(count).ToList();
}
}
GroupBy(q => q.Tag) 分組,組內洗牌取定額。**題庫 ≤ 1 萬:全量載入內存 + Fisher-Yates 部分洗牌**,代碼最少、性能最強、業務邏輯最靈活。大題庫用「隨機 ID + 批量查詢」配合 Redis 緩存熱門題目。