題庫 n 道題,需抽取 m (m ≤ n) 道。簡單做法產生 m 個 [0, n-1] 隨機數,但:
將 1..n 視為圍成一圈的 n 人。每輪產生隨機數 round,從當前位置數到 round 號人退出,下輪從下一位繼續。此過程等價於:
k 輪產生的隨機數 round_k,經過前 k-1 輪淘汰調整後,即為原始題庫中的真實索引。維護一個已排序的已抽取索引陣列 intArray。第 i 輪:
temp = rand() % n。intArray[0..i-1](已排序),對每個 intArray[j] ≤ temp,temp++(補償塌陷)。k,將 intArray[k..i-1] 後移,temp 插入 k 處。intArray[k] 即為本輪真實抽中題目索引。int *intArray = malloc(m * sizeof(int));
int i, j, k, temp;
time_t t;
srand((unsigned)time(&t));
for (i = 0; i < m; i++) {
temp = rand() % n;
// 還原真實索引
for (j = 0; j < i; j++) {
if (temp >= intArray[j]) temp++;
else { k = j; break; }
}
if (j == i) k = i; // 插入尾部
// 後移騰位並插入(維持陣列有序)
for (j = i - 1; j >= k; j--)
intArray[j + 1] = intArray[j];
intArray[k] = temp;
// 此處 intArray[k] 為本輪抽中題目索引(0-based)
// 依題號生成試題...
}
// 保留原始抽取順序:額外陣列 intRandArray[m]
// intRandArray[i] = intArray[k] = temp;
free(intArray);
O(m²)(等價插入排序),m ≪ n 時極快。O(m),無額外 O(n) 空間。m/n。C 標準 rand() 品質有限。專業場景建議替換為:
<random> 引擎(std::mt19937 + std::uniform_int_distribution)/dev/urandom, CryptGenRandom)將上述引擎整合至本演算法即得高品質抽題器。