为什么写下数学证明比编写计算机代码更能抵御故障?


190

我注意到,我发现写下数学证明而不犯任何错误要比写下没有错误的计算机程序容易得多。

似乎这不仅仅是我的经验。大多数人在编程时总是会犯软件错误,而且他们让编译器始终告诉他们错误是什么。我从未听说过有人一口气编写大型计算机程序,但一无所错,并且完全相信它不会出错。(实际上,几乎没有任何程序是没有错误的,甚至包括许多高度调试的程序)。

然而,人们可以写整篇论文或数学证明书,而无需任何编译器向他们反馈自己犯错的反馈,有时甚至无法从他人那里得到反馈。

让我清楚一点。这并不是说人们不会在数学证明中犯错误,但是对于即使是有经验的数学家来说,错误通常也不是问题,并且可以在没有诸如编译器指向您的编译器的“外部oracle”帮助下解决。错误。

实际上,如果不是这种情况,那么在我看来数学几乎是不可能的。

因此,这引发了我一个问题:编写无故障的数学证明和编写无故障的计算机代码有什么不同,从而使前者比后者更易于处理?

可以说,事实是人们拥有编译器的“外部预言”,使他们指出自己的错误,这使程序员变得懒惰,从而阻止他们执行严格编写代码所必需的工作。这种观点意味着,如果他们没有编译器,他们将能够像数学家一样完美无缺。

您可能会发现这很有说服力,但是基于我的编程经验和写下数学证明,在我看来,这并不是真正的解释。两项工作似乎在根本上有所不同。

我最初的想法是,可能会有区别,对于数学家来说,正确的证明仅要求每个逻辑步骤都是正确的。如果每个步骤都正确,则整个证明都是正确的。另一方面,要使程序无缺陷,不仅每一行代码都必须正确,而且它与程序中每行其他代码的关系也必须正常工作。

换句话说,如果步骤中证明是正确的,那么在制作工序中的错误不会弄乱一步如初。但是,如果正确记录了一行代码,则在行犯错误将影响行的工作,因此,每当编写行,都必须考虑其与所有其他行的关系。我们可以使用封装以及所有类似的东西来限制这种情况,但是不能完全删除它。XYX Y X XXXYXX

这意味着检查数学证明中的错误的过程在证明步骤的数量上基本上是线性的,但是检查计算机代码中的错误的过程在代码行数上基本上是指数的。

你怎么看?

注意:该问题有大量答案,可探讨各种事实和观点。在回答之前,请阅读所有内容并仅在有新添加内容时回答。多余的答案或不支持事实的观点的答案可能会被删除。


3
您是否知道程序的正确性证明,无论是纸上证明还是定理证明中的机械化证明?两者都存在并且与您的更新矛盾。的确是,如常传授的编程与带有正确性证明的编程几乎没有关系。
Blaisorblade

76
让我想起了Knuth的报价,我想“当心上面的代码!我只证明了它是正确的,我从未测试过”
Hagen von Eitzen 17/12/12

评论不作进一步讨论;此对话已转移至聊天
吉尔斯

7
给我找一个手写的数学证明,它长1亿行,没有“ bug”,我将为您提供我拥有的所有东西。
Davor

功能程序比证明更容易编写,但是,一旦状态出现,困难
就会激增

Answers:


226

让我提供一个原因和一个误解来回答您的问题。

主要原因,这是比较容易写(貌似)正确的数学证明的是,他们是在一个非常高的水平写的。假设您可以编写如下程序:

function MaximumWindow(A, n, w):
    using a sliding window, calculate (in O(n)) the sums of all length-w windows
    return the maximum sum (be smart and use only O(1) memory)

用这种方式进行编程将更容易出错,因为程序的说明比其实现要简洁得多。确实,每个试图将伪代码转换为代码(特别是高效代码)的程序员都在算法思想及其实现细节之间遇到了巨大的鸿沟。数学证明更多地集中在思想上,而不是细节上。

数学证明代码的真正对应物是计算机辅助证明。这些比通常的文本证明更难开发,并且人们经常发现各种隐藏的角落,这些角落对于读者(通常甚至不会注意到它们)“很明显”,但对计算机却不那么明显。同样,由于计算机目前只能填补相对较小的空白,因此必须将证明的详细程度提高到使阅读它们的人会错过森林而无法树木的程度。

一个重要的误解是数学证明通常是正确的。实际上,这可能是相当乐观的。编写没有错误的复杂证明非常困难,并且论文中经常会包含错误。也许最有名的最近的案例是怀尔斯在第一次尝试(特例)模块化定理(这意味着费尔马大定理),以及各种差距在有限单群分类,其中包括一些1000多页quasithin组这是在分类完成20年后写的。

Voevodsky的一篇论文中的一个错误使他对书面证明产生了极大的怀疑,以至于他开始发展同型理论,这是一种对正式发展同型理论有用的逻辑框架,从此以后就使用一台计算机来验证他的所有后续工作(至少根据他的本人)。入场)。尽管这是一个极端的(目前是不切实际的)立场,但仍然存在这样的情况,即使用结果时,应该检查证明并检查它是否正确。在我所在的地区,有几篇已知有误但从未被撤回的论文,其状态在专家中间从嘴到耳朵。


评论不作进一步讨论;此对话已转移至聊天
DW

1
将来是否有可能使用证明助手来检查您的代码和证明?也许是时候学习一个阿格达了吗?(对不起...)
Alex Vong

3
@AlexVong这样做的一个问题是,为非平凡的代码编写正式的规范(以便您可以验证代码确实满足该规范)几乎是不可能的。例如,您能想象浏览器的正式规范会变得多么复杂(包括所有用户交互,所有受支持的文件格式和协议等)吗?
svick

2
@svick是正确的,对于用户交互,有时甚至不清楚什么是正确的行为。因此,我们应该专注于具有适当形式规范的内容(例如,证明,编译器)。
Alex Vong

1
确实。这也可能解释了为什么许多人会发现,与使用高级抽象语言进行编码相比,使用低级语言进行编码更加繁琐且有趣得多。当然,尽管这可能因人而异(当然,有些人可能甚至更喜欢构建非常低级的硬件/电子电路,而不是编写在其上运行的软件?)而且,在许多情况下,低级代码仍然是不可替代的并且编写得很好本身就是一项值得称赞的稀缺技能/壮举)。
xji

77

(我可能在这里冒几票的风险,因为我没有时间/兴趣来做出一个正确的答案,但是我发现下面引用的文本(以及所引用的文章的其余部分)非常有见地,并且考虑到它们是书面的由一位著名的数学家撰写。也许以后我可以改善答案。)

我认为这个想法与现有答案并没有特别的区别,是“证明”或论点传达给数学界,其目的是说服他们相信(繁琐的)细节可以在原则上得到填补,获得完整指定的形式证明-无需经常这样做。一个重要的例子是您可以通过简单地陈述现有定理来使用它们,但是代码重用通常更具挑战性。还要考虑较小的“错误”,它们可能完全使一段代码无用(例如,它为SEGFAULTs),但可能会使数学参数保持完整(即,如果可以在不折叠参数的情况下包含错误)。

使计算机程序完全运行所必需的正确性和完整性标准比数学界的有效证明标准高两个数量级。但是,即使大型计算机程序经过非常仔细的编写和非常仔细的测试,它们似乎也总是存在错误。daccess-ods.un.org daccess-ods.un.org在我们实践中,数学比其他科学在形式上更加完整和精确,但是在形式上却不如计算机程序那么完整和精确。差异不仅仅在于努力的数量:努力的种类在质量上也不同。在大型计算机程序中,必须在巨大的兼容性问题上花费大量的精力:确保所有定义都是一致的,开发“良好”的 具有有用但不繁琐的通用性的数据结构,决定了功能等的“正确”通用性。与簿记部分不同,花费在大型程序的工作部分上的能量比例惊人地小。由于兼容性问题几乎不可避免地会升级,因为“正确”的定义会随着添加通用性和功能而改变,因此通常需要经常从头开始频繁地重写计算机程序。

