非官方指南
OEIS编码
好的程序员有个人风格,坏的程序员也有。
在这篇文章中,我想解释一下程序部分中的好程序在我看来,OEIS数据库看起来很像,原因是什么。
我将仅通过示例继续,并在评论。当我生气时,我会不时更新此列表通过一些贡献或在我遇到好的例子时添加新的例子(许多很好示例当然已经在OEIS中了!)
这篇文章的目的是增强OEIS用户体验,尽管我的批评有时会毫不留情我知道我的标准和评级都是主观的。
此外,我必须承认,没有一条规则是我没有的违反了我的贡献。因此,写这篇文章首先是为了教育我自己。
第一个示例:A034895
A034895的定义是:“删除任何数字都会得到一个质数。”
丑陋的
P: =proc(q)局部d,k,n;对于从1到q的n,d:=ilog10(n);对于k从0到d do如果不是素数(trunc(n/10^(k+1))*10^k+(n mod 10^k)),则中断;fi;od;如果k=d+1,则打印(n);fi;od;结束:P(10^4);
更好
A034895_list:=proc(up_to)局部k,n,L,prime;L:=空;对于从1到up_to的n质数:=真;当素数do时,k从0到ilog10(n)素数:=i素数(trunc(n/10^(k+1))*10^k+(n mod 10^k));od;如果素数,则L:=L,n fi;od;[五十] 结束时间:A034895_列表(1673);
有什么区别?
- 进行格式化!
如果程序值得发布,那么也有足够的空间良好的格式。
- 使用系统名称。
这里是“A034895_list”而不是“P”。这样可以避免名称冲突将函数轻松地包含到更大的上下文或库中,而无需修改名称。名称还应指示返回的类型value:Aaaaaa(n)返回在n处计算的函数值,而Aaaaaa_list(len)返回长度序列的初始段len和Aaaaaa_list(up_to)的初始值小于或等于up_to。
- 不要在函数中使用“print”命令!
而是从函数中返回一个值(整数)或整数列表。然后打印这个列表-如果这是你想要的。当然在很多情况下人们只想处理列表,而打印语句则适得其反。
- 始终给出有意义的返回值。
例如,如果执行P(10),几乎什么都不会发生(可见)并且用户将被激怒。执行A034895_list(10)将返回[],空列表。
除此之外还有什么?
- 学习和使用计算机的语法和惯用风格你使用的语言!
例如,循环
对于k从0到d do如果不是质数(..),则打破fi;od;
是正确的,但不是所用语言的惯用语。Maple不是“C”,在这里提供了很好的构造
当质数为do时,k从0到d素数:=i素数(..);od;
这种逻辑也比“break”构造的逻辑被否定。
- 避免伪主题化!
使用适合当前问题的库函数。
A034895是一个基本序列。这是一个“不错”的基本序列,但那是品味问题。无论如何,基本序列并不是真正的数学。他们根植于符号表示和用于处理的计算工具它们首先是字符串处理工具。
在P中使用函数“ilog10”和“n mod k”并不完全合适。他们在没有数学的地方假装数学。我们也可以在没有它们,正如下面的变体所示,在我看来,这要诚实得多。
甚至更好
A034895_list:=进程(up_to)局部素数,k,n,m,j,k,L,S;S:=空;对于从1到up_to的nL:=换算(n,基数,10);质数:=真;当质数为do时,k从1到nops(L)K:=底土(K=NULL,L);m:=加(K[j]*10^(j-1),j=1..nops(K));素数:=isprime(m)od;如果素数,则S:=S,n fi;od;[S] 结束时间:
然而,这并不是编码A034895的最佳方法。在继续之前,我们切换为了对A051362进行排序,Jaime Gutierrez将其命名为“超级实时数字”。唯一的区别是,我们希望原始数字也是质数。全部我们要做的是将第6行中的“prime:=true”替换为“prime=isprime(n)”。关于数学。StackExchange对此序列进行了非常愉快的讨论删除任何数字都会产生素数.
列表和筛选器(选择)。
|
|
还有一个问题尚未解决:为什么原始作者选择实现作为列表的函数?
实现返回列表而不是返回单个项目是效率。如果计算单个与计算列表然后实现列表是自然的。然而,如果不是这样,则工作更灵活带过滤器。
|
在我们的示例中,这实际上是为了从并将“A051362_list”重命名为“is_A051362”。
最佳枫树
is_A051362:=进程(n)局部L,K,j,K,m,素数;L:=换算(n,基数,10);素数:=i素数(n);当质数为do时,k从1到nops(L)K:=底土(K=NULL,L);m:=加(K[j]*10^(j-1),j=1..nops(K));素数:=i素数(m)od;优质端:
为了重新引入函数A051362_list,我们现在定义:
A051362_list:=n->选择(is_A051362,[$1..n]);
我认为实现的简洁性和可读性的提高可见。
OEIS列为软件设计模式
OEIS有两种主要类型的序列,即正常序列如n^2和“lists”,它们枚举具有某些特殊属性的整数比如素数列表。实现序列的第一步识别序列的类型。
在我们的示例中,我们查看了具有删除任意数字属性的数字根据十进制数表示法,给出了质数。因此,我们正在查看OEIS意义上的列表。
我们希望将实现模式与OEIS列表相关联。根据维基百科,软件设计模式“是关于如何解决可以使用的问题的描述或模板在许多不同的情况下。模式已正式化程序员在设计时可以用来解决常见问题的最佳实践实现。”
在上面的讨论中,我们概述了这种实现模式。 粗略地说:如果序列是一个列表,那么实现分两步完成功能:
- 实现integer->boolean类型的isAaaaaa函数它回答了整数n是否具有Aaaaaa属性。
- 基于isAaaaaa实现函数Aaaaaa_list(up_to)函数过滤范围(1..up_to)中满足以下条件的值财产。
这是筛选(或选择)模式,我们建议将其作为模板实施OEIS清单。
圣人的喜悦
根据经验法则,OEIS中99%的序列实现不需要Mathematica或Maple提供的更复杂的能力超级用户。既然有开源软件,为什么要使用Maple或Mathematica软件圣人?请注意,您可以开始使用Sage,而无需通过使用云中圣人,您只需要浏览器。除此之外,像您一样安装Sage也没有任何麻烦可以下载二进制文件来自附近大学的服务器。
Sage使用蟒蛇作为一种基础语言,它强调代码的可读性,并使代码清晰简洁程序。这正是应该呈现的节目类型在OEIS中。
Sage版本
定义为_A051362(n):素数=is_prime(n)如果是素数:L=ZZ(n).数字(10)对于范围内的k(len(L)):K=L[:];del K[千]素数=is_prime(ZZ(K,基数=10))如果不是素数:break返回素数A051362_list=λn:过滤器(is_A051362,范围(n))
当然,也可以在A034895上铺设A051362层。要做到这一点,只有将过滤器的范围限制为质数。假设质数也是由过滤器(谓词“isprime”)生成的实际上相当于作文个筛选器。事实上,很容易组合过滤器的可能性是最大的这种模式的优点。
使用SageIPython公司提供了一个很好的、几乎类似数学的工作流(格式更好)。 在IPython笔记本中,函数如下所示:
当我们谈论Sage时,我们可以看看另一个过滤器实现模式的示例这表明该系统与OEIS集成得多么好。请注意斯隆。Annnnnn公司功能(主要由Jaap Spies编写)内置于Sage中。
第二个例子:具有常数的线性递归。
类似公式
a(n)=(5*(1-(-1)^n)+2*n*n)/4
很容易计算。然而,作为一个序列,以下形式更具启发性:
a(n)=2*a(n-1)-2*a(n-3)+a(n-4)。
它显示了序列中的一个术语如何依赖于前面的术语。这是一个常系数线性递归的例子。这里的常数是[a(n-1),a(n-2),a,即(2,0,-2,1)。
以递归形式书写a(n)的优点很明显:它展示了序列的结构肉眼看不到公式使用这种形式在计算上是有利的。闭合公式需要3次乘法、2次加法、,1项(-1)^n的划分和评估。相反,递归公式需要2次乘法和2个附加项。所以效率更高。
在Sage中,重复可以这样实现:
定义G():a、 b、c、d=0、3、2、7为True时:产量aa、 b、c、d=b、c和d、a+2*(d-b)A245581=G()[A245581.next()用于范围(20)中的n]
我们还没有提到,但很明显我们还需要4个初始值才能开始。所以我们可以通过调用生成器来概括程序使用初始值。
定义G(A、B、C、D):a、 b、c、d=a、b、c、d为True时:产量aa、 b、c、d=b、c和d、a+2*(d-b)
在这种形式中,程序表示OEIS!
A001840、A001859、A002620、A00262、A004652、A006578、A007590、A014616、,A033638、A035608、A047838、A061925、A077043、A085622、A092634、A097063、,A102214、A110907、A114113、A121470、A137928、A139595、A142717、A164486、,A173511、A174929、A176222、A179272、A184005、A194073、A195605、A209350、,A245578、A245581
所有这些序列都是以相同的方式计算的,不同之处仅在于抛出的初始术语。例如G(0,3,2,7)是A245581,G(1,10,18,22)是A245578和G(1,0,1,5)是A209350。你可以找到数千个这样的例子在中列出OEIS索引.
顺便说一下,序列G(0,3,2,7),作为一个实函数,有一个很好的属性:它与y轴对称。事实上,恒等式a(n)=a(-n)适用于序列A245581。
“Prog”部分不是皮条客脚本库!
一些用户提交看起来像贡献的脚本臭名昭著的“Pimp My Formula”电视节目。如数据库所示:
[seq(cel(二项式(n+1,1)*2^(n-1)),n=-1..29)];
作者不尊重偏移量(0),显然不知道什么二项式(n+1,1)是,或确实知道,但想让事情看起来更像很有趣。函数“二项式”和函数'ceil'在这里无论如何都是必要的。不仅没有理由使用函数“ceil”,而且它的使用也有误导性:它表明该定义基于非整数,但事实并非如此。扔进垃圾桶!
推荐形式:
A057711:=n->`如果`(n<3,n,n*2^(n-2));
- 不要强化脚本。让它们变得简单!
低级脚本是识别平庸程序员的标准。
- 如果不需要,请不要使用库函数!
实施数学公式,不要复制它们不假思索。
一个稍微刺激智力的例子是:
选择[Prime@Range[2,110],JacobiSymbol[-1,#]==-1&]
Jacobi符号是一个非平凡的函数,实现需要付出非平凡的努力,如您所见在这里.在事件中不清楚作者想要计算什么,这就是解决方案:4n+3形式的素数. 然而,为什么作者建议这个特定的程序,人们只能猜测:“雅各比”这个名字确实起到了装饰作用每个脚本。但是使用它合适吗?或者它只是一个皮条客脚本?
在我看来,这个脚本不应该进入尤其是数据库,因为已经有了一个完美的解决方案阿隆索·德尔·阿特(Alonso del Arte):
选择[4范围[150]-1,PrimeQ]
Sage的推荐形式:
A002145_list=λn:过滤器(is_prime,范围(3,n,4))
为了避免任何误解:对于形式为4n+3的素数p恒等式Legendre(-1,p)=-1成立(因为p=-1(mod 4)时,(-1)^((p-1)/2))=-1)值得注意,但仅在注释或公式中第A002145节,而不是作为计算方法。