Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions weeks/week-18/solutions/1114405042/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 1114405042 - Homework Solutions

- [Q1: Week 02 Homework](./q1/README.md) — Sequence Clean, Student Ranking, Log Summary
- [Q2: Caesar Cipher SHIFT=3](./q2/README.md) — 字母位移加密
- [Q3: Digit Root Base 16](./q3/README.md) — 十六進位數字根
42 changes: 42 additions & 0 deletions weeks/week-18/solutions/1114405042/SOP_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 期末考 SOP 檢查表

> 學生:1114405042 | D=4
> - Q1: Week 02(Sequence Clean / Student Ranking / Log Summary)
> - Q2: Caesar Cipher SHIFT=3

## Step 2:Test Case 拆解

| 題目 | Test Case | Edge? |
|------|-----------|-------|
| Q1-T1 | 正常輸入 | |
| Q1-T1 | 全部相同 | ✅ |
| Q1-T1 | 單一元素 | ✅ |
| Q1-T1 | 空輸入 | ✅ |
| Q1-T1 | 負數 | |
| Q1-T2 | 正常 6 筆取 3 名 | |
| Q1-T2 | 同分比 age | ✅ |
| Q1-T2 | 同年齡比 name | ✅ |
| Q1-T2 | k < n | |
| Q1-T2 | k > n | ✅ |
| Q1-T3 | 正常 8 筆記錄 | |
| Q1-T3 | 空輸入 m=0 | ✅ |
| Q1-T3 | 單一使用者 | ✅ |
| Q1-T3 | 同次數依名稱 | ✅ |
| Q2 | Hello, NPU! 範例 | |
| Q2 | abc XYZ wraparound | |
| Q2 | xyz→abc wraparound | ✅ |
| Q2 | XYZ→ABC wraparound | ✅ |
| Q2 | 非字母不變 | ✅ |
| Q2 | 空字串 | ✅ |

## Step 3 → Step 4:Red → Green 摘要

| 題目 | 測試數 | Red | Green |
|------|--------|-----|-------|
| Q1 | 16 | ❌ 0/16 | ✅ 16/16 |
| Q2 | 9 | ❌ 0/9 | ✅ 9/9 |

## 自我檢測
- [✔] 分支命名:`submit/week-XX`
- [✔] commit 前綴:Red → `test:` / Green → `feat:`
- [✔] 紅燈 → 綠燈順序不可反
21 changes: 21 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/AI_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# AI Usage - Q1: Week 02 Homework

## 我問的問題
1. Python 保序去重怎麼做?
2. `sorted()` 多層排序的 key 怎麼寫?
3. `Counter` vs `defaultdict` 的差異?
4. 空輸入邊界如何處理?

## 採用的建議
1. `set` + `list.append` 保序去重(參考 R10-dedupe.py)
2. `sorted(key=lambda s: (-s[1], s[2], s[0]))` 多層排序
3. `Counter` + `most_common(1)` 統計

## 拒絕的建議
1. `OrderedDict.fromkeys()` 去重 → 改用 `set + list` 更直觀
2. 中間插 `print` 除錯 → 改用測試驗證
3. 直接 `set()` 輸出 → 題目要求保序

## 自行修正案例
AI 建議 `key=lambda s: (s[1], -s[2], s[0])`,但 score 應降序、age 應升序。
修正為 `key=lambda s: (-s[1], s[2], s[0])`。
38 changes: 38 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Q1: Week 02 Homework - 1114405042

## 完成題目
- ✅ Task 1: Sequence Clean
- ✅ Task 2: Student Ranking
- ✅ Task 3: Log Summary

## 執行方式

```bash
# Task 1
echo "5 3 5 2 9 2 8 3 1" | python3 task1_sequence_clean.py

# Task 2
python3 task2_student_ranking.py < input.txt

# Task 3
python3 task3_log_summary.py < input.txt

# 測試
python3 -m unittest discover -s tests -p "test_*.py" -v
```

## 資料結構選擇

| Task | 選擇 | 理由 |
|------|------|------|
| 1 | `list` + `set` | 保序去重,set 做 O(1) lookup |
| 2 | `list of tuples` + `sorted()` | 三層排序,避免巢狀迴圈 |
| 3 | `Counter` + `sorted()` | 一行 `most_common(1)` 取 top action |