关于数学的证明和进展(第9-10页),作者威廉·P·瑟斯顿(WILLIAM P.THURSTON) https://arxiv.org/pdf/math/9404236.pdf


3
关于“代码重用”的观点很恰当。将较长的证明从俄语译成英语需要大量的打字 ; 但是将大型计算机程序从C ++转换为Java需要花费大量的思考。同样,在古希腊语中恢复具有3000年历史的证明几乎是一样容易的。在PL / 1中恢复一个已有30年历史的程序几乎同样困难。
Quuxplusone

2
古希腊的例子也使我意识到:计算机程序员使用大量的本地语和口语化语言,例如(void*)1open('/dev/null'),甚至在不同亚文化之间都不是可移植的,更不用说翻译成目标语言了。(读者只是有点儿不得不依靠长期的经验来理解它们的近似语义。)我认为数学证明中很少包含这种“ s语”。如果证明使用单词,那么读者应该以某种方式推论其实际的普遍含义。计算机程序甚至没有通用的含义!
Quuxplusone

1
+1,因为作为建构主义者,与任意大数值不同的的猖ramp推定使我发疯。当数学家开始谈论无限级数,然后根据这些级数进行论证时,这会从值级谬误上升为逻辑谬误,并通过隐藏的谬误按比例产生错误。000
纳特

@Nat您能详细说明吗?我不明白
Gregory Magarshak '18年

@GregoryMagarshak 这个答案展示了一个案例,假设无限的有效序列构造会导致谬误,我将其描述为隐藏的-谬误00(Wikipedia中较低的“ 伪装 ”版本)部分)。古典数学家可能会说,错误是假设无限级数收敛,尽管建构主义者将谬误描述为无穷无穷的无穷推定。
纳特

55

请允许我先引用EW Dijkstra:

“程序设计是应用数学中最困难的分支之一;较贫穷的数学家最好还是纯粹的数学家。” (从EWD498起)

尽管Dijkstra与“编程”的含义与当前用法有很大不同,但此引用仍有一些优点。其他答案已经提到,允许数学的抽象水平比编程的水平高很多,这意味着我们可以忽略一些棘手的部分。

但是,我认为这仅仅是证明和计算机程序之间更根本的区别而导致的,这就是它们的目的

数学证明的主要目的之一是,使自己相信数学要求是正确的,并且甚至更重要的是,获得理解。因此,您可以选择只在数学世界中工作,在该世界中,所有事物都是创造出来的,这样可以通过设计达到理解的目的(尽管有些学生要求有所不同……)这正是Dijkstra所说的“纯数学家”的意思, (几乎)只关心数学事实并了解其性质。

因此,提供正确的证明是相对可靠的,您应该不会感到惊讶:这是整个“练习”的重点。(不过,他们说,这并不意味着错误不存在或几乎不存在,错误只是人为原因)

现在,如果我们考虑编程,我们的目的是什么?我们真的不求理解,我们想要的东西的工作。但是什么时候“起作用”?当我们成功创建了某种东西后,某些东西便可以工作,这使得某些奇怪的机器可以完成我们希望它完成的任务,并且最好也很快。

我认为,这是根本的区别,因为这意味着我们的目标不能简单地表述为程序“证明”的某些定理,而是我们想要的是现实世界(无论是什么)中的某种东西,而不是某种数学上的人工产物。这意味着我们不能纯粹从理论上实现我们的目标(尽管Dijkstra会无视您的尝试),因为我们必须安抚机器,希望我们实际上知道我们要执行的任务,并且要知道尚未考虑但尚未发生的事情不知何故。

因此,最后,除了尝试并可能失败,修复,失败并重试,别无选择,直到我们对结果感到满意为止。


请注意,编写无故障证明的假设比无故障程序(实际上@Ariel指出的是不同的语句)要简单得多,因为其证明通常是通过某种程度的尝试和错误构造的。不过,我还是希望这能为所隐含的问题提供一些启示:“证明某些定理与编写程序之间的真正区别是什么?” (库里-霍华德书信的粗心观察者可能会说:“一无所有!”)


正如@wvxvw在评论中提到的,“结构化编程说明”(EWD249,第21页)中的以下段落非常相关:

(...)程序本身绝不是目标;程序的目的是唤起计算,而计算的目的是建立所需的效果。尽管该程序是程序员制作的最终产品,但它所引发的可能的计算-程序的“制作”则留给机器!-是他交易的真正主题。例如,每当程序员声明自己的程序正确时,他就对可能引起的计算进行断言。

(...)从某种意义上讲,程序的制定比数学理论的制定更困难:程序和理论都是结构化的,永恒的对象。但是,尽管数学理论按其现状是有意义的,但该程序仅通过其执行才有意义。


2
我只是一个外行;Dijkstra真正通过“编程”指的是什么?
Ovi)

2
@Ovi我不确定,但主要区别是他谈论的(非平凡的)算法问题比“一般”编程任务更多,也就是说,他当然不是在谈论某些需要连接某些程序的CRUD程序。现有的体系结构或其他组件等。有关此问题的
离散蜥蜴

3
引用Dijkstra表示支持,但您选择的地方错误!他在结构化编程的第一段中写了很多有关此问题的文章。我不想通过提交不同的报价来改变您的答案,但我希望您可以考虑在该论文的答案中添加更多内容!
wvxvw

@Ovi我对您的问题的猜测是,在Dijkstra时代,编程通常意味着编写汇编代码,而不是现代高级语言。同样,我正在阅读1974年版的Mythical Man-Month,目前仍在阅读这些概念,但技术参考是系统级汇编器或PL / I,与当今大多数人认为的编程有很大不同
JimLohse

46

Lamport为在如何编写证明(第8-9页)中证明错误的普遍性提供了一些依据:

大约20年前,我决定为数学入门课编写Schroeder-Bernstein定理的证明。我能找到的最简单的证明是在Kelley的经典通用拓扑文本中。由于凯利(Kelley)为更复杂的读者而写,所以我不得不在他的半页证明书上增加很多解释。当我意识到凯利的证明是错误的时,我写了五页。最近,我想用令人信服的错误证明来说明关于我的证明风格的讲座,因此我转向Kelley。他的证明我没有发现错。看来显然是正确的!阅读并重新阅读证明使我确信,我的记忆力不佳,或者二十年前我很愚蠢。尽管如此,Kelley的证明很短,可以作为一个很好的例子,所以我开始将其重写为结构化证明。

...这种风格是我在与马丁·阿巴迪(Martin Abadi)撰写的一篇论文中首次应用于普通定理的证明。他已经写过常规证明,这些证明足以说服我们,甚至可以说服裁判员。我们以结构化的风格重写了证明,尽管定理是正确的,但我们发现几乎每个人都有严重的错误。在我们的下一次合作中,对不正确的证明不会导致不正确的定理的任何希望都被摧毁了。我们会一次又一次地做出一个猜想,并在黑板上写一个证明草图(该草图可以很容易地变成有说服力的常规证明),然后通过尝试编写结构化证明来发现该猜想是错误的。从那时起,如果没有经过仔细的结构化证明,我再也不会相信结果。


