freeCodeCamp 每日编程挑战 364:用加权平均混合两桶 RGB 颜料(Between Two Buckets)
2026/9/10 10:00:31 网站建设 项目流程

freeCodeCamp 每日编程挑战 364:用加权平均混合两桶 RGB 颜料(Between Two Buckets)

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本篇围绕 freeCodeCamp 每日编程挑战(Daily Coding Challenges)JavaScript 序列中的第 364 题 “Between Two Buckets” 展开:给定两桶各带 RGB 颜色与浓淡(fullness)等级的颜料,要求返回混合后的 RGB 颜色数组。读完本文,你将完整掌握这道题的输入输出契约、全部官方测试用例、加权平均混合的数学推导与参考实现,并能结合仓库源码了解该挑战在 freeCodeCamp 平台中作为dailyChallengeJs题型(challengeType 28)的承载方式。

题目描述:两桶颜料的加权混合

原题位于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/6a26df95efa55a2524399745.md,题面约定如下:

给定两桶颜料,每桶都有一个 RGB 颜色和一个浓淡等级(fullness),请返回混合后的 RGB 颜色,以三个整数组成的数组形式返回。

具体的数据结构约束:

  • 每桶是一个对象(JavaScript)或字典(Python),包含两个属性:
    • color:长度为 3 的整数数组[r, g, b]
    • fullness:0–100 之间的数值,表示该桶颜料的“量”。
  • 混合颜色是两个颜色各自三个通道的加权平均,权重就是各自的 fullness,每个通道的结果四舍五入到最接近的整数

也就是说,函数签名与核心计算式为:

mixPaint(bucket1, bucket2) // 对每个通道 c ∈ {r, g, b}: // mixed[c] = round( (bucket1.color[c] * bucket1.fullness + bucket2.color[c] * bucket2.fullness) // / (bucket1.fullness + bucket2.fullness) )

官方测试用例(hints)全解析

原题通过 5 组断言用例定义了完整的行为契约,这里全部继承并逐组说明其考察点:

用例 1:等量混合,结果应为两色中点

assert.deepEqual(mixPaint({ color: [250, 250, 250], fullness: 50 }, { color: [0, 0, 0], fullness: 50 }), [125, 125, 125]);

两桶 fullness 相等,权重各占 50%。以红通道为例:(250 * 50 + 0 * 50) / 100 = 125。三个通道对称,结果为[125, 125, 125]。这组用例验证“等权”时的简单平均行为。

用例 2:不等权混合,结果偏向浓的那一桶

assert.deepEqual(mixPaint({ color: [250, 250, 250], fullness: 80 }, { color: [0, 0, 0], fullness: 20 }), [200, 200, 200]);

(250 * 80 + 0 * 20) / 100 = 200。权重 80:20 使结果明显偏向第一桶,验证了“加权”而非“平均”。

用例 3:两桶颜色完全相同时,权重不影响结果

assert.deepEqual(mixPaint({ color: [100, 150, 200], fullness: 30 }, { color: [100, 150, 200], fullness: 70 }), [100, 150, 200]);

颜色相同时,无论 fullness 如何分配,加权平均仍等于该颜色本身。这组用例是对公式的恒等性质检验。

用例 4:非整除结果,考察四舍五入

assert.deepEqual(mixPaint({ color: [143, 143, 101], fullness: 45 }, { color: [100, 204, 204], fullness: 90 }), [114, 184, 170]);

手算验证(total = 45 + 90 = 135):

  • 红:(143 * 45 + 100 * 90) / 135 = 15435 / 135 ≈ 114.33→ 114
  • 绿:(143 * 45 + 204 * 90) / 135 = 24795 / 135 ≈ 183.67→ 184
  • 蓝:(101 * 45 + 204 * 90) / 135 = 22905 / 135 = 170→ 170

注意绿通道是“向上舍入”(183.67 → 184),说明题面要求的“四舍五入到最接近整数”不是简单的截断取整。

用例 5:再一组非整除混合

assert.deepEqual(mixPaint({ color: [15, 134, 249], fullness: 29 }, { color: [97, 178, 55], fullness: 54 }), [68, 163, 123]);

total = 29 + 54 = 83:

  • 红:(15 * 29 + 97 * 54) / 83 = 5673 / 83 ≈ 68.35→ 68
  • 绿:(134 * 29 + 178 * 54) / 83 = 13498 / 83 ≈ 162.63→ 163
  • 蓝:(249 * 29 + 55 * 54) / 83 = 10191 / 83 ≈ 122.78→ 123

这组用例同时覆盖了小数值通道(15)与大数值通道(249),并再次检验舍入方向。

起始代码与参考实现

原题给出的种子代码(seed)是一个占位实现,直接返回第一个桶:

function mixPaint(bucket1, bucket2) { return bucket1; }

原题附带的官方参考解法如下:

function mixPaint(bucket1, bucket2) { const total = bucket1.fullness + bucket2.fullness; return bucket1.color.map((channel, i) => Math.round((channel * bucket1.fullness + bucket2.color[i] * bucket2.fullness) / total) ); }

