跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

Python 实现中秋月相计算、月饼切分与可视化

通过 Python 代码精确计算 2025 年中秋月相,得出月球被照亮程度约为 94.91%。利用 turtle 库绘制月相图直观展示。探讨月饼公平切分算法,包括经典圆心辐射切割及不过圆心的平行弦切割方案。使用马尔可夫链生成中秋诗词。基于模拟数据构建月球表面地形 3D 可视化,并制作一个月内月相变化动画,验证“十五的月亮十六圆”现象。

苹果系统发布于 2026/2/4更新于 2026/5/288.1K 浏览
Python 实现中秋月相计算、月饼切分与可视化

一、月相计算:今晚的月亮到底有多圆

今天是中秋节,作为 Python 开发者,可以用代码来精确计算月相,看看今年中秋的月亮到底有多圆。

月相的计算涉及到朔望月的概念。思路其实挺简单的,找一个已知的新月时间点作为基准,然后根据朔望月周期(29.53 天)往后推算就行。

from datetime import datetime, timedelta
import math

def calculate_moon_phase(date):
    """计算指定日期的月相(0-1,0 为新月,0.5 为满月)"""
    # 2000 年 1 月 6 日 18:14 UTC 是一个已知的新月时刻
    known_new_moon = datetime(2000, 1, 6, 18, 14)
    # 朔望月周期(天)
    synodic_month = 29.53058867
    # 计算距离已知新月的天数
    days_diff = (date - known_new_moon).total_seconds() / 86400
    # 计算当前在月相周期中的位置
    phase = (days_diff % synodic_month) / synodic_month
    return phase

# 计算今年中秋(2025 年 10 月 6 日)的月相
mid_autumn = datetime(2025, 10, 6, 12, 0)
phase = calculate_moon_phase(mid_autumn)
illumination = (1 - abs(phase - 0.5) * 2) * 100
print(f"2025 年中秋月相值:{phase:.4f}")
print(f"月球被照亮程度:{illumination:.2f}%")

运行结果显示,2025 年中秋的月相值为 0.4745,接近满月的 0.5,月球被照亮的程度约为 94.91%。也就是说,中秋夜的月亮已经非常圆了,肉眼几乎看不出和满月的差别。

1. 月相可视化

光有数字还不够直观,我们可以用 turtle 库画出当前的月相。虽然 turtle 通常被认为是初学者的玩具,但用来绘制简单的天文图形却恰到好处。

import turtle
import math

def draw_moon_phase(phase, radius=100):
    """绘制月相图"""
    screen = turtle.Screen()
    screen.bgcolor("black")
    moon = turtle.Turtle()
    moon.speed(0)
    moon.color("white")
    # 绘制完整的月球轮廓
    moon.penup()
    moon.goto(0, -radius)
    moon.pendown()
    moon.circle(radius)
    # 根据月相绘制阴影部分
    if phase < 0.5:
        # 上弦到满月
        offset = radius * (1 - phase * 4)
        for angle in range(180):
            rad = math.radians(angle)
            x = offset * math.cos(rad)
            y = radius * math.sin(rad)
            moon.goto(x, y - radius)
    else:
        # 满月到下弦
        offset = radius * ((phase - 0.5) * 4)
        for angle in range(180):
            rad = math.radians(angle)
            x = -offset * math.cos(rad)
            y = radius * math.sin(rad)
            moon.goto(x, y - radius)
    turtle.done()

用 turtle 画了个中秋月亮:白色圆代表月球轮廓,根据刚才算出的月相值,再画一个椭圆阴影,就能直观看到它接近满月的样子。当月相从 0 向 0.5 过渡时,阴影逐渐消失;从 0.5 向 1 过渡时,阴影又逐渐增加。

二、月饼切分算法:公平分配的艺术

中秋吃月饼是传统,但如何公平地分月饼却是个数学问题。假设有一家人围坐在一起,如何用最少的刀数把圆形月饼切成等份?