6
同一篇论文:“轶事证据表明,在数学期刊上发表的论文中,多达三分之一包含错误-不仅是小错误,而且还有不正确的定理和证明”。好吧,那是在90年代,但是今天有什么不同吗?可能那些日子里存在的那些论文仍然存在,并且一切都堆积起来...因此,我并不完全相信论文中提供的数学证明包含的错误更少。
MarkokraM

令人着迷的轶事,但我没有看到它直接回答或参与了这个问题。您是否要修改答案以更直接地回答所提出的问题?您是否认为数学证明与编写计算机代码一样有错误?您是否可以提供进一步的证据?一两个轶事并不能真正证明这一点,对吗?
DW

@DW如果您可以为莱斯利提供进一步的证据,我会向莱斯利发送电子邮件。
MarkokraM

3
@DW Leslie在真诚的回答中说,他的同事对当时发表在Math Reviews上的51个证明进行了调查。在他看来,这不只是传闻,但由于多个事实,不适合有力的证据。案例更为复杂,因为使用了错误的证明,舞会,以前发表的论文等,导致出现了一些关于证明的错误。这将是一个很好的研究主题,但需要做很多工作。如何以编程方式验证数学证明仍然是一个巨大的问题。交互式证明帮助的应用程序还处于早期阶段。至少它们的接口。
MarkokraM

@DW这则或两则轶事演示了数学证明如何看起来“正确”但实际上是不可靠的。对于既写了复杂的计算机算法又做了数学证明,并试图写出像数学证明一样的计算机算法,然后发现高级“算法”如何被细节中的许多错误所背叛的人一点也不奇怪。
Yakk

39

一个很大的不同是,程序通常编写为在输入上运行,而数学证明通常从一组公理和先验定理开始。有时,您必须涵盖多个极端情况才能获得足够的一般性证明,但是这些情况及其解决方案已被明确枚举,并且结果的范围隐含地局限于所涵盖的情况。

将此与计算机程序进行比较,该计算机程序必须为一系列可能的输入提供“正确”的输出。几乎不可能列举所有输入并尝试所有输入。更糟糕的是,假设该程序与人互动并允许他们的输入修改功能?众所周知,人类是不可预测的,并且在与人类互动的合理的大型程序中可能输入的数量以惊人的速度增长。当失败是唯一的选择时,您需要尝试预见程序的所有不同使用方式,并尝试使所有这些用例以合理的方式起作用或至少以合理的方式失败。并假设您甚至知道在所有那些晦涩的角落情况下应该如何工作。

最后,大型程序实际上不能与单个证明进行比较,即使是复杂的证明也不能与之相比。大型程序可能更类似于收集和审查小型文献库,其中一些文献可能存在您需要解决的错误。假设对于更具证明性的程序(可能是小的算法实现)而言,经验丰富的软件工程师可以/完成它们而不会犯错误,尤其是在使用可防止/解决常见的小错误(例如拼写错误)的现代工具时),相当于您在校对中要解决的早期问题。


14
上一段+1。虽然数学上的证明原则上是相互叠加的,但通常基础知识是易于理解的,类似于计算机库(尽管它们也有错误……),而实际证明并不算长。相反,消费者软件又长又复杂,因此有更多的失败机会。
Yuval Filmus

6
在实践中,消费类软件的另一个问题是,“正确”的行为通常在开始时就定义不当,因此以后曾经正确的行为是错误的。这就像试图写证明只是为了发现人们决定改变公理一样。您可以用符号来解决,对不对?
丹·布莱恩特

2
@DanBryant这种情况确实发生在数学中。特别是,术语的定义会随着时间的推移而变化,并且即使使用时也常常含糊不清。伊姆拉卡托斯(Imre Lakatos)的“证明与驳斥”用术语“多边形”来描述这一点。类似的事情发生在“功能”上,在较小的程度上是“整体”。即使在今天,“类别”也不是明确的,证明和定理可能会失败,具体取决于您的意思。
德里克·埃尔金斯

25

他们说计算机的问题在于它们完全按照您的指示去做。

我认为这可能是许多原因之一。

请注意,对于计算机程序,编写器(您)很聪明,而读取器(CPU)却很笨。
但是有了数学证明,作者(您)很聪明,读者(评论者)也很聪明。

这意味着您永远无法承受使用计算机进入“嗯,您知道我的意思 ”的情况。它完全按照您说的做,却不知道您的意图。

例如,假设这是一些证明的步骤:

x2+4x+3x+3=(x+1)(x+3)x+3=x+1

如果您告诉计算机评估然后除以,则当您让时,它将在您的程序中阻塞。但是数学家不会不必要地陷入这一点。他会意识到,这很可能与您要提出的观点无关,并且放弃并抱怨这个小问题对任何人都没有帮助。他会明白你的意思的,因为他不只是跟着你。他了解你。当您的读者很聪明时,就很难失败。x + 3 x = 3x2+4x+3x+3x=3


3
好答案!除了作为计算机,我反对您使用“不必要”一词。;)[假设这只是旨在证明其-x为合成的更大证明的一个步骤。这个步骤什么时候是错误的事实-x = 3与完成的证明的正确性高度相关!]
Quuxplusone

@Quuxplusone:= P
Mehrdad

计算机也可以使用符号数学和非确定性重写规则,只是我们使用的语言(例如C ++)都非常低级并且基于古老的技术(例如,C的功能少于Algol 60)。唯一的例外是证明/检查语言,例如Idris / Agda,带有符号求解器的Lisp和Mathematica。 ja.wolframalpha.com/input/...
aoeu256

23

我认为Yuval的答案未解决的一个问题是,您似乎在比较不同的动物。

说“代码正确”是一个语义声明,您的意思是说您的代码描述的对象满足某些属性,例如,对于每个输入它计算。这确实是一项艰巨的任务,要解决这个问题,人们不仅要看语法。您对此的数学类比是“该证明是正确的”,这很难说是公平的,因为正确的类比应该是“该定理是正确的,如果可以,我们如何证明它?”。尽管前者是一个语法声明,可以“轻松”验证,但后者要难得多(可以将其形式化)。考虑一下您曾经尝试过的最复杂的代码,并将其与试图证明庞加莱的猜想(现在是定理)进行比较,这应该给您不同的比例。n nn!

验证程序的语义属性是不确定的(里斯定理),类似地,检查一阶谓词逻辑中的语句是否为真也是不确定的。关键是硬度与您解决问题的方式没有真正的区别。另一方面,我们可以推理程序(编译器)的语法属性,这类似于可以验证证明的事实。错误(代码没有实现我想要的功能)是语义的,因此应将它们与正确的错误进行比较。

我将加强Yuval的作用,并说整个领域的发展都是出于编写可以在某种正式系统中编写和验证的数学证明的动机,因此,即使验证过程也不是一件容易的事。


18

编写完美的数学证明和编写完美的计算机代码有什么不同,从而使前者比后者更容易处理?

我认为主要原因是幂等(对于相同的输入给出相同的结果)和不变性(不变)。

如果数学证明在星期二或当年份从1999年发展到2000年时可以给出不同的结果怎么办?如果数学证明的一部分是返回几页,重写几行,然后从这一点重新开始怎么办?

我敢肯定,这种证明几乎像普通的计算机代码段一样容易出现错误。

我也看到其他次要因素:

  1. 在尝试编写重要/可发布的证明之前,数学家通常受过更高的教育。自称职业开发人员的1/4不到6年前就开始编写代码(请参阅2017SO调查),但我认为大多数数学家都接受了十年以上的正规数学教育。
  2. 数学证明很少像计算机代码那样受到严格的审查。单个错字可能会/将破坏程序,但是数十个错字可能不足以破坏证明的价值(仅仅是其可读性)。
  3. 细节中有魔鬼,计算机代码无法跳过细节。证明可以随意跳过被认为简单/常规的步骤。现代语言中有一些不错的语法糖,但是这些糖都是硬编码的,相比之下非常有限。
  4. 数学较老,具有更坚实的基础/核心。当然,数学中有许多崭新的,令人眼花sub乱的子领域,但是大多数核心原理已经使用了数十年。这导致稳定性。另一方面,程序员仍然在基本编码方法上存在分歧(只问敏捷开发及其采用率)。

