月亮 / 月相算法
有没有人知道一种算法,可以计算某个日期的月相或者月龄,或者找出某一年新月和满月的日期?
我在网上查了一下,发现答案在某本天文学的书里,但我不想为了这一页内容去买整本书。
更新:
我应该更清楚地说明我查找的情况。我确实找到了一些解决方案,但它们只适用于某个特定的时间段(比如1900年代);还有一些基于三角函数的解决方案,计算起来比较复杂,不太符合我的需求。
S Lott在他的Python书里有几种算法,可以计算某一年复活节的日期,大多数算法都不超过十行代码,有些适用于整个公历的所有日期。找到三月份的满月是计算复活节的一个关键步骤,所以我想应该有一种算法,不需要用到三角函数,并且适用于公历的所有日期。
7 个回答
另外,pyephem — 科学级的天文学计算工具 [PyPI],这是一个Python的包,但它的核心计算部分是用C语言写的,而且它确实有说明:
从-1369到+2950的精度小于0.05秒。
使用查表的方法来减少对三角函数的调用。
如果你和我一样,想做一个谨慎的程序员,那么当你看到网上那些看似能解决复杂天文问题的随机代码时,心里肯定会有点紧张,因为这些代码并没有解释为什么它们的解决方案是正确的。
你相信一定有一些权威的来源,比如书籍,里面包含了经过仔细推敲和完整的解决方案。例如:
Meeus, Jean. 《天文算法》。Richmond: Willmann-Bell, 1991. ISBN 0-943396-35-2.
Duffett-Smith, Peter. 《用计算器进行实用天文学》。第3版. 剑桥: 剑桥大学出版社, 1981. ISBN 0-521-28411-2.
你会信任那些广泛使用、经过充分测试的开源库,因为它们的错误可以被修正(而静态网页就不行)。接下来,这里有一个基于PyEphem库的Python解决方案,使用了月相接口。
#!/usr/bin/python
import datetime
import ephem
from typing import List, Tuple
def get_phase_on_day(year: int, month: int, day: int):
"""Returns a floating-point number from 0-1. where 0=new, 0.5=full, 1=new"""
#Ephem stores its date numbers as floating points, which the following uses
#to conveniently extract the percent time between one new moon and the next
#This corresponds (somewhat roughly) to the phase of the moon.
#Use Year, Month, Day as arguments
date = ephem.Date(datetime.date(year,month,day))
nnm = ephem.next_new_moon(date)
pnm = ephem.previous_new_moon(date)
lunation = (date-pnm)/(nnm-pnm)
#Note that there is a ephem.Moon().phase() command, but this returns the
#percentage of the moon which is illuminated. This is not really what we want.
return lunation
def get_moons_in_year(year: int) -> List[Tuple[ephem.Date, str]]:
"""Returns a list of the full and new moons in a year. The list contains tuples
of either the form (DATE,'full') or the form (DATE,'new')"""
moons=[]
date=ephem.Date(datetime.date(year,1,1))
while date.datetime().year==year:
date=ephem.next_full_moon(date)
moons.append( (date,'full') )
date=ephem.Date(datetime.date(year,1,1))
while date.datetime().year==year:
date=ephem.next_new_moon(date)
moons.append( (date,'new') )
#Note that previous_first_quarter_moon() and previous_last_quarter_moon()
#are also methods
moons.sort(key=lambda x: x[0])
return moons
print(get_phase_on_day(2013,1,1))
print(get_moons_in_year(2013))
这个代码会返回
0.632652265318
[(2013/1/11 19:43:37, 'new'), (2013/1/27 04:38:22, 'full'), (2013/2/10 07:20:06, 'new'), (2013/2/25 20:26:03, 'full'), (2013/3/11 19:51:00, 'new'), (2013/3/27 09:27:18, 'full'), (2013/4/10 09:35:17, 'new'), (2013/4/25 19:57:06, 'full'), (2013/5/10 00:28:22, 'new'), (2013/5/25 04:24:55, 'full'), (2013/6/8 15:56:19, 'new'), (2013/6/23 11:32:15, 'full'), (2013/7/8 07:14:16, 'new'), (2013/7/22 18:15:31, 'full'), (2013/8/6 21:50:40, 'new'), (2013/8/21 01:44:35, 'full'), (2013/9/5 11:36:07, 'new'), (2013/9/19 11:12:49, 'full'), (2013/10/5 00:34:31, 'new'), (2013/10/18 23:37:39, 'full'), (2013/11/3 12:49:57, 'new'), (2013/11/17 15:15:44, 'full'), (2013/12/3 00:22:22, 'new'), (2013/12/17 09:28:05, 'full'), (2014/1/1 11:14:10, 'new'), (2014/1/16 04:52:10, 'full')]
我之前把一些代码移植到了Python上。原本打算直接给个链接,但没想到这段时间它已经下线了,所以我只好把它找出来重新上传。你可以查看这个moon.py,它是基于John Walker的moontool制作的。
我也找不到这个代码在什么时间段内是准确的参考资料,但看起来作者们做得相当严谨。这意味着代码确实使用了三角函数,但我想不出有什么用途会让它的计算变得特别复杂。实际上,调用Python函数的开销可能比三角运算的成本还要高。计算机在计算方面的速度是相当快的。
代码中使用的算法来源于以下几本书:
Meeus, Jean. Astronomical Algorithms. Richmond: Willmann-Bell, 1991. ISBN 0-943396-35-2.
这是一本必备的书;如果你只买一本书,确保就是这本。书中的算法是以数学形式呈现的,而不是作为计算机程序,但你可以单独从出版社订购实现书中许多算法的源代码,语言包括QuickBasic、Turbo Pascal或C。Meeus提供了许多计算的实例,这对调试你的代码非常重要,并且经常会展示几种算法,这些算法在准确性、速度、复杂性和长期(世纪和千年)有效性之间有不同的权衡。
Duffett-Smith, Peter. Practical Astronomy With Your Calculator. 3rd ed. Cambridge: Cambridge University Press, 1981. ISBN 0-521-28411-2.
尽管书名中有“计算器”这个词;但如果你对开发计算行星位置、轨道、日食等软件感兴趣,这本书是个很有价值的参考。书中提供了比Meeus更多的背景信息,帮助那些对天文学不太熟悉的人理解常常令人困惑的术语。这里给出的算法比Meeus的简单且准确性稍差,但对于大多数实际工作来说是足够的。