1. 经典切分策略

最直观的方法是从圆心出发,向外辐射切割。如果要分给 n 个人,我们需要 n 刀,每刀之间的角度是 360/n 度。但这要求第一刀的位置必须精确定位圆心,实际操作中并不容易。

import numpy as np
import matplotlib.pyplot as plt

def fair_mooncake_division(n, radius=1):
    """计算 n 等分月饼的切割路径"""
    plt.figure(figsize=(8, 8))
    ax = plt.subplot(111, projection='polar')
    # 绘制月饼
    theta = np.linspace(0, 2*np.pi, 100)
    ax.plot(theta, [radius]*100, 'brown', linewidth=3)
    ax.fill(theta, [radius]*100, 'wheat', alpha=0.5)
    # 计算切割线
    angles = [2*np.pi*i/n for i in range(n)]
    for angle in angles:
        ax.plot([angle, angle], [0, radius], 'r--', linewidth=2)
    # 标注每份的角度
    for i, angle in enumerate(angles):
        mid_angle = angle + np.pi/n
        ax.text(mid_angle, radius*0.6, f'{i+1}', fontsize=14, ha='center')
    ax.set_ylim(0, radius*1.2)
    plt.title(f'月饼{n}等分方案', pad=20)
    plt.show()
    return angles

用极坐标画了个圆形月饼,然后按等角度切成 n 等份,每份角度是 360/n。图中红色虚线就是切刀位置,简单直观。这其实就是最少刀数问题的经典解法:n 个人用 n 刀,从圆心放射状切。

这种方法是最直观的等分方式,通过从圆心出发的放射状切割,确保了每份的面积完全相等。

2. 进阶问题:不过圆心的切分

更有趣的是这样一个问题:如果切割时不经过圆心,能否仍然保证每份面积相等,这就需要用到更复杂的几何计算了。

def calculate_chord_position(n, piece_index, radius=1):
    """ 计算平行弦切割的位置 
    n: 总份数
    piece_index: 当前是第几份(从 0 开始)
    """
    # 每份应占的面积
    target_area = np.pi * radius**2 / n
    # 累计到当前份的总面积
    cumulative_area = target_area * (piece_index + 1)
    # 通过数值方法求解弦的位置
    def area_to_left(h):
        """计算距圆心高度为 h 的弦左侧的面积"""
        if abs(h) >= radius:
            return 0 if h > 0 else np.pi * radius**2
        # 圆弓形面积 = 扇形面积 - 三角形面积
        angle = 2 * np.arccos(h / radius)
        sector = 0.5 * radius**2 * angle
        triangle = h * np.sqrt(radius**2 - h**2)
        return sector - triangle + np.pi * radius**2 / 2

    from scipy.optimize import brentq
    h = brentq(lambda x: area_to_left(x) - cumulative_area, -radius, radius)
    return h

其实通俗一点讲就是不用从圆心下刀,也能把月饼等面积切成 n 份。思路是用一系列平行的直线(弦)来切,关键是算出每条弦该放在哪里。结果如下所示: 跟上面的第一种做法完全是一样的结果,先算出每份应有的面积对第 i 份,求一条弦,使得它左侧的面积正好等于 i 份的总面积,最后用数值方法解方程,找到弦的位置 h。

三、诗词生成:中秋凑诗

既然是中秋佳节,怎能少了诗词助兴?不用复杂的大模型,一个简单的'马尔可夫链'就够——说穿了,就是让代码先记几句经典中秋诗,再照着'前两个字啥样,就接啥字'的规矩,自己拼出两句来。

先说说这个'凑诗逻辑':它记东西很'短视',下一个字选什么,只看前面一两个字。比如学过'举头望明月',下次碰到'举头',就大概率会接'望';碰到'望明',就可能接'月'。就像学说话的小孩,先背熟几个词组,再瞎组合,偶尔能蒙对味儿。