值得一提的是,编程等效于“幂等”是功能纯净,它在某些语言(例如Haskell)中得到认可和支持。
法拉普

12

我同意尤瓦尔的著作。但是还有一个简单得多的答案:在实践中,软件工程师通常甚至不尝试检查其程序的正确性,他们只是不这样做,甚至不写下定义程序何时正确的条件。

有多种原因。一个是大多数软件工程师不具备数学上清楚地表述问题的技能,也不知道如何编写正确性证明。

另一个问题是,为复杂的软件系统(特别是分布式系统)定义正确性条件是一项非常困难且耗时的任务。他们可能需要几周的时间才能工作。

另一个原因是程序的正确性取决于其他人编写的许多其他系统,这些系统又没有清晰的语义。从本质上说,有一条Hyrum定律说,如果您的图书馆/服务具有可观察到的行为(而不是其合同的一部分),则最终将依赖它。从本质上讲,这意味着开发具有清晰合同(例如数学中的引理)的模块化形式的软件的想法在实践中不起作用。在使用反射的语言中,情况变得更糟。即使今天的程序是正确的,当某人对其依赖项之一进行一些琐碎的重构时,它可能也会在明天崩溃。

实际上,通常会发生测试。测试按照程序的预期进行。每当发现新的错误时,他们都会添加测试以捕获该错误。它在某种程度上可以发挥作用,但不是正确性证明。

当人们没有技能来定义正确性,编写正确的程序,或者不期望这样做的时候,这样做相当困难,因此软件不正确也就不足为奇了。

但是还要注意,最后,在更好的地方,软件工程是通过代码审查来完成的。那就是程序的作者必须说服至少另一个人该程序可以正常工作。这就是提出一些非正式高级观点的重点。但是,通常情况下,通常不会发生任何对正确性的明确严格定义或正确性证明的事情。

在数学中,人们专注于正确性。在软件开发中,程序员需要关心很多事情,并且它们之间需要权衡取舍。拥有无错误的软件,甚至正确定义正确性(要求随时间变化)是理想的选择,但是必须与其他因素进行权衡,其中最重要的一项就是花费时间来开发现有的开发人员。因此,实际上,在更好的地方,目标和流程正在尽可能地降低错误的风险,而不是使软件没有错误。


实际上,我不确定在程序员和数学家之间正式(即以机器检查的方式)制定正确性规范并证明适用于10KLOC或更大程序的代码正确的人是谁。一方面,您是完全正确的,大多数程序员都没有完善的定理证明技能。另一方面,大型形式证明就像大型程序一样,本质上需要软件工程技能来管理。我完全相信,对于这种程序,任何非正式的正确性证明都将是正确的。
德里克·埃尔金斯

也许。在任何情况下,为了澄清,我在回答中都不会考虑正式证明,而只是在算法论文中看到的非正式证明。
卡夫

11

已经有很多好的答案,但是还有更多原因导致数学和编程不一样。

1 数学证明往往比计算机程序简单得多。考虑一个假设证明的第一步:

设a为整数

设b为整数

令c = a + b

到目前为止,证明还不错。让我们将其变成类似程序的第一步:

让a = input();

令b = input();

令c = a + b;

我们已经有无数的问题。假设用户确实输入了整数,我们必须检查边界。是一个大于-32768(或任何分钟您的系统上int是)?是一个小于32767?现在我们必须检查b的相同内容。并且由于我们添加了ab,因此除非a + b,否则该程序是不正确大于-32768且小于32767。这是程序员必须担心数学家可以忽略的5个独立条件。程序员不仅要担心它们,还必须弄清楚在不满足这些条件之一时该怎么办,并编写代码以决定是否要处理这些条件。数学很简单。编程很难。

2发问者没有说他是在指编译时错误还是在运行时错误,但程序员通常只是不在乎编译时错误。编译器会找到它们,而且很容易修复。他们就像错别字。人们多长时间第一次键入几段而没有错误?

3 培训。从很小的时候起,我们就被教导做数学,并且我们一遍又一遍地面临着小错误的后果。受过训练的数学家通常必须在中学阶段就开始解决多步代数问题,并且一年必须每周处理数十个(或更多)此类问题。单个下降的负号导致整个问题出问题。代数之后,问题变得越来越长,也越来越困难。另一方面,程序员通常受的正规培训少得多。许多人是自学成才的(至少在最初是这样),直到大学才接受正式培训。即使在大学级别,程序员也必须上很多数学课,而数学家可能要上一到两个编程课。


10

我喜欢尤瓦尔(Yuval)的回答,但我想稍微讲一下。您可能会发现更容易编写数学证明的一个原因可能归结为柏拉图式数学本体的方式。要了解我的意思,请考虑以下几点:

  • Math中的函数是纯函数(调用函数的整个结果完全封装在返回值中,该值是确定性的,并完全根据输入值计算得出)。
  • 数学没有突变或重新分配(当您需要对事物,函数和序列进行建模时)。
  • 数学是参照透明的(例如,没有指针,没有按名称调用与按值调用的概念),并且数学对象具有扩展相等语义(如果“两种”事物在每种可观察的方式上都是相同的,那么它们实际上是一样的东西)。

尽管上述限制是否使编写程序变得容易是有争议的,但我认为已经广泛同意上述限制确实使对程序的推理变得容易。编写数学证明时,您要做的主要事情是有关当前正在编写证明的原因(因为与编程不同,由于抽象是免费的,因此您不必在数学上重复工作),因此坚持这样做通常是值得的以上限制。


7

基本的数学证明并不等于现实世界的应用,其目的是为了满足人类的生活需求。

人们将在计算机程序领域中每天可能改变的愿望,需求和要求。

编写完美的数学证明和编写完美的计算机代码有什么不同,从而使前者比后者更容易处理?

有了数学问题的明确要求,就可以编写出无故障的程序。证明Dijkstra的算法可以找到图形上两点之间的最短路径与实现接受任意输入并找到其中任意两点之间的最短点的程序不同。

有内存,性能和硬件方面的问题需要管理。我希望我们在编写算法时不能考虑那些可以使用纯功能结构来管理的算法,但是计算机程序生活在“真实”的硬件世界中,而数学证明则存在于...“理论”中。


或者,更简洁地说

在此处输入图片说明


4

从另一个角度来看,在非学术环境中,它通常归结为金钱。

正如其他文章所断言的那样,Math是一个单一的抽象规范,因此,证明需要在要证明的那个规范内始终如一地工作。计算机程序可以在数学抽象规范的许多实现上进行操作-也就是说,一种语言或硬件制造商实现浮点数学的方式可能与另一种语言略有不同,这可能会导致结果略有波动。

这样,在编写计算机程序之前“证明”该计算机程序将涉及针对该程序的每种可能的硬件组合,在硬件级别,操作系统级别,驱动程序级别,编程语言,编译器,也许是解释器等方面证明逻辑。可能会在其上运行,并且可能会提取任何可能的数据。您可能会在太空任务,武器系统或核动力控制系统上找到这种准备和了解的水平,其中失败意味着数百亿美元的损失和潜在的许多生命损失,但除此之外就不多了。

对于您的“日常”程序员和/或企业,接受大多数正确的代码中的一定程度的准确性并出售可用的产品,其成本效益远不止于此,并且开发人员可以追溯地修复错误,因为它们在其用法。


