訂明本例驗收任務
假設分組有不同且非全空白的文字個案編號,原樣比較,並有 development/reserved 分配,及已明示的近似連結,每條恰好兩端點。自連及重複二端連結可留,但不增加連通關係。把連結視作無向關係,計傳遞連通組;每組全部須在同一用途。近似關係由資料提供,不自動估相似度。連結有未登記端點或欠分配便先拒絕。
- 核已列端點及用途分配
- 走無向連結求傳遞連通組
- 每個完整連通組核同一用途
混合用途組須重配或修已列連結,原核對仍留。
使用已填個案
| 個案 | 已提供輸入 | 預期結構化結果 | 判斷及原因 |
|---|---|---|---|
| 間接連結跨用途 | {"cases":[{"id":"A","partition":"development"},{"id":"B","partition":"development"},{"id":"C","partition":"reserved"}],"links":[["A","B"],["B","C"]]} | {"components":[["A","B","C"]],"mixed":[["A","B","C"]],"valid":false} | A 經 B 連 C;只查 A/C 直接配對會漏同一連通組。 |
| 兩組各自保持同用途 | {"cases":[{"id":"A","partition":"development"},{"id":"B","partition":"development"},{"id":"C","partition":"reserved"}],"links":[["A","B"]]} | {"components":[["A","B"],["C"]],"mixed":[],"valid":true} | A/B 都在開發,獨立 C 保留驗收。 |
| 一條連結有三端點 | {"cases":[{"id":"A","partition":"development"},{"id":"B","partition":"development"}],"links":[["A","B","Z"]]} | {"error":"split-input"} | 走圖前拒絕非二端連結,不能靜默丟掉 Z。 |
| 連通組成員欠個案編號 | {"cases":[{"partition":"reserved"}],"links":[]} | {"error":"split-input"} | 成員沒有編號,不能當有效已列個案連通組。 |
| 連結端點未登記 | {"cases":[{"id":"A","partition":"development"}],"links":[["A","Z"]]} | {"error":"split-input"} | Z 沒有個案分配,不能默默略去而算不完整連通組。 |
逐步處理 undeclared-endpoint 個案,須得到甚麼結果?原因是甚麼?
查看參考答案
{"error":"split-input"}。Z 沒有個案分配,不能默默略去而算不完整連通組。
執行完整本機驗算
import assert from 'node:assert/strict';
function evaluate(x){
const ids=x.cases.map(c=>c.id),by=new Map(x.cases.map(c=>[c.id,c.partition]));if(new Set(ids).size!==ids.length||x.cases.some(c=>typeof c.id!=='string'||!c.id.trim()||!['development','reserved'].includes(c.partition))||x.links.some(link=>!Array.isArray(link)||link.length!==2||!by.has(link[0])||!by.has(link[1])))return {error:'split-input'};
const adjacency=new Map(ids.map(id=>[id,[]]));for(const [a,b] of x.links){adjacency.get(a).push(b);adjacency.get(b).push(a);}
const seen=new Set(),components=[];for(const root of [...ids].sort())if(!seen.has(root)){const component=[],queue=[root];seen.add(root);while(queue.length){const id=queue.shift();component.push(id);for(const next of adjacency.get(id))if(!seen.has(next)){seen.add(next);queue.push(next);}}components.push(component.sort());}
const mixed=components.filter(c=>new Set(c.map(id=>by.get(id))).size>1);return {components,mixed,valid:mixed.length===0};
}
const cases = [
{
"id": "transitive-crossing",
"input": {
"cases": [
{
"id": "A",
"partition": "development"
},
{
"id": "B",
"partition": "development"
},
{
"id": "C",
"partition": "reserved"
}
],
"links": [
[
"A",
"B"
],
[
"B",
"C"
]
]
},
"expected": {
"components": [
[
"A",
"B",
"C"
]
],
"mixed": [
[
"A",
"B",
"C"
]
],
"valid": false
}
},
{
"id": "separate-components",
"input": {
"cases": [
{
"id": "A",
"partition": "development"
},
{
"id": "B",
"partition": "development"
},
{
"id": "C",
"partition": "reserved"
}
],
"links": [
[
"A",
"B"
]
]
},
"expected": {
"components": [
[
"A",
"B"
],
[
"C"
]
],
"mixed": [],
"valid": true
}
},
{
"id": "three-endpoint-record",
"input": {
"cases": [
{
"id": "A",
"partition": "development"
},
{
"id": "B",
"partition": "development"
}
],
"links": [
[
"A",
"B",
"Z"
]
]
},
"expected": {
"error": "split-input"
}
},
{
"id": "component-case-id-absent",
"input": {
"cases": [
{
"partition": "reserved"
}
],
"links": []
},
"expected": {
"error": "split-input"
}
},
{
"id": "undeclared-endpoint",
"input": {
"cases": [
{
"id": "A",
"partition": "development"
}
],
"links": [
[
"A",
"Z"
]
]
},
"expected": {
"error": "split-input"
}
}
];
for (const c of cases) assert.deepEqual(evaluate(c.input), c.expected, c.id);
console.log(JSON.stringify({checked:cases.length,caseIds:cases.map(c=>c.id)}));
分組檔連同已列連結及計出的成員保存。未明示相似關係不會被找出;圖通過也不代表已證明統計獨立。