freeCodeCamp 每日编程挑战解析:用 Python 实现 Binary to Decimal(二进制转十进制)
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本篇技术指南围绕 freeCodeCamp 开源仓库中的 Python 每日编程挑战Challenge 52: Binary to Decimal(挑战原文)展开,完整讲解题目要求、进制转换原理、官方测试用例、官方参考解法,并结合仓库源码剖析其双语言(Python/JavaScript)同步机制、自动化验证流程以及每日挑战的数据库发布链路。读完本文,你既能独立完成该挑战并通过全部测试,也能理解这类挑战在 freeCodeCamp 课程体系中的完整生命周期。
一、挑战背景:什么是 Daily Coding Challenge
daily-coding-challenges-python是 freeCodeCamp 课程体系中的一个挑战块(block),位于dev-playground超级块(superblock)之下。该块以legacy-challenge-list布局呈现,当前标记为isUpcomingChange: true,并使用多文件编辑器(usesMultifileEditor: true),帮助分类为Python,具体配置见 daily-coding-challenges-python.json。
这个挑战块的特殊之处在于:仓库同时维护了功能完全对等的JavaScript 版本挑战块(daily-coding-challenges-javascript,配置见 daily-coding-challenges-javascript.json,helpCategory为 JavaScript)。Challenge 52 在两个语言块中各有一份题目文件,且共享同一个挑战 ID68b7cadffed0e75a517da673:
| 语言 | 文件路径 | challengeType |
|---|---|---|
| Python | 68b7cadffed0e75a517da673.md | 29 |
| JavaScript | 68b7cadffed0e75a517da673.md | 28 |
在 daily-coding-challenges-python.json 的challengeOrder中,本挑战被登记为Challenge 52: Binary to Decimal,紧随其后的Challenge 53: Decimal to Binary(ID68b7cadffed0e75a517da675)与本挑战互为逆运算,构成一组非常经典的进制转换练习。
二、题目要求与进制转换原理
题目原文
Given a string representing a binary number, return its decimal equivalent as a number.
即:给定一个表示二进制数的字符串,返回其对应的十进制数值(number 类型)。
转换原理
二进制数只使用0和1两个数字来表示任意数值。要将二进制转换为十进制,需要把每一位乘以2的幂次,然后求和:
- 最右侧一位乘以
2^0 - 其左侧相邻一位乘以
2^1 - 依此类推,每一位的幂次从右向左递增
例如,二进制数101转换为十进制等于5:
1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 4 + 0 + 1 = 5这一原理是理解整个挑战的核心:二进制是一种基数为 2 的位置计数法,每一位的"权重"是 2 的幂,而非十进制中的 10 的幂。
三、测试用例(Hints)逐条解析
挑战文件中的--hints--部分定义了 4 个测试断言,全部通过runPython包装的 Python 标准库unittest执行。注意 Python 版本的函数命名为snake_case:to_decimal(与 JavaScript 版的toDecimal命名风格保持一致)。
| 测试输入 | 期望输出 | 推导过程 |
|---|---|---|
to_decimal("101") | 5 | 1×2² + 0×2¹ + 1×2⁰ = 4 + 0 + 1 |
to_decimal("1010") | 10 | 1×2³ + 0×2² + 1×2¹ + 0×2⁰ = 8 + 0 + 2 + 0 |
to_decimal("10010") | 18 | 1×2⁴ + 0 + 0 + 1×2¹ + 0 = 16 + 2 |
to_decimal("1010101") | 85 | 64 + 16 + 4 + 1 = 85 |
每个测试用例的实际断言代码如下(以第一个为例):
({test: () => { runPython(` from unittest import TestCase TestCase().assertEqual(to_decimal("101"), 5)`) }})这里揭示了 freeCodeCamp 挑战的双层测试机制:外层用 JavaScript 的runPython内嵌 Python 代码片段,内层则调用 Pythonunittest.TestCase.assertEqual做严格相等断言。这意味着你的to_decimal函数必须返回 Python 的整数5,而不是字符串"5"。
四、初始代码与官方参考解法
初始代码(Seed)
def to_decimal(binary): return binary初始函数只是把输入原样返回("101"返回"101"字符串),显然无法通过任何测试。你需要在函数体内实现真正的转换逻辑。
官方参考解法
def to_decimal(binary): decimal = 0 for i, bit_char in enumerate(reversed(binary)): bit = int(bit_char) decimal += bit * (2 ** i) return decimal逐行解读
reversed(binary):反转字符串,使最右侧(最低位)的字符排在第一位,从而让枚举下标i直接对应 2 的幂次;enumerate(...):同时取出下标i与字符bit_char,其中i即该位应乘的幂次;int(bit_char):把单个字符'0'或'1'转换为整数0或1;decimal += bit * (2 ** i):按位置权累加,等价于题目描述中的"每位乘以 2 的幂后求和";- 函数返回整数
decimal,与测试断言的类型要求一致。
复杂度分析:设二进制字符串长度为n,该解法只需一次线性遍历,时间复杂度为O(n),空间复杂度为O(1),属于该问题的最优量级。
五、JavaScript 版本的对照实现
为保证每日挑战对编程语言选择的包容性,仓库为同一挑战维护了 JS 版本。其初始代码与参考解法如下:
function toDecimal(binary) { let decimal = 0; for (let i = 0; i < binary.length; i++) { const bit = parseInt(binary[binary.length - 1 - i], 10); decimal += bit * Math.pow(2, i); } return decimal; }与 Python 解法对照可发现同一算法的两种语言形态:
- Python 用
reversed(binary)+enumerate从左向右携带幂次信息; - JS 用下标换算
binary.length - 1 - i从右向左取位,配合Math.pow(2, i)计算权重; - 两者都遵循"从右往左、位权递增"的核心算法骨架。
六、双语言一致性的自动化保障
你可能好奇:两个语言版本的挑战如何保证不"跑偏"?答案在测试文件 daily-challenges.test.js 中。该文件基于 Vitest 编写,核心校验逻辑包括:
- 数量匹配:JS 与 Python 两个块必须都存在且挑战数量大于 0;
- 数量相等:JS 与 Python 的每日挑战数量必须完全一致;
- 属性一致:按索引逐一配对,校验每个挑战的
id、title、description和测试数量(tests数组长度)是否完全一致,任何不匹配都会将错误信息汇总后触发断言失败。
这解释了为什么 Python 挑战的 ID68b7cadffed0e75a517da673与 JS 版本完全相同——这是仓库刻意设计的"一一对应"关系。
七、从挑战文件到每日推送:种子脚本链路
Daily Coding Challenge 并非静态页面,而是按"每天一道题"的节奏推送。理解这一机制可以帮你更好地定位本题在生产环境中的角色。核心脚本是 seed-daily-challenges.ts:
- 从
dev-playground超级块通过 GraphQL 抓取全部挑战(客户端需开启 upcoming changes 才能暴露这些内容); - 分别获取 JavaScript 与 Python 两个语言版本的挑战数组,并要求两者数量相等(此处期望365 道,对应全年每日一题);
- 调用 helpers.ts 中的
combineChallenges,将同一天的 JS 与 Python 挑战合并为一条记录,并关联一个日期——起始日期硬编码为2025-08-11T00:00:00.000Z,每道题顺延一天(24 * 60 * 60 * 1000ms); - 通过 MongoDB 的
bulkWrite+replaceOne(upsert)写入DailyCodingChallenges集合,实现"已存在则替换、不存在则插入"; - 脚本内置起始日期校验与文档总数校验,防止意外改动影响已发布排期。
因此,Challenge 52 会被分配到一个具体日期,在前端(widget.tsx)的"今日挑战"入口中被渲染出来。从这个角度看,to_decimal不仅是一道练习题,更是 freeCodeCamp"每天一道编码挑战"产品形态中的一环。
八、延伸思考:配套的进制转换挑战
本题所在的块还围绕"进制与编码"安排了多个姊妹挑战,可一起练习形成体系:
- Challenge 53: Decimal to Binary(
68b7cadffed0e75a517da675):与本题互为逆运算,将十进制转为二进制; - Challenge 62: Hex to Decimal(
68c497f3aaefc9fd9f1b0e26):十六进制转十进制,幂次变为 16; - Challenge 166: Hex Validator(
696655d24b614176d4c9b789):校验十六进制字符串合法性。
这些挑战共享同一套"按位乘幂累加"的思想,区别仅在于基数(base)不同。掌握to_decimal的写法后,你可以在本地用 Python 交互式环境验证任意输入:
# 本地手动验证 >>> to_decimal("101") 5 >>> to_decimal("1010101") 85 # 对照 Python 内置能力 >>> int("101", 2) 5需要说明的是:int(binary, 2)是 Python 内置的快速转换方案,但本挑战的目的正是让你手写实现这一过程,以加深对位置计数法与位权的理解。若你希望验证更多边界输入,可自行扩展测试,例如"0"应返回0、"11111111"应返回255——这些均不属于原文档的强制断言,但有助于巩固对算法的掌握。
九、小结
通过本文,你已完整掌握 freeCodeCamp Challenge 52 "Binary to Decimal" 的全部要素:
- 算法本质:将二进制字符串按位乘以 2 的幂并累加,得到十进制整数;
- 验收标准:
to_decimal需通过 4 个基于unittest的断言,覆盖 4 位到 7 位不等的二进制输入; - 参考实现:
reversed+enumerate+int的线性遍历解法,时间复杂度O(n); - 仓库机制:本挑战以相同 ID 存在于 Python(challengeType 29)与 JavaScript(challengeType 28)两个版本,通过 daily-challenges.test.js 保证双语言一致性,并最终由 seed-daily-challenges.ts 以每日一题的形式写入数据库推送给学习者。
现在,你可以在本地打开挑战文件,先尝试独立编写实现,再对照官方解法逐行验证自己的思路。
【免费下载链接】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),仅供参考