3
您似乎对数学是什么只有一个狭view的看法,而对于“证明”计算机程序需要什么却过于宽泛。您不需要证明整个系统正确就可以证明程序正确,而只需假设其他组件符合其规格就可以证明它是正确的。如果没有,那不是程序的错。另一方面,如果您的程序由于不依赖于这些组件规范的细节而中断,例如IEEE754实现的变体,那么这就是您的错。
德里克·埃尔金斯

公平的评论。我可能会误用某些术语,因为它不是我的学术背景。尽管我确实认为,由于我之前的评论,假设其他组件没有缺陷是不明智的。
navigator_

4

我认为您的推理是有效的,但您的输入无效。如果数学证明都由人编写,那么它们根本不会比程序具有更多的容错能力。Dijkstra已经在这里被引用,但是我将提供额外的报价。

但是,我们必须以这样一种方式来组织计算,即我们有限的能力足以保证计算将建立所需的效果。这种组织包括程序的组成,在那里,我们面临着下一个规模问题,即。程序文本的长度,我们还应该给这个问题以明确的认识。我们应该保持一个事实,即我们可以阅读或编写文本的程度在很大程度上取决于其大小。[...]

我想以同样的心情引起读者的注意,即“清晰度”具有明显的定量方面,很多数学家似乎并没有意识到这一事实。当满足十页的条件时,一个说明结论有效性的定理几乎不是一个方便的工具,因为只要呼吁该定理就必须验证所有条件。在欧几里得几何中,毕达哥拉斯定理对A,B和C的任何三个点都成立,因此通过A和C可以画出一条与通过B和C的直线正交的直线。还是所有A,B和C点都重合?但这似乎在很大程度上归功于毕达哥拉斯定理的使用方便性。

总结:作为一个机智的人,我的头很小,我最好学会忍受它,尊重我的局限性,并给予他们充分的信誉,而不是试图忽略它们,因为后者将徒劳无功。被失败惩罚。

这是Dijkstra的《结构化程序设计》第一章中最后三段的略微编辑。

换个说法,以便更好地适用于您的问题:正确性很大程度上取决于证明的大小。很难确定长的数学证明的正确性(大量已发布的“证明”处于不确定性的边缘,因为没有人实际对其进行验证)。但是,如果将琐碎程序的正确性与琐碎证明相比较,则可能没有明显的区别。但是,自动证明助手(从广义上讲,您的Java编译器也是证明助手),可以通过使大量基础工作自动化来使程序取胜。


“长数学证明”是什么意思?图次要定理的证明是很长的,但是没有人为之争辩。Feit-Thompson定理有很长的证明,但是从来没有真正陷入过困境。您如何比较证明和程序的长度?字数?当您比较复杂程度(长度)相似的证明和程序时,证明和程序之间确实没有明显的区别吗?
Yuval Filmus

@YuvalFilmus就像引用中一样:十页断言对人类来说很长。如何判断程序的长度?好吧,Dikstra提供了一个度量标准:文本的长度。我认为这可能太简单了,但是仍然是一个很好的启发式方法。还有其他更有趣的指标,例如圈复杂度
wvxvw

3

正如其他答案在其答案中提到的一样(我想详细说明),但是问题的很大一部分是图书馆的使用情况。即使拥有完善的文档(与无错误的代码一样常见),也无法将库的全部知识传递给使用该库的每个程序员。如果程序员不能完全理解他们的库,则在使用它时可能会犯错误。有时,这些可能会导致严重的错误,这些错误在代码无法正常工作时被发现。但是对于较小的错误,这些错误可能不会引起注意。

如果数学家在没有充分理解它们的情况下使用现有的证明和引理,情况也会类似。他们自己的证据可能有缺陷。虽然这可能表明一种解决方案是完全学习一个人使用的每个库;这实际上非常耗时,并且可能需要程序员没有的领域知识。(我对DNA测序/蛋白质合成了解很少;但是可以使用库来处理这些概念)。

简而言之,软件工程(实际上是总体上的工程)基于封装不同层次的抽象,使人们可以专注于他们所擅长的问题的较小领域。每层之间。如果沟通不完美,就会引起问题。


3
等等,是什么让您认为数学家“完全理解”他们使用的证明和引理?我不确定您要在此处演示的数学家和程序员之间的区别。
德里克·埃尔金斯

3

在所有这些很棒的答案之后,我都会尽量保持原创。

程序就是证明

咖喱霍华德同构告诉我们,在你的程序的类型是定理和实际的代码是他们的证明。

诚然,这是一个非常抽象的高层视图。您可能会想到的问题是,编写典型的代码比较困难,因为它变得太底层了。在大多数情况下,您“需要告诉机器该怎么做”。或者,换个角度来看:数学家真的很擅长抽象。

附带说明:“流音乐”是两者之间最美丽的桥梁之一。它基本上设置的东西要能够说:“我想那个方式”和神奇的机器做这个根据需要准确。


我没有完全看到这是否解决了这个问题。OP没有任何迹象表明他们在谈论使用功能强大的类型系统的编程语言,我认为它们的意思是更通用的类型系统。因此,在这种情况下,Curry-Howard显得微不足道。
6005

我知道它对于C或类似的东西有些牵强。但我的观点是:数学比典型的CS初学者想像的要近!
奥列格·洛巴乔夫

1
您似乎是我在回答中提到的Curry-Howards同构的“粗心观察者”。即使我们在程序和证明之间存在同构,也不能因此认为编写程序和编写证明的行为完全相似。实际上,甚至所有“有趣”或“典型”程序都可能并不对应于典型的证明,反之亦然。
离散蜥蜴

@Discretelizard显然不是“有趣的”程序与“典型的证明”不对应的情况。这是一个示例,其中我采用某人的“典型证明”并生成(略过)一个不可否认的有趣程序(与高斯消去密切相关的程序)。配备适当精确的类型,我认为大多数“有趣”的程序将是有用的引理或定理,但是许多(构造性)证明没有真正的计算意义-它们只是在验证副条件-尽管很多确实如此。
德里克·埃尔金斯

3

许多其他答案均未指出以下内容。数学证明在具有无限内存和无限计算能力的虚拟计算系统上运行。因此,它们可以将任意大的数保持为无限的精度,并且在任何计算中都不会失去精度。

π


2
“数学证明在具有无限内存和无限计算能力的虚拟计算系统上运行。” 大多数数学证明都在形式代数系统上“操作”,例如实数(我们具有“无限精度”)。这也可以在程序中完成:有所谓的计算机代数系统(CAS)可以做到这一点!此外,整个数学领域都关注着这样一个事实,即我们不能完全将所有实数表示为有限的浮点数。我认为您是在数学与程序设计之间进行区分,而数学与编程之间是没有区别的。
离散蜥蜴

1
@Discretelizard,是的,特殊软件包以任意精度存在,但是即使那样,可用内存也会限制实际可达到的精度。而且它们是特殊包装。此类程序包只进行了很小一部分的编程,并且大部分是在学术环境中进行的。
crobar

π

@Discretelizard,我认为我的观点仍然成立,大多数程序员都没有使用这种CAS系统。对于实际编程,它们太慢了。大多数编程从根本上涉及对有限精度数字的运算。顶级语言是C,C ++,Python,Java等。默认情况下,都不使用CAS样式表示法(尽管可以在其中创建用于执行此操作的程序包)。您的反例是计算机语言/系统的一小部分。
crobar

2
@crobar您的答案的问题是,检测到的大多数错误都不是由于浮点错误或整数溢出引起的(尽管它们确实贡献了不错的数量,并且这些方面肯定使得程序的完全正确性要小得多)。但是,您可以提出一个更笼统的说法,即数学家缺乏程序员的许多担忧,例如性能,上市时间,可维护性以及在证明要求极高的情况下更改需求的能力有限。
德里克·埃尔金斯

