故事得從一位留學生的煩惱講起。某大領導的公子在美國名校攻讀計算機科學,本想潇灑度日、混張文憑回國,卻遭遇嚴厲教授布置的經典作業——模擬賭場經典骰子遊戲 Craps(克拉普斯) 的首輪勝率。無奈之下,領導動用關係,這筆「私活」便落到了筆者頭上。
Craps 使用兩顆六面骰子,首輪擲骰稱為「Come-out Roll」:
void diceRoll(int *d1, int *d2) 函數,利用 rand() 產生兩顆獨立骰子的點數(1~6)。#include <stdio.h>
#include <stdlib.h>
#define ROLLS 10000
void diceRoll(int *d1, int *d2) {
*d1 = 1 + rand() % 6;
*d2 = 1 + rand() % 6;
}
int main(void) {
unsigned seed;
int win = 0, lose = 0;
printf("Enter an integer seed value: ");
scanf("%u", &seed);
srand(seed);
for (int i = 0; i < ROLLS; i++) {
int d1, d2, sum;
diceRoll(&d1, &d2);
sum = d1 + d2;
if (sum == 7 || sum == 11) win++;
else if (sum == 2 || sum == 3 || sum == 12) lose++;
}
printf("Number of wins: %d\n", win);
printf("Probability of winning: %.4f\n", (double)win / ROLLS);
printf("Number of losses: %d\n", lose);
printf("Probability of losing: %.4f\n", (double)lose / ROLLS);
return 0;
}
作業要求輸入種子,確保結果可驗證。srand() 僅需呼叫一次,切勿放在迴圈內。
原始碼片段使用 i <= ROLLS 會多跑一次(10001 次),正確寫法為 i < ROLLS。
整數相除會截斷,務必轉型為 double 或 float 再除,並以 %.4f 輸出四位小數,方便與理論值對照。
diceRoll 只負責產生點數,不處理勝負邏輯,符合「單一職責原則」,便於測試與維護。
Enter an integer seed value: 1
Number of wins: 2230
Probability of winning: 0.2230
Number of losses: 1098
Probability of losing: 0.1098
Enter an integer seed value: 102
Number of wins: 2258
Probability of winning: 0.2258
Number of losses: 1072
Probability of losing: 0.1072
結果隨種子波動,但隨模擬次數增加會逼近理論值(大數法則)。
/dev/urandom 或 C11 <random> 取代 rand() 取得更高品質亂數。這道看似簡單的作業,實則涵蓋亂數產生、模組化設計、統計模擬、浮點精度等核心觀念。掌握這類「小程式、大概念」的練習,是從寫代碼邁向工程思維的關鍵一步。