import random
from collections import defaultdict

class PoemGenerator:
    """基于马尔可夫链的诗词生成器"""
    def __init__(self, order=2):
        self.order = order
        self.chain = defaultdict(list)

    def train(self, poems):
        """训练模型"""
        for poem in poems:
            # 添加起始和结束标记
            words = ['<START>'] * self.order + list(poem) + ['<END>']
            for i in range(len(words) - self.order):
                state = tuple(words[i:i+self.order])
                next_word = words[i+self.order]
                self.chain[state].append(next_word)

    def generate(self, length=28):
        """生成诗句"""
        state = ('<START>',) * self.order
        result = []
        while len(result) < length:
            if state not in self.chain:
                break
            next_word = random.choice(self.chain[state])
            if next_word == '<END>':
                break
            result.append(next_word)
            state = state[1:] + (next_word,)
        return ''.join(result)

# 训练数据示例
training_poems = [
    "明月几时有把酒问青天",
    "但愿人长久千里共婵娟",
    "海上生明月天涯共此时",
    "露从今夜白月是故乡明",
    "举头望明月低头思故乡"
]
generator = PoemGenerator(order=2)
generator.train(training_poems)
for i in range(5):
    poem = generator.generate(length=28)
    # 格式化为七言绝句
    lines = [poem[i:i+7] for i in range(0, 28, 7)]
    print('\n'.join(lines))
    print()

能看出来,它没什么'逻辑',比如第一首里'问青天'接'但愿人长'有点跳,但'明月''天涯''婵娟'这些中秋关键词都在,偶尔还能拼出'海上生明月天涯共'这种像模像样的句子。 要是把 order 改成 1(只记前 1 个字),就会更'放飞',比如可能凑出'明月天涯共此时望',虽然乱,但说不定有意外的意境;改成 3 的话,就几乎是抄原诗的片段了。总之,不算真的'写诗',但中秋凑个热闹,看代码瞎编几句带月亮的话,还挺好玩儿的。

四、月球数据可视化:用数据看月亮

NASA 和各国航天机构提供了大量的月球观测数据。我们可以用这些数据来创建月球的三维可视化,或者分析月球表面的地形特征。

1. 先画月球表面:模拟环形山地形

月球表面坑坑洼洼全是环形山,咱们不用真的下载 NASA 数据,用代码'造'一份模拟地形,再用 3D 图显出来。

# 先导入必须的工具:数值计算 + 绘图 +3D 绘图
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

def generate_moon_terrain(size=200):
    """造一份模拟月球地形数据:主要模拟环形山和月海"""
    # 生成网格:代表月球表面的经纬度(简化成 -1 到 1 的范围)
    x = np.linspace(-1, 1, size)
    y = np.linspace(-1, 1, size)
    X, Y = np.meshgrid(x, y)
    # 把 x 和 y 拼成网格,每个点对应一个位置
    # 基础地形:月海比较平坦,先设个低海拔基线
    Z = np.zeros_like(X) - 0.2
    # 负数代表较低的地方
    # 模拟环形山:用多个'高斯坑'叠加(环形山中间低、周围高)
    def add_crater(X, Y, Z, center_x, center_y, radius, depth):
        """给地形加一个环形山:center 是中心,radius 是半径,depth 是深度"""
        distance = np.sqrt((X - center_x)**2 + (Y - center_y)**2)
        # 环形山的形状:中间凹陷,周围有一圈隆起
        crater = -depth * np.exp(-(distance**2)/(2*radius**2))
        # 凹陷部分
        crater += 0.1 * np.exp(-((distance - radius)**2)/(2*(0.02)**2))
        # 周围隆起
        Z += crater
        return Z
    # 随机加几个环形山(位置、大小、深度随便调)
    for _ in range(15):
        cx = np.random.uniform(-0.8, 0.8)  # 环形山中心 x 坐标
        cy = np.random.uniform(-0.8, 0.8)  # 环形山中心 y 坐标
        r = np.random.uniform(0.05, 0.15)  # 环形山半径
        d = np.random.uniform(0.3, 0.8)    # 环形山深度
        Z = add_crater(X, Y, Z, cx, cy, r, d)
    # 加一点点随机噪声,让地形更自然
    Z += np.random.randn(size, size) * 0.02
    return X, Y, Z