3

不是。从本质上讲,数学证明就像是越野车一样,只是他们的读者比编译器更宽容。类似地,至少在尝试运行计算机程序之前,计算机程序的读者很容易被愚弄,以为它是正确的。

例如,如果我们尝试将数学证明转换为像ZFC这样的正式语言,它还将包含错误。那是因为这些证明可能会花很长时间,所以我们不得不编写一个程序来生成证明。尽管在形式化基础证明方面进行了积极的研究,但很少有人冒着麻烦去冒险。

确实,数学可以获得BSOD! 这不是第一次!

在此处输入图片说明

这种正统的想法是,所有经过充分验证的数学证明基本上都是正确的或可以变得正确的,这与激励您的软件项目在工作中是同一个想法:只要我们坚持路线图,我们就会发现所有错误,并且功能完备—这是一个反复进行的过程,可以确定最终的产品。

这是反面。您看,我们已经获得了资金,已经验证了业务概念,所有文档都在这里供您阅读。我们只需要您执行,这是肯定的事情!

我们也不要为希尔伯特感到难过,他知道自己正在经历什么。这只是生意。

如果您想确定,请根据具体情况进行总结并得出自己的结论!


3

我看到两个重要的原因,为什么程序比数学证明更容易出错:

1:程序包含随时间变化的变量或动态对象,而证明中的数学对象通常是静态的。因此,数学中的符号可以用作推理的直接支持(如果a = b,情况仍然如此),这在程序中不起作用。同样,在程序并行或具有多个线程的情况下,此问题变得更加严重。

2:数学通常假设相对整齐的定义的对象(图形,流形,环,组等),而编程则处理非常杂乱且不规则的对象:有限精度算术,有限堆栈,字符整数转换,指针,需要收集的垃圾等等。因此很难记住与正确性相关的条件。


3

您应该区分两个不同的“类别”:

  • 伪证明(或伪代码)-这就是您在书中看到的。它以自然语言(例如英语)书写。那就是您用来学习数学(或算法)的方法。
  • 正式证明(或正式代码)-当您需要证明(或代码)可机械验证(或可执行)时,可以编写它。这种表示不需要任何“人类智慧”。通过遵循一些预定义的步骤(通常由当今的计算机完成),可以对其进行机械验证(或执行)。

我们已经使用伪代码数千年了(例如Euclids算法)。在计算机发明之后,编写正式代码(使用诸如C或Java等正式语言)变得非常流行和有用。但是,可悲的是,形式证明(使用诸如Principia Mathematica或Metamath的形式语言)并不十分流行。而且由于今天每个人都在编写伪证明,所以人们经常争论新的证明。在实际的“证明”之后的数年,数十年甚至几个世纪后,就会发现其中的错误。


3

我找不到参考,但是我认为Tony Hoare曾经说过以下几句话:检查程序和检查证明之间的区别是,一次可以两行检查一个证明。

一言以蔽之:地点。

编写证明以使其易于检查。编写程序以便可以执行它们。由于这个原因,程序员通常会忽略那些对检查程序有用的信息。

考虑这个程序,其中x是只读的

    assume x >= 0
    p := 0 ;
    var pp := 0 ;
    while( x >= pp + 2*p + 1 ) 
    {
        var q := 1 ;
        var qq := q ;
        var pq := p ;
        while(  pp + 4*pq + 4*qq <= x )
        {
            q, pq, qq := 2*q, 2*pq, 4*qq ;
        }
        p, pp := p + q, pp + 2*pq + qq ;
    }
    assert  p*p <= x < (p+1)*(p+1)

它易于执行,但难以检查。

但是,如果我重新添加缺少的断言,则可以通过检查每个赋值序列相对于其前置条件和后置条件是否正确以及每个循环的循环后置条件是否隐含来在本地检查程序。不变和环路保护的取反。

    assume x >= 0
    p := 0 ;
    var pp := 0 ; 
    while( x >= pp + 2*p + 1 ) 
        invariant p*p <= x 
        invariant pp == p*p
        decreases x-p*p 
    {
        var q := 1 ;
        var qq := q ; 
        var pq := p ; 
        while(  pp + 4*pq + 4*qq <= x )
            invariant (p+q)*(p+q) <= x
            invariant q > 0 
            invariant qq == q*q 
            invariant pq == p*q 
            decreases x-(p+q)*(p+q)
        {
            q, pq, qq := 2*q, 2*pq, 4*qq ;
        }
        assert (p+q)*(p+q) <= x and pp==p*p and pq==p*q and qq==q*q and q>0
        p, pp := p + q, pp + 2*pq + qq ;
    }
    assert  p*p <= x < (p+1)*(p+1)

回到最初的问题:为什么写下数学证明比编写计算机代码更能抵御错误?由于证据的设计易于读者阅读,因此作者也易于检查,因此机敏的作者往往不会在证据中犯(或至少保留)逻辑错误。当我们编程时,我们常常无法写下代码正确的原因;结果是程序的阅读者和作者都很难检查代码;结果是作者犯了(然后保持)错误。

但是有希望。如果在编写程序时还写下我们认为程序正确的原因,则可以在编写代码时检查代码,从而减少错误代码。这也有一个好处,就是其他人可以阅读我们的代码并自己检查。


2

我们可能会问,在实践上或从原则上讲,编写证明或编写代码是否更困难。

实际上,证明比编码困难得多。参加过两年大学数学课程的人很少能编写证明,甚至是琐碎的证明。在已经学习了两年大学级CS的人中,至少有30%的人可以解决FizzBu​​zz

但是从原则上讲,存在其他根本原因的根本原因。至少在原则上,可以通过不需要任何判断或理解的过程来检查证明的正确性。程序不能-我们甚至无法通过任何规定的过程来判断程序是否会停止。


3
两年的大学水平数学并不意味着两年的时间专注于编写证明(或花费任何时间编写证明)。就是说,我的印象是,初中/初中几何课程包含证明是很常见的,因此,显然,我们可以预期,即使13岁的孩子也能够在少于一个学年的教育水平上编写简单的证明。话题。分步代数计算本质上也是证明。我认为您将“琐碎”的标准设置为过低,而证明过高。
德里克·埃尔金斯

3
我们可以用相同的方式编写程序。您可以想象一个要求,即您编写的每个函数/过程都必须提供正式规范和证明(符合Coq标准)符合规范。然后,有一些方法可以以不需要任何判断或任何理解的方式来检查该证明的正确性。
DW

@DW:您假设(1)在所有情况下都可以完全指定所需的行为,(2)存在必要的证明(即,问题并非不可确定),以及(3)如果存在证明,则我们可以找到它。我认为这三个假设在至少某些情况下(可能几乎所有情况)都是错误的。关于第3条,请注意,尽管有些证明可能很容易,但是很难找到许多证明。
Ben Crowell

@DerekElkins:我声称很少有大学生可以写琐碎的证明,这是基于我对学生的经验。这是一所社区大学,所以YMMV。某些高中几何课程包含大量的证明,这一事实并不能转化为所有大学生都能编写证明的事实。他们还应该知道如何进行基本的代数运算,但是在我的学校中,大约有一半的新生calc学生不能这样做-这有助于解释为什么这么多学生不及格。
Ben Crowell

这将是一个很好的解释,可以添加到答案中,解释为什么您不能采用相同的方法来检查程序的正确性。一般(2)和(3)很少的问题,无论是在实践中或在原则上(如果你不能证明程序是正确的,以不同的方式写,直到你证明它是正确的)。但是,您的(1)很重要的一点,我认为它将加强答案,以解释为什么这很难使程序和证明做相同的事情。
DW

