freeCodeCamp 每日编程挑战 55:用 Python 实现恒星光谱分类(OBAFGKM)
2026/9/10 0:57:27 网站建设 项目流程

freeCodeCamp 每日编程挑战 55:用 Python 实现恒星光谱分类(OBAFGKM)

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

本篇技术指南围绕 freeCodeCamp 开源仓库中的每日编程挑战第 55 题展开:根据恒星表面温度(开尔文 K)判断其光谱分类,输出经典的"O""B""A""F""G""K""M"分类字母。文章将完整解析题目要求、七个温度区间划分、八个官方测试用例、种子代码与标准解法,并结合仓库源码说明每日挑战的存储、校验与评分机制。读完本文,你不仅能独立解出这道题,还能理解这类挑战题目在 freeCodeCamp 中从 Markdown 到数据库、再到前端评测的完整链路。

挑战背景:世界空间周与天文主题编码

本题位于 daily-coding-challenges-python 区块,是该系列的第 55 题(Challenge 55)。题目描述明确指出:10 月 4 日是世界空间周(World Space Week)的开始,接下来的七天每天都会带来一个天文主题的编程挑战,本挑战正是"空间周第一天"的恒星分类题。

在区块的challengeOrder中可以找到本挑战的登记信息:ID 为68c1a929005bf54d342aa8d2,标题为 "Challenge 55: Space Week Day 1: Stellar Classification",紧随其后的是空间周第二至第七天(Exoplanet Search、Phone Home、Landing Spot、Goldilocks Zone、Moon Phase、Launch Fuel)。由此可见,本题并非孤立的语法练习,而是 freeCodeCamp 用连续主题串联编程能力的教学设计:同一周内,你将从"恒星分类"一路写到"发射燃料计算"。

题目要求:温度到光谱类型的映射

题目给出一个恒星表面温度(单位:开尔文 K),要求返回该恒星的光谱分类。分类依据是经典的哈佛光谱分类体系(OBAFGKM),温度越高,字母越靠前,具体的区间划分如下:

分类温度区间 (K)说明
"O"30,000 K 及以上最热、最蓝的恒星
"B"10,000 K – 29,999 K蓝白色恒星
"A"7,500 K – 9,999 K白色恒星
"F"6,000 K – 7,499 K黄白色恒星
"G"5,200 K – 5,999 K黄色恒星(如我们的太阳)
"K"3,700 K – 5,199 K橙色恒星
"M"0 K – 3,699 K最冷、最红的恒星

注意区间的边界处理:每个区间都是"下限包含、上限不包含"(除"O"无上限、"M"无下限)。例如 6,000 K 属于"F"而非"G",3,700 K 属于"K"而非"M",3,699 K 属于"M"。这些边界值恰恰是官方测试用例重点覆盖的对象。

从天文常识看,我们的太阳表面温度约为 5,778 K,落在 5,200 K – 5,999 K 区间,因此属于"G"型恒星——这也是本题测试用例的第一个断言。

函数签名与种子代码

题目要求实现名为classification的函数,接收一个参数temp(恒星表面温度,单位为 K),返回对应的分类字母(字符串)。种子代码位于原文档的--seed-contents--部分:

def classification(temp): return temp

种子函数目前只是原样返回温度,你需要补充判断逻辑。挑战类型为challengeType: 29,即每日编程挑战(Daily Coding Challenge),与普通练习不同,这类题目在仓库中采用"JavaScript 与 Python 双语言"的结构——同一道题在 daily-coding-challenges-javascript 区块存在对应的 JavaScript 版本,本文聚焦 Python 解法。

官方测试用例:边界值与典型值的双重覆盖

--hints--部分给出了 8 个测试断言,它们共同验证了解法的正确性。每个测试都通过runPython调用 Python 的unittest.TestCase().assertEqual(...)来执行:

输入温度 (K)期望输出测试意义
5778"G"太阳表面温度,典型 G 型恒星
2400"M"典型的红矮星
9999"A"A 区间上边界(10,000 K 以下)
3700"K"K 区间下边界
3699"M"与上一条形成边界对照
210000"O"远超 30,000 K 的极端高温
6000"F"F/G 分界边界值
11432"B"典型的 B 型恒星

这组用例的设计颇具匠心:既有5778240011432这样的"典型值",也有9999370036996000这样的"边界值",可以同时检验你的比较运算符是否用了>=而非>,以及区间是否出现缝隙或重叠。

在 freeCodeCamp 的评测体系中,hints中的testString会被解析为可执行的测试代码。从 api/src/daily-coding-challenge/schemas/daily-coding-challenge.ts 可以看到,每个挑战的测试由text(测试说明)与testString(可执行测试字符串)两部分构成;在 client/src/utils/daily-coding-challenge-validator.ts 中,DailyCodingChallengeFromDb结构也明确了tests数组会保存这两部分内容,用于前端评测时回放。

标准解法:一条 if/elif 链搞定七个区间

官方--solutions--给出的解法如下:

def classification(temp): if temp >= 30000: return "O" elif temp >= 10000: return "B" elif temp >= 7500: return "A" elif temp >= 6000: return "F" elif temp >= 5200: return "G" elif temp >= 3700: return "K" else: return "M"

解法思路拆解

这个解法的精髓在于利用递减的阈值配合elif短路求值,将"区间判断"转化为"下界判断":

  1. 首先检查是否>= 30000,命中即返回"O"
  2. 未命中说明temp < 30000,此时再检查>= 10000,命中的自然落入 10,000 – 29,999 的"B"区间;
  3. 依此类推,每个elif都隐含了"已排除上方所有区间"的前提;
  4. 最后所有条件都不满足时(temp < 3700),落入else返回"M"