def plot_moon_terrain():
    """画月球地形的 3D 图"""
    # 生成模拟地形数据
    X, Y, Z = generate_moon_terrain(size=200)
    # 创建设备(画布 +3D 子图)
    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, projection='3d')
    # 画月球表面:用灰色系配色,更像真实月球
    surf = ax.plot_surface(
        X, Y, Z, cmap=cm.gist_gray,
        linewidth=0, antialiased=True, alpha=0.8
    )
    # 加标签和标题
    ax.set_xlabel('经度(简化)', fontsize=12)
    ax.set_ylabel('纬度(简化)', fontsize=12)
    ax.set_zlabel('高程(km,相对值)', fontsize=12)
    ax.set_title('中秋观月:月球表面地形模拟(环形山清晰可见)', fontsize=14, pad=20)
    # 加颜色条:显示高程对应颜色
    fig.colorbar(surf, shrink=0.5, aspect=10, label='高程(相对值)')
    # 调整视角:让环形山看得更清楚
    ax.view_init(elev=30, azim=45)
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    plot_moon_terrain()

月球正面(咱们中秋看到的那面)有大片'月海'(平坦的暗色区域),背面全是环形山,运行结果如下: 代码里虽然没分正反面,但能直观看到:月球不是'光滑的球',而是被撞得坑坑洼洼的,这些坑是几十亿年前小行星撞的,记录了太阳系早期的历史。

2. 再做月相动画:看一个月月亮怎么变

我们还可以模拟一个月内月相的变化过程,生成一个类似延时摄影的效果。中秋只看一天的满月不过瘾,咱们用代码做个'延时摄影',把一个月的月相变化动画放出来,还能标上第几天。

# 1. 开头必须导入 patches 模块!
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
from matplotlib.animation import FuncAnimation

def create_moon_phase_anim(save_gif=True):
    """生成月相变化动画(Windows 适配版)"""
    # 准备画布:黑色背景(模拟夜空)
    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_xlim(-1.5, 1.5)
    ax.set_ylim(-1.5, 1.5)
    ax.set_aspect('equal')
    ax.axis('off')
    fig.patch.set_facecolor('black')
    ax.set_facecolor('black')
    # 动画核心:每帧更新月相(修正 Ellipse 调用)
    def update_frame(frame):
        ax.clear()
        ax.set_xlim(-1.5, 1.5)
        ax.set_ylim(-1.5, 1.5)
        ax.set_aspect('equal')
        ax.axis('off')
        ax.set_facecolor('black')
        # 计算月相和天数
        phase = frame / 100  # 0=新月,0.5=满月
        day = int(phase * 29.53) + 1
        # 1. 画月球本体(白色圆形)
        moon = plt.Circle((0, 0), radius=1, color='white', fill=True)
        ax.add_patch(moon)
        # 2. 画阴影(关键:用 Ellipse 类,从 patches 导入)
        if phase < 0.5:
            # 新月→满月:阴影从右侧消失
            shadow_width = 2 * (0.5 - phase)
            shadow = Ellipse((shadow_width/2, 0), width=shadow_width, height=2, color='black', fill=True)
            ax.add_patch(shadow)
        else:
            # 满月→新月:阴影从左侧出现
            shadow_width = 2 * (phase - 0.5)
            shadow = Ellipse((-shadow_width/2, 0), width=shadow_width, height=2, color='black', fill=True)
            ax.add_patch(shadow)
        # 标月相名称和天数
        phase_names = {0:'新月', 0.25:'上弦月', 0.5:'满月', 0.75:'下弦月'}
        closest_phase = min(phase_names.keys(), key=lambda x: abs(x - phase))
        phase_name = phase_names[closest_phase]
        ax.text(0, -1.3, f'朔望月第{day}天 | {phase_name}', color='white', ha='center', fontsize=14, weight='bold')
    # 生成动画
    anim = FuncAnimation(fig, update_frame, frames=100, interval=100, repeat=True, blit=False)
    # 保存动画
    if save_gif:
        anim.save('中秋月相变化.gif', writer='pillow', fps=10, dpi=100)
        print('GIF 已保存到当前文件夹:中秋月相变化.gif')
    else:
        plt.show()

