using System;
using System.Collections.Generic;
using System.Linq;
namespace ScheduleGenerator {
class Program {
static void Main() {
string[] courses = { "體育課", "體育課", "美術課", "音樂課", "勞動課",
"語文課", "數學課", "英語課" };
string[] weekDays = { "一", "二", "三", "四", "五" };
// 5天 × 6節
var schedule = Enumerable.Range(0, 5)
.Select(_ => Enumerable.Range(0, 6).Select(__ => "").ToArray())
.ToArray();
Random rnd = new Random();
// 滾動生成直到滿足所有約束
while (!schedule.All(day =>
day.Contains("語文課") &&
day.Contains("數學課") &&
day.Contains("英語課") &&
day.Contains("自修課") &&
day[0] != "體育課")) { // 關鍵:day[0] 不是體育課
// 打亂 30 個位置索引
var shuffledIndices = Enumerable.Range(0, 30).OrderBy(_ => rnd.Next()).ToArray();
// 核心課程固定分配策略
for (int day = 0; day < 5; day++) {
int pos = 0;
string[] core = { "語文課", "數學課", "英語課" };
for (int period = 0; period < 6; period++) {
int flatIdx = day * 6 + period;
// 如果該位置在打亂序列中有預置課程
if (Array.IndexOf(shuffledIndices, flatIdx) < courses.Length) {
int courseIdx = Array.IndexOf(shuffledIndices, flatIdx);
schedule[day][period] = courses[courseIdx];
} else {
// 填充核心課程或自修
schedule[day][period] = (pos < 3) ? core[pos++] : "自修課";
}
}
}
// 同課程連排:按是否自修分組,組內隨機,組間保持
schedule = schedule.Select((day, d) =>
new[] { "星期" + weekDays[d] }
.Concat(day
.OrderBy(c => c == "自修課" ? 1 : 0) // 自修排後
.ThenBy(_ => rnd.Next()) // 組內隨機
.GroupBy(c => c) // 同課程分組
.SelectMany(g => g) // 展平:同課程連在一起
).ToArray()
).ToArray();
}
// 輸出:按節次橫向顯示
for (int period = 0; period < 7; period++) { // 含表頭行
for (int day = 0; day < 5; day++) {
Console.Write(schedule[day][period] + "\t");
}
Console.WriteLine();
}
}
}
}
| 原問題 | 修正 |
|---|---|
| 體育課出現在第 1 節 | `day[0] != "體育課"` 加入驗證條件 |
| Guid.NewGuid() 作為隨機源 | 改用 `Random` 實例,性能更好 |
| Zip 語法複雜且易錯 | 簡化為索引打亂 + 直接賦值 |
| 核心課程分配邏輯不清晰 | 明確 `pos` 計數器填充語數英 |
星期一 星期二 星期三 星期四 星期五
語文課 數學課 英語課 語文課 數學課
數學課 語文課 數學課 英語課 語文課
英語課 英語課 語文課 數學課 英語課
體育課 體育課 美術課 音樂課 勞動課
美術課 音樂課 自修課 自修課 自修課
自修課 自修課 自修課 自修課 自修課
LINQ 鏈式操作表達力強,但要注意邏輯順序與邊界條件,隨機算法需配合驗證循環保證硬約束。