逐行拆解:

  1. const total = bucket1.fullness + bucket2.fullness;—— 先求出总权重,作为加权平均的分母。fullness 的取值范围是 0–100,因此只要两桶不同时为 0,total 就在 1–200 之间,除法安全。
  2. bucket1.color.map((channel, i) => ...)—— 以第一桶的三个通道为基准做映射,同时利用i作为下标从bucket2.color[i]取出对应通道,实现两数组“按位配对”,无需手写zip
  3. Math.round((channel * bucket1.fullness + bucket2.color[i] * bucket2.fullness) / total)—— 对每个通道计算加权和再除以总权重,并用Math.round完成题面要求的“四舍五入到最接近整数”。Math.round.5一律向 +∞ 方向取整,而 5 组官方用例中均不出现精确.5的中间值,因此该行为在本题契约内没有歧义。

等价的展开式写法(不用map)同样成立:

function mixPaint(bucket1, bucket2) { const total = bucket1.fullness + bucket2.fullness; const [r1, g1, b1] = bucket1.color; const [r2, g2, b2] = bucket2.color; const f1 = bucket1.fullness, f2 = bucket2.fullness; return [ Math.round((r1 * f1 + r2 * f2) / total), Math.round((g1 * f1 + g2 * f2) / total), Math.round((b1 * f1 + b2 * f2) / total) ]; }

两种写法在语义上完全一致;map版本的优点是天然遍历三个通道,未来若扩展到 CMYK 等更多通道只需改动颜色数组本身。

一个值得注意的边界:total 为 0

从源码结构看,题面将 fullness 约束为 0–100,官方解法没有对total === 0做防护——若两桶 fullness 都是 0,Math.round(NaN / 0 相关运算)会得到NaN。题面的 5 组用例都不触发这一分支,因此就本题契约而言无需处理;但这一细节恰好展示了“实现贴合测试契约”与“防御式编程”之间的取舍:在刷题场景以契约为界,在工程场景则应先判断分母。

这道题在 freeCodeCamp 平台中的承载方式

这道挑战不是一个孤立的算法题,而是 freeCodeCamp 每日编程挑战(Daily Coding Challenges)体系的一部分。结合仓库源码,可以确认以下事实:

  • 题型编号:原文件 frontmatter 中challengeType: 28。在 packages/shared/src/config/challenge-types.ts 中,常量dailyChallengeJs = 28(第 30 行),即本题属于“JavaScript 每日挑战”题型;相邻的dailyChallengePy = 29则是 Python 版每日挑战。
  • 渲染与提交方式:同一文件中viewTypesdailyChallengeJs映射为'classic'(使用经典编辑器视图),submitTypes将其映射为'tests'(提交时运行断言测试判定通过)——这也解释了为什么原题的判定完全依赖assert.deepEqual形式的 hints 用例。
  • 题型识别工具函数getIsDailyCodingChallenge(challengeType)通过判断类型是否属于[dailyChallengeJs, dailyChallengePy]来识别每日挑战(见 challenge-types.ts#L182-L188)。
  • 所属内容块:该题属于结构块 curriculum/structure/blocks/daily-coding-challenges-javascript.json,对应题目前缀 “Challenge 364”,即 JavaScript 每日挑战序列中的第 364 题。
  • 服务端接口:每日挑战的信息接口位于 api/src/daily-coding-challenge/(该 README 注明“Endpoints to get daily coding challenge info. Daily challenge submission still lives in the main part of the API.”)。
  • 前端校验:客户端通过 client/src/utils/daily-coding-challenge-validator.ts 中的 Joi schema 校验从数据库取出的挑战数据,要求每种语言(javascript/python)都必须包含teststext+testString)与challengeFilesfileKey+contents)数组——原题中的 seed 代码与 hints 断言正是以challengeFilestests的形态进入这条链路的。

对想在本地查看原题全文与全部用例的读者,直接打开 6a26df95efa55a2524399745.md 即可;题目、5 组测试、种子代码与官方解法都在同一文件的--description----hints----seed----solutions--区块中。

小结:这道题到底在考什么

把“Challenge 364: Between Two Buckets”抽象出来,它考察的是三个基础但高频的 JavaScript 能力:

  1. 加权平均的数学建模:把自然语言“按浓淡混合”翻译成(a·w1 + b·w2) / (w1 + w2)的逐通道公式,并正确处理四舍五入(用例 4、5 专门验证了这一点);
  2. 数组按位配对:用map((channel, i) => ...)的下标技巧替代显式循环或zip,这是处理“两个等长数组按位运算”的惯用模式;
  3. 与测试契约对齐:题面没有额外边界(如 total 为 0)的要求,实现以 5 组assert.deepEqual用例为完整验收标准——这也是每日挑战类题目的典型形态:契约明确、判分基于断言,通过即完成。

掌握这一模式后,类似“按比例合并两组数值数组并取整”的题目(亮度混合、浓度配比、坐标插值等)都可以直接套用同一套加权平均骨架。

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询