2

只有一小部分正确的数学陈述可以得到实际证明。更重要的是,不可能构造一个非平凡的(*)数学公理集来证明所有真实的陈述。如果只需要编写程序来完成计算机可以完成的工作的一小部分,就可以编写可证明正确的软件,但是经常需要计算机执行超出可证明正确范围的工作。软件可以完成。

(*)可以定义一组公理,该公理允许枚举所有真实的语句,从而进行证明,但是通常这些并不是很有趣。虽然可以将公理集正式地归类为相对而言,相对而言很简单的公理集,但关键点在于,可证明存在的正确但不能被证明的陈述不是集合中的缺陷公理。添加公理以使任何现有的真实但不可证明的陈述可被证明将导致其他陈述变为真实但没有可证明的陈述。


1
“只有一小部分正确的数学陈述可以得到实际证明。” -您如何测量“部分”?这是在某种概率分布下吗?您是否有证据支持这一说法?
DW

“通常要求计算机执行超出可证明正确的软件可以完成的范围的工作。” -您有任何证据吗?你有例子吗?您是在声称“超出原则上可以证明正确的范围”还是“超出我们可以合理想象的实践证明范围”?
DW

@DW:如果X和Y是正确但不可证明的正交语句,则对于每个可证明语句P,将至少有两个正交语句(P和X)以及(P和Y)是正确但不可证明可证明的。在处理无限集时,这种逻辑不一定要证明任何事情,因为一个人可以使用类似的逻辑来表明偶数是奇数的两倍,因为对于每个奇数,可以识别两个偶数(4x),并且(4x + 2)与其他奇数整数无关,但是偶数和奇数整数当然具有相同的基数。
超级猫

@DW:因此,短语“一小部分”可能仅在描述可以被实际证明的真实陈述的一部分时才真正有意义,但我认为了解无力证明所有真实陈述不是“缺陷”是有用的。对于计算机,许多领域通常使用算法,该算法具有极小的但不为零的故障概率,然后对其进行调整,以使该概率较低(例如,低于被流星撞击的设备的概率)。在许多情况下,各种故障模式并不是独立的,因此,这实际上是不可能的……
supercat

...以确定不同故障组合的概率。如果有人估计在任意一分钟内发生故障的概率为十分之一-500,那么一个人可能偏离数百个数量级,并且仍然具有可靠的系统,但是如果一个人偏离494个数量级该系统大约每两年就会发生一次故障。
超级猫

2
  1. 计算机程序在现实世界中经过测试。在很长的数学证明中,只有少数几个人可以理解的棘手的技术错误很有可能会被发现。软件产品中的同类错误很可能会产生普通用户注意到的奇怪行为。因此前提可能不正确。

  2. 计算机程序执行有用的现实功能。他们不必一定要100%正确地做到这一点,而且高标准的正确性非常昂贵。证明仅在它们确实证明了某些东西时才有用,因此对于数学家来说,跳过“ 100%正确”部分不是一个选择。

  3. 数学证明明确定义。如果证明有缺陷,则说明作者犯了一个错误。由于未正确传达要求,或者存在程序员从未听说过的兼容性问题,计算机程序中会出现许多错误。

  4. 许多计算机程序无法证明是正确的。他们可能会解决非正式定义的问题,例如识别人脸。或者它们可能像股票市场预测软件一样,有一个正式定义的目标,但涉及太多现实世界变量。


2

作为人类活动,数学的很大一部分是领域特定语言的开发,在这种语言中,人类易于进行证明的验证。

证明的质量与长度和复杂程度成反比。通常通过开发良好的符号来描述我们正在陈述的情况以及辅助概念在考虑中的特定证据中相互作用,来减少长度和复杂度。

这不是一个容易的过程,大多数被从研究的最前沿移走的人们所见证的证据恰好是在数学领域(如代数和分析),这些领域已经有数百年甚至数千年的历史了。经过精炼,使实际写下证明的动作变得轻而易举。

但是,在研究的最前沿,特别是如果您从事的领域不是具有公认的概念或发展良好的概念的问题时,我会感到难以编写甚至编写正确的证明也接近编写正确的程序的困难。这是因为您还必须同时编写一种编程语言设计的模拟物,训练您的神经网络正确地对其进行编译,尝试编写证明,用尽内存,尝试优化语言,反复使您的大脑学习该语言,再次编写证明,等等。

重申一下,我认为在数学的某些领域中编写正确的证明可以解决编写正确的程序的困难,但是这些领域肯定是年轻且未开发的,因为数学进展的观念与证明容易性紧密地联系在一起验证。

另一种表达我想指出的观点的方式是,编程语言和数学都在一天的最后阶段进行了设计,以便可以分别编译计算机程序和证明。只是在计算机中完成计算机程序的编译即可确保语法正确性,而语法正确性通常与程序本身的正确性没有多大关系,而“编译”证明则是由人来完成的,并确保语法正确性与证明的正确性。


1

您正在诚实地在这里比较苹果和橙子。 防故障无错误不是一回事。

如果程序比较这些数字232 is greater than 3,则可能是由于错误的实现:

# Buggy implementation
function is_a_greater_than_b(a,b):
  return b > a

该程序仍然没有错误。比较两个数字a和时b,它将始终能够告诉您是否b大于a。这不是您(程序员)应要求计算机执行的操作。


2
那么,您在程序中对“故障”的定义是什么?
user56834 '17

0

a)因为计算机程序比数学证明还大

a.1)我相信编写复杂的计算机程序时使用的人比编写数学证明时要用的人更多。这意味着错误率更高。

b)因为首席执行官/股东比修复小错误更关心金钱,同时您(作为开发人员)必须完成任务才能满足某些要求/截止日期/演示

c)因为您可能是在comp sci中没有“深入”知识的程序员,所以很难在数学上做(我相信)

另外:

美国宇航局:

该软件没有错误。它是完美的,就像人类所达到的那样完美。考虑一下这些统计信息:程序的最后三个版本(每行420,000行长)每个都只有一个错误。该软件的最后11个版本共有17个错误。

进行软件升级以使航天飞机可以使用“全球定位卫星”进行导航,这一更改仅涉及程序的1.5%或6366行代码。一项更改的规范需要运行2500页,比电话簿还厚。当前程序的规范填充30卷,并运行40,000页。

https://www.fastcompany.com/28121/they-write-right-stuff


“计算机程序比数学证明还大”,这取决于程序和证明。而且其中很多似乎都是投机性的。
David Richerby

@DavidRicherby好吧,我想到了诸如Last fermat定理和NASA的Apollo之类的 东西
Exeus

0

基本级别:

让我们从最简单,最基本的角度看待事物。

对于数学,我们有:
2 + 3 = 5

我很小的时候就知道了这一点。我可以看一下最基本的元素:两个对象和三个对象。大。

对于计算机编程,大多数人倾向于使用高级语言。某些高级语言甚至可以“编译”为较低的高级语言之一,例如C。然后可以将C转换为汇编语言。然后将汇编语言转换为机器代码。许多人认为复杂性到此为止,但事实并非如此:现代CPU将机器代码作为指令,然后运行“微代码”以实际执行那些指令。

这意味着,从最基本的层次(处理最简单的结构)来看,我们现在正在处理嵌入在硬件中的微代码,大多数程序员甚至不直接使用也不更新。实际上,不仅大多数程序员不触摸微代码(比微代码高0级),大多数程序员也不触摸机器代码(比微代码高1级),甚至汇编(比微代码高2级)(除了,也许是在大学期间接受了一些正规培训)。大多数程序员只会将时间花在3或更多级别上。