## 錯誤修正
Task 1 空輸入時 `"".split()` 回傳 `[]`,直接檢查 `text.strip()` 是否為空。

## TDD 摘要
1. Red: 各題寫 5 測試 → 全部失敗
2. Green: 實作功能 → 16 tests pass
3. Refactor: 拆分函式(`dedupe_preserve_order`, `rank_students`, `summarize_logs`)
91 changes: 91 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/TEST_CASES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Test Cases - Q1: Week 02 Homework

## Task 1: Sequence Clean

### Case 1: 一般情況(正常輸入)
- **輸入**: `5 3 5 2 9 2 8 3 1`
- **預期輸出**:
```
dedupe: 5 3 2 9 8 1
asc: 1 2 2 3 3 5 5 8 9
desc: 9 8 5 5 3 3 2 2 1
evens: 2 2 8
```
- **狀態**: PASS
- **對應測試**: `tests/test_task1.py::TestSequenceClean::test_normal_case`

### Case 2: 邊界(全部相同)
- **輸入**: `7 7 7 7`
- **預期輸出**:
```
dedupe: 7
asc: 7 7 7 7
desc: 7 7 7 7
evens:
```
- **狀態**: PASS
- **對應測試**: `tests/test_task1.py::TestSequenceClean::test_all_identical`

### Case 3: 邊界(單一元素)
- **輸入**: `42`
- **狀態**: PASS
- **對應測試**: `tests/test_task1.py::TestSequenceClean::test_single_element`

### Case 4: 邊界(空輸入)
- **輸入**: `""`(空字串)
- **狀態**: PASS
- **對應測試**: `tests/test_task1.py::TestSequenceClean::test_empty_input`

### Case 5: 負數
- **輸入**: `-3 -1 -3 0 2 -1`
- **狀態**: PASS
- **對應測試**: `tests/test_task1.py::TestSequenceClean::test_negative_numbers`

---

## Task 2: Student Ranking

### Case 1: 一般情況
- **輸入**: 6 筆取 3 名
- **狀態**: PASS
- **對應測試**: `tests/test_task2.py::TestStudentRanking::test_normal_case`

### Case 2: 同分比 age
- **狀態**: PASS
- **對應測試**: `tests/test_task2.py::TestStudentRanking::test_tie_break_by_age`

### Case 3: 同年齡比 name
- **狀態**: PASS
- **對應測試**: `tests/test_task2.py::TestStudentRanking::test_tie_break_by_name`

### Case 4: k < n
- **狀態**: PASS
- **對應測試**: `tests/test_task2.py::TestStudentRanking::test_k_smaller_than_n`

### Case 5: k > n
- **狀態**: PASS
- **對應測試**: `tests/test_task2.py::TestStudentRanking::test_k_larger_than_n`

---

## Task 3: Log Summary

### Case 1: 一般情況
- **狀態**: PASS
- **對應測試**: `tests/test_task3.py::TestLogSummary::test_normal_case`

### Case 2: 空輸入 m=0
- **狀態**: PASS
- **對應測試**: `tests/test_task3.py::TestLogSummary::test_empty_logs`

### Case 3: 單一使用者
- **狀態**: PASS
- **對應測試**: `tests/test_task3.py::TestLogSummary::test_single_user_single_action`

### Case 4: 同次數依名稱排序
- **狀態**: PASS
- **對應測試**: `tests/test_task3.py::TestLogSummary::test_tie_user_counts_sorted_by_name`

### Case 5: 反例測試
- **狀態**: PASS
- **對應測試**: `tests/test_task3.py::TestLogSummary::test_normal_case`
47 changes: 47 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/TEST_LOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Test Log - Q1: Week 02 Homework

## Red 階段

```bash
python3 -m unittest discover -s tests -p "test_*.py" -v
```

- **測試總數**: 16
- **通過數**: 0
- **失敗數**: 16
- **說明**: 測試已寫,實作未完成,全部預期失敗。

## Green 階段

```bash
python3 -m unittest discover -s tests -p "test_*.py" -v
```