由于 Python 的elif链从上到下依次短路求值,这种写法不需要显式写出每个区间的上界,也天然保证了区间不重叠、无缝隙,正确处理了题目中所有边界值:

  • 9999:不满足>= 30000>= 10000,但满足>= 7500,返回"A"
  • 3700:满足>= 3700,返回"K"
  • 3699:不满足任何elif,落入else,返回"M"
  • 6000:满足>= 6000,返回"F"

复杂度分析

该算法的时间复杂度为 O(1):最多执行 7 次整数比较;空间复杂度为 O(1),仅使用常数额外空间。由于区间数量固定(7 类),无论输入温度多大,判断次数都不随输入规模增长。

常见易错点

  • 边界运算符选错:若将>=写成>,则9999会落入"B"6000会落入"G",导致测试失败;
  • 区间顺序颠倒:若从低温向高温判断(先判断"M"),由于低温条件最先满足,高温恒星会被误判为"M"
  • 返回类型错误:题目要求返回字符串,如"G"而非整数或布尔值。

进阶思路:二分查找与查表法

虽然 if/elif 链是本题的最简解,但了解其他实现思路有助于应对"区间数量变大"的泛化场景:

二分查找法:将阈值与分类放入有序序列,用bisect定位温度所在区间:

import bisect def classification(temp): thresholds = [30000, 10000, 7500, 6000, 5200, 3700] classes = ["O", "B", "A", "F", "G", "K", "M"] # bisect_right 返回第一个大于 temp 的阈值下标,temp 恰好等于阈值时归入更热区间 idx = bisect.bisect_right(thresholds, temp - 1) return classes[min(idx, len(classes) - 1)]

在区间数量固定的场景下,这种写法与 if/elif 链性能相当,但可读性因人而异;if/elif 链的优势是意图直白、无需引入bisect

从 Markdown 到每日挑战:仓库中的完整链路

本挑战的 Markdown 文件只是"源头",每日编程挑战在实际运行中还经历以下环节(均可从仓库源码验证):

1. 区块定义:curriculum/structure/blocks/daily-coding-challenges-python.json 声明了该区块的属性——helpCategory: "Python"blockLayout: "legacy-challenge-list"usesMultifileEditor: true,并在challengeOrder中按顺序登记 300+ 道挑战,本挑战(ID68c1a929005bf54d342aa8d2)排在第 55 位。

2. 数据入库:tools/daily-challenges/seed-daily-challenges.ts 是每日挑战的种子脚本。它通过 GraphQL 从 dev-playground 超级区块抓取 JavaScript 与 Python 两套挑战数据,校验两者数量一致且等于EXPECTED_CHALLENGE_COUNT = 365后,以bulkWrite+upsert写入 MongoDB 的DailyCodingChallenges集合。每个挑战会被分配一个顺序递增的challengeNumber和按起始日期(2025-08-11 UTC)逐日递增的date字段,这正是"每日"挑战的时间来源。

3. 接口与校验:api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 提供查询每日挑战的 API;api/src/daily-coding-challenge/schemas/daily-coding-challenge.ts 使用 Joi 校验数据库返回结构,要求每条挑战必须同时包含javascriptpython两个语言对象,且每个对象都含testschallengeFiles数组——也就是说,本道 Python 挑战的种子代码与测试字符串,最终会被打包进这个统一结构中。

4. 前端入口:client/src/components/daily-coding-challenge/widget.tsx 是用户在首页/学习地图上看到的"每日编程挑战"入口组件,提供"前往今日挑战"(按美国中部时间的月日生成链接)与"挑战归档"两个按钮;client/src/client-only-routes/show-daily-coding-challenge.tsx 则负责渲染具体的挑战页面并驱动评测。

5. 评测执行:挑战页面中的测试通过runPython--hints--里的testString(如from unittest import TestCase; TestCase().assertEqual(classification(5778), "G"))注入 Python 运行时执行,用户代码的返回值与期望值逐一比对。这也是为什么每个 hint 都用unittest.TestCase().assertEqual包裹的原因——评测器只需要断言函数输出即可,无需关心用户如何实现。

在本地验证你的解法

你可以直接在本地 Python 环境(3.x 即可,无需额外依赖)中验证解法:

python -c " from unittest import TestCase def classification(temp): if temp >= 30000: return 'O' elif temp >= 10000: return 'B' elif temp >= 7500: return 'A' elif temp >= 6000: return 'F' elif temp >= 5200: return 'G' elif temp >= 3700: return 'K' else: return 'M' cases = [(5778, 'G'), (2400, 'M'), (9999, 'A'), (3700, 'K'), (3699, 'M'), (210000, 'O'), (6000, 'F'), (11432, 'B')] for temp, expected in cases: TestCase().assertEqual(classification(temp), expected) print(f'classification({temp}) = {classification(temp)!r} (期望 {expected!r})') print('全部 8 个测试通过') "

如果输出显示 8 组断言全部通过,你的实现就与官方--hints--完全一致。

小结

本挑战用一道简洁的天文映射题,综合训练了 Python 条件判断的三个关键能力:

  1. 区间划分与边界处理:理解"下限包含、上限不包含"的区间语义,并正确选用>=
  2. elif链的短路求值:利用判断顺序消除冗余条件,写出无缝隙、无重叠的分支逻辑;
  3. 面向测试的编码习惯:以官方 hints 中的边界用例为基准自查实现。

在仓库中,这道题对应的源文件位于 curriculum/challenges/english/blocks/daily-coding-challenges-python/68c1a929005bf54d342aa8d2.md,如果你想继续挑战同一主题系列,可以在同一目录下阅读空间周其余六天的题目(68c1a929005bf54d342aa8d368c497f3aaefc9fd9f1b0e25),把天文主题连续刷完。

【免费下载链接】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),仅供参考

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

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

立即咨询