此外,如果我们看一下组装(这是人们通常所掌握的水平),那么每个步骤通常都是经过培训并有资源来解释该步骤的人员所理解的。从这个意义上讲,汇编语言比高级语言要简单得多。但是,组装是如此简单,以至于执行复杂的任务甚至是平庸的任务都非常繁琐。上层语言使我们摆脱了这种困境。

在关于“逆向工程”的法律中,法官宣布,即使理论上可以一次处理一个字节,现代程序也需要数百万个字节,因此必须为此专门制作某种记录(例如代码的副本)努力可行。(因此,内部开发不被认为违反了版权法的一般性“禁止复制”规则。)(我可能正在考虑制造未经授权的Sega Genesis墨盒,但可能会在Game Genie案中想到某些说法。 )

现代化:

您是否运行用于286s的代码?还是您运行64位代码?

数学使用的基础可以追溯到几千年前。有了计算机,人们通常会认为投资已有二十年之久的资源是无用的浪费。这意味着可以对数学进行更彻底的测试。

二手工具标准:

我(由一个接受过比我更正式的计算机编程培训的朋友所学到的)告诉我,没有满足C规范的无错误C编译器之类的东西。这是因为C语言基本上假定可以将无限内存用于堆栈。显然,当人们试图使可用的编译器在本质上更加有限的实际机器上工作时,就必须偏离这种不可能的要求。

在实践中,我发现使用Windows Script Host中的JScript,我已经能够完成许多使用对象的工作。(我喜欢这种环境,因为尝试新代码所需的工具集已内置在Microsoft Windows的现代版本中。)使用这种环境时,我发现有时没有关于对象如何工作的易于查找的文档。但是,使用对象是如此有益,以至于我还是这样做。因此,我要做的是编写代码,这可能是一个大黄蜂的巢,在一个很好的沙盒环境中可以看到效果,并在与之交互时了解对象的行为。

在其他情况下,有时只有在弄清对象的行为方式之后,我才发现该对象(与操作系统捆绑在一起)存在错误,并且微软已故意解决这一已知问题,将不会予以解决。 。

在这种情况下,我是否依赖于由熟练的程序员创建的OpenBSD,这些程序员定期(每年两次)按计划创建新版本,并且在过去10多年中享有“仅两个远程漏洞”的著名安全记录?(即使它们具有不太严重的问题的勘误补丁。)否,绝对不是。我不依赖于如此高质量的产品,因为我正在为一家支持为人们提供使用Microsoft Windows的机器的企业的公司工作,这就是我的代码需要进行的工作。

实用性/可用性要求我在人们认为有用的平台上工作,而这是一个众所周知的对安全性不利的平台(即使自千年之初以来取得了巨大的进步,而同一公司的产品却差得多) 。

摘要

计算机编程更容易出错的原因有很多,这被计算机用户社区所接受。实际上,大多数代码是在不能容忍无错误工作的环境中编写的。(在某些方面,例如开发安全协议,可能会在这方面付出更多的努力。)除了企业通常不希望投资更多原因以及错过人为的截止日期以使客户满意的原因外,还有以下影响:技术的进步只是表明,如果您花费太多时间,那么您将在一个过时的平台上工作,因为十年之内事情确实会发生重大变化。

简而言之,当我看到一些strlen和strcpy的源代码时,我对那些非常有用和流行的功能短短感到惊讶。例如,strlen可能类似于“ int strlen(char * x){char y = x; while((y ++)); return(yx)-1;}”

但是,典型的计算机程序要比这更长。同样,许多现代编程将使用其他代码,这些代码可能没有经过充分测试,甚至被认为是错误的。如今的系统比容易想象的要复杂得多,除了通过手工挥舞掉很多细节作为“较低层次处理的细节”之外。

这种强制性的复杂性,以及与复杂甚至错误的系统一起工作的确定性,使得计算机编程要比许多数学简化许多硬件来验证,因为数学往往会简化为许多简单的层次。

在数学上分解事物时,您会得到孩子可以理解的各个部分。大多数人相信数学;至少是基本算术(或至少是计数)。

当您真正分解计算机程序以了解幕后发生的事情时,您最终会破坏标准和代码的最终实现以电子方式执行,而物理实现仅比大多数大学训练有素的计算机科学家所采用的微代码低了一步。不敢碰(如果他们甚至意识到的话)。

我曾与一些大学或刚毕业的程序员交谈过,他们完全反对可以编写无错误代码的想法。他们已经取消了这种可能性,尽管他们承认一些令人印象深刻的示例(我已经证明了)是令人信服的论点,但他们认为此类示例是不具代表性的罕见骗子,并且仍然忽略了能够计数的可能性具有更高的标准。(与我们在数学中看到的更可信的基础相比,态度截然不同。)


1
尽管您可以很好地说明编程的复杂性,但您几乎根本不会考虑数学!实际上,您似乎低估了形式数学的复杂性:“当您分解数学中的内容时,您会得到孩子可以理解的单个部分”,真的吗?此外,对于足够的“高级”编程也可以这么说(例如,Scratch是为儿童设计的)。还要注意,尽管无法实现完整的C-spec,但是使用计算机辅助的证明,支持重要子集的编译器在形式上是正确的。
离散蜥蜴

2+3=5

注意:如果您在一件事上是专家,而另一件事是专家初学者(或更低的专家),那么您将处于最糟糕的位置来比较两者。
拉斐尔

离散蜥蜴-这是计算机科学SE。此外,在我发布之前实际上已经阅读了其他答案,我觉得他们比计算机更着迷于数学。我觉得我的回答是更好的,不要再增加它的时间,而只是增加那些与其他地方写的东西在很大程度上是多余的单词。///至于Scratch,高层次更复杂,而不是简单(从完全理解所有运动部件的角度来看)。从我写这个观点来看,在其他层之上的组装比从头开始更简单(但电子NAND门更简单)
TOOGAM

0

数学证明描述“什么”知识,程序描述“如何”知识。

编写程序更加复杂,因为程序员必须对可能出现的所有不同状态以及程序行为如何发生变化做出推理。证明使用公式或分类推理来证明有关其他定义的内容。

大多数错误是由进程进入程序员无法预期的状态引起的。在程序中,通常有数千个变量,或者在大型系统中,有数百万个可能的变量,这些变量不是静态数据,而是实际上改变了程序的执行方式。所有这些相互作用共同产生了无法预料的行为,尤其是在现代计算机中,您的下面隐藏着抽象层。

在证明中,没有变化的状态。讨论的定义和对象是固定的。证明确实需要一般地考虑问题并考虑很多情况,但是这些情况由定义固定。


2
我想说的是,数学证明完全有能力描述“什么”知识:例如,使用任何构造示例来证明存在的证明或一种计算值的方法。不过,我同意状态是证明中所缺少的,因为除了作者(或读者!)明确描述的状态外,没有其他状态。正是这种状态允许程序执行读者/作者不知道的事情,而这在证明中是不可能的。(当然,证明可能具有意想不到的特征或结果,但要获得它们,仍需要一些积极的思考)
离散蜥蜴

@Discretelizard这是一个有用的评论。我认为“做什么”和“如何做”之间的界限肯定是模糊的。证明算法确实可以实现您认为的功能,实际上并不是在我脑海中描述“如何做”,只是保证了某些属性成立。从哲学的角度来看,我认为“如何”知识需要与世界相对应。程序总是按照您的要求进行操作。当您遇到一个错误时,您告诉它做的事情与世界不符(您正在建模的东西)。独立于应用程序(如物理问题)的数学似乎都取决于相干性。
贾斯汀·迈纳斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.