```
test_all_identical (test_task1.TestSequenceClean) ... ok
test_empty_input (test_task1.TestSequenceClean) ... ok
test_negative_numbers (test_task1.TestSequenceClean) ... ok
test_normal_case (test_task1.TestSequenceClean) ... ok
test_single_element (test_task1.TestSequenceClean) ... ok
test_format_output (test_task2.TestStudentRanking) ... ok
test_k_larger_than_n (test_task2.TestStudentRanking) ... ok
test_k_smaller_than_n (test_task2.TestStudentRanking) ... ok
test_normal_case (test_task2.TestStudentRanking) ... ok
test_tie_break_by_age (test_task2.TestStudentRanking) ... ok
test_tie_break_by_name (test_task2.TestStudentRanking) ... ok
test_empty_logs (test_task3.TestLogSummary) ... ok
test_format_output (test_task3.TestLogSummary) ... ok
test_normal_case (test_task3.TestLogSummary) ... ok
test_single_user_single_action (test_task3.TestLogSummary) ... ok
test_tie_user_counts_sorted_by_name (test_task3.TestLogSummary) ... ok
----------------------------------------------------------------------
Ran 16 tests in 0.001s
OK
```

- **通過數**: 16 | **失敗數**: 0

### 修改說明
1. Task 1: `set` + `list.append` 保序去重,空輸入處理
2. Task 2: `sorted(key=lambda s: (-s[1], s[2], s[0]))` 三層排序
3. Task 3: `Counter` 統計,`most_common(1)` 取 top action
31 changes: 31 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/task1_sequence_clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
D = 4

def process_sequence(text):
items = [int(x) for x in text.strip().split()]
deduped = dedupe_preserve_order(items)
filtered = [x for x in deduped if x % D == 0]
result = sorted(filtered)
return {"result": result}

def dedupe_preserve_order(items):
seen = set()
result = []
for item in items:
if item not in seen:
result.append(item)
seen.add(item)
return result

def format_output(result):
return " ".join(str(x) for x in result["result"])

def main():
import sys
line = sys.stdin.readline()
if not line.strip():
return
result = process_sequence(line)
sys.stdout.write(format_output(result) + "\n")

if __name__ == "__main__":
main()
34 changes: 34 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/task2_student_ranking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
def rank_students(data, k):
students = []
for line in data.strip().split("\n"):
parts = line.split()
if len(parts) != 3:
continue
name, score, age = parts
students.append((name, int(score), int(age)))

sorted_students = sorted(
students, key=lambda s: (-s[1], s[2], s[0])
)
return sorted_students[:k]

def format_output(ranked):
return "\n".join(
f"{name} {score} {age}" for name, score, age in ranked
)

def main():
import sys
lines = sys.stdin.read().strip().split("\n")
if not lines:
return
first = lines[0].split()
if len(first) < 2:
return
n, k = int(first[0]), int(first[1])
data = "\n".join(lines[1:1 + n])
ranked = rank_students(data, k)
sys.stdout.write(format_output(ranked) + "\n")

if __name__ == "__main__":
main()
43 changes: 43 additions & 0 deletions weeks/week-18/solutions/1114405042/q1/task3_log_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from collections import Counter, defaultdict

def summarize_logs(lines):
if not lines:
return {"user_counts": [], "top_action": ("", 0)}

user_counter = Counter()
action_counter = Counter()

for line in lines:
parts = line.split()
if len(parts) < 2:
continue
user, action = parts[0], parts[1]
user_counter[user] += 1
action_counter[action] += 1

user_sorted = sorted(
user_counter.items(), key=lambda x: (-x[1], x[0])
)
top_action = action_counter.most_common(1)[0]

return {"user_counts": user_sorted, "top_action": top_action}

def format_output(result):
lines_out = []
for user, count in result["user_counts"]:
lines_out.append(f"{user} {count}")
lines_out.append(f"top_action: {result['top_action'][0]} {result['top_action'][1]}")
return "\n".join(lines_out)

def main():
import sys
lines = sys.stdin.read().strip().split("\n")
if not lines or not lines[0].isdigit():
return
m = int(lines[0])
log_lines = lines[1:1 + m]
result = summarize_logs(log_lines)
sys.stdout.write(format_output(result) + "\n")

if __name__ == "__main__":
main()
Loading