昨天確認了單筆平均分攤的基本計算,今天換一個看似簡單、卻不能直接除完就結束的問題:100 元要分給三個人,每個人到底應該付多少?
如果把 100 除以 3,會得到無法用整數元表示的結果。目前「一起 AA」以新台幣整數元計算,如果每個人都四捨五入成 33 元,三個人加起來只有 99 元,少了一元。如果全部改成 34 元,又會變成 102 元。每個人的數字看起來接近平均,不代表整本帳就算對了。
因此,今天先確認一個規則:先讓每個人分到相同的整數金額,再把剩下的零頭逐元分配。100 元分給三個人,就是先各分 33 元,剩下的一元再交給其中一人負擔,最後得到 34、33、33,合計剛好是 100 元。
剩下的一元要由誰負擔,也不能讓程式隨意決定。我們先採用帳本中的固定成員順序,只計入這筆支出的參與者。假設順序是小明、小美、阿哲,那麼 100 元三人均分時,小明負擔 34 元,小美與阿哲各負擔 33 元。這個順序不是姓名排序,也不是勾選參與者的先後順序。
檢查現有的 logic.js 後,確認原型已經包含這套邏輯。程式先從帳本成員名單篩出本筆參與者,保留原本順序,再算出每人的基本金額與餘數。今天的工作因此以驗證為主,沒有為了配合天數重新實作相同功能。
第一組案例就是 100、101 和 102 元分給三個人。實際結果分別是 34、33、33,34、34、33,以及 34、34、34。剩下一元時,第一位多負擔一元;剩下兩元時,前兩位各多負擔一元;可以整除時,就不需要額外分配。
我們也測試了總金額比人數少的情況:一元分給三個人。結果是小明一元,另外兩人零元。這提醒我,支出總額必須大於零,但個人的分攤金額可以是零。若硬性要求每位參與者至少負擔一元,反而會讓加總超過原本的消費。
接著確認,零頭只能分給真正參與這筆消費的人。帳本雖然有三個人,如果 100 元只有小美與阿哲需要分攤,兩人就各負擔 50 元,小明為零。把金額改為 101 元後,則由小美負擔 51 元、阿哲負擔 50 元。
為了確認結果不受參與名單的輸入順序影響,我們把小美與阿哲的輸入順序反過來再算一次。結果仍然是小美 51 元、阿哲 50 元,符合帳本固定順序的規則。這樣就不會因為使用者先勾了誰,讓同一筆帳突然換一個人負擔零頭。
付款人也需要另外驗證。100 元由小明代墊時,小美與阿哲各付小明 33 元,小明收回 66 元,自己負擔 34 元。如果改成小美代墊,每人的分攤仍然是 34、33、33,只是付款清單改成小明付小美 34 元、阿哲付小美 33 元。代墊者改變,不應該改變每個人原本的消費負擔。
今天共執行七類情境、八次計算模組驗證,全部通過。除了對照預先算好的答案,也檢查每份分攤都是非負整數、同筆參與者的負擔差距不超過一元,以及所有分攤加總等於原始金額。結算部分則確認淨額加總為零,並模擬依付款清單交付後,每個人的差額都能歸零。
這次也補上了瀏覽器操作驗證。一開始本機預覽沒有啟動,開啟頁面時無法連線;啟動預覽後,我們在獨立的測試分頁建立三位成員,新增一筆由小明代墊的 100 元支出,再查看畫面結果。
畫面顯示小明分攤 34 元,小美與阿哲各分攤 33 元;付款清單也正確列出兩人各付小明 33 元,小明應收回 66 元。這次確認的是這一條新增支出與顯示結果的操作流程,其餘金額及順序案例是在計算模組中驗證,不能把它寫成所有畫面情境都測過了。
固定順序的優點是規則清楚,同樣的資料可以得到相同的結果。不過,它也有取捨:如果連續多筆支出都有零頭,排在前面的成員可能累積負擔比較多。因此,目前只能說每筆分配的差距不超過一元,不能宣稱跨多筆帳目也完全公平。未來若想輪流分配零頭,需要另外定義規則,以及修改、刪除支出時要如何處理。
今天沒有修改產品程式,也沒有發布新版,而是留下可重複執行的測試與實際結果。我發現,和 AI 一起做分錢工具時,除了問「能不能算出答案」,更需要問「這個答案符合什麼規則」,並拿具體例子核對。即使只差一元,也不能讓它在計算中憑空消失。
第八天完成了零頭規則確認、計算驗證與一項瀏覽器操作驗證。下一天會處理使用者輸入不符合規則的情況,檢查空白、重複姓名與錯誤金額,以及畫面是否能清楚說明該怎麼修正。