if __name__ == "__main__":
    create_moon_phase_anim(save_gif=True)

这个动画展示了一个完整朔望月的月相变化,结果如下所示: 动画里有个细节:满月不一定在第 15 天,可能在第 16 天——这就是'十五的月亮十六圆'的原因。因为朔望月是 29.53 天,不是整数,新月出现的时间每天会延后一点,满月自然也可能延后到第 16 天。比如 2025 年的中秋(10 月 6 日),满月就可能在 10 月 7 日凌晨,正好对应了我们之前算过照亮程度是 94.91%。

五、总结

代码就分享到这里。这些项目大部分我自己跑过,效果还不错。

月相计算那个我刚才又跑了一遍,2025 年中秋(10 月 6 日)的月相值是 0.4745,照亮度 94.91%。虽然不是 100% 的满月,但 94.91% 已经很圆了,肉眼基本看不出来。这也验证了'十五的月亮不一定十五圆'这个说法!

今年虽然不是完美满月,但 94.91% 的圆度已经足够亮了。晚上记得出去看看,天气好的话应该挺漂亮的。

目录

  1. 一、月相计算:今晚的月亮到底有多圆
  2. 计算今年中秋(2025 年 10 月 6 日)的月相
  3. 1. 月相可视化
  4. 二、月饼切分算法:公平分配的艺术
  5. 1. 经典切分策略
  6. 2. 进阶问题:不过圆心的切分
  7. 三、诗词生成:中秋凑诗
  8. 训练数据示例
  9. 四、月球数据可视化:用数据看月亮
  10. 1. 先画月球表面:模拟环形山地形
  11. 先导入必须的工具:数值计算 + 绘图 +3D 绘图
  12. 2. 再做月相动画:看一个月月亮怎么变
  13. 1. 开头必须导入 patches 模块!
  14. 五、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Linux 网络基础:局域网通信与跨网段传输详解
  • 从 DDS 到 FPGA:信号发生器技术演进
  • Ubuntu 22.04 更换清华镜像源全流程
  • 国内环境部署 OpenClaw 个人 AI 助手搭建指南
  • Docker 部署 OpenClaw 常见报错排查与解决方案
  • 用双栈模拟队列:LIFO 到 FIFO 的转换艺术与实现
  • WordPress 设置固定链接后 Apache 无法访问网页的解决方案
  • 计算机学院校友网信息管理系统:SpringBoot+Vue+MySQL 架构与实现
  • 主流 AI 编程助手 Copilot 概览
  • 医疗 AI 场景下算法编程深度解析(一)
  • 基于百度天气 API 与 Leaflet 的 WebGIS 天气预报系统实现
  • 基于 C++ 的 AIGC 推理框架设计与性能优化
  • C++高性能计算实战:多线程 SIMD 内存池与并发数据结构
  • 深入源码:Spring Bean 作用域、生命周期与自动装配
  • 前端状态管理实战:从混乱到有序的最佳实践
  • Linux 下 C/C++ 调试器 GDB/Cgdb 实战指南
  • AI 编程技能(Skill)详解与 Java 方法生成实战
  • 六款高实用性 C# 开源项目推荐
  • Python 基础语法详解:变量、类型、运算符与交互
  • 位运算核心原理与实战应用

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online