HW11中对ageVar采用缓存优化的等价性证明(包括溢出情况)

概要

我们知道,第三次作业里age上限变为2000,而如果缓存年龄的平方和,2000*2000*800 > 2147483647,会溢出。但是实际上,我们仍然能通过缓存得到正确的结果。这是因为,计算机内进行的二进制运算其实每一步都进行了 \(\&0xffff\_ffff\) 操作,有交换律、结合律、平方公式成立。即使在溢出的情况下,两个式子仍然是等价的。本文试着利用二进制运算和无符号数运算的关系,以及无符号数运算的性质,来证明这一点。

引论

无符号数的运算和二进制的运算

我们知道,在机器语言里,不管一个字是有符号数还是无符号数,采用的四则运算指令都是相同的(MIPS中add和addu的区别也只是是否会触发算术溢出异常,而具体的运算方式仍是一样的)。

我们现在Java程序里,做的其实是上述计算机里的二进制运算,和数学世界的运算是有所不同的。我们将计算机二进制的“四则运算”分别记为 \(\dot{+} \quad \dot{-} \quad \dot{*} \quad \dot{/}\) 。

这四种运算肯定不是随便定义的,和现实中的数学运算肯定有着一定的关系。这里我们尝试从无符号数的角度,建立计算机二进制运算和无符号数运算之间的关系。(我们在这里不讨论除法,因为这和我们的主题无关 因为除法很麻烦作者根本不会(x))

为此,我们引入一些记号。同一个32位二进制数据,既可以表示一个无符号整数,又可以表示为一个有符号整数。我们将某个数据记为 \(d\) ,记 \(U(d)\) 为按无符号数解释 \(d\) 得到的数字,\(T(d)\) 为按二的补码有符号数解释 \(d\) 得到的数字。我们将相应的逆过程,将一个数字按照有/无符号数解读并转化为二进制数据的过程,记作 \(UD(x)\) / \(TD(x)\)。

根据我们已有的知识,在未发生溢出的情况下,计算机二进制运算的结果 和 把二进制数据解读为无符号数进行运算的结果 是相同的。现在我们主要需要考虑的内容是:数学中的无符号数是无位数限制的。结合这一点,我们可以得到:

\(U(a \dot{+} b) = (U(a) + U(b)) \% 0x1\_0000\_0000\)

\(U(a \dot{-} b) = (U(a) - U(b)) \% 0x1\_0000\_0000\)

\(U(a \dot{*} b) = (U(a) * U(b)) \% 0x1\_0000\_0000\)

其中a,b为两个二进制数据。

进一步

\(a \dot{+} b = UD((U(a) + U(b)) \% 0x1\_0000\_0000)\)

\(a \dot{-} b = UD((U(a) - U(b)) \% 0x1\_0000\_0000)\)

\(a \dot{*} b = UD((U(a) * U(b)) \% 0x1\_0000\_0000)\)

其中a,b为两个二进制数据。

取模运算

此处参考了取模运算的性质 By varinic

(取模和取余似乎还略有不同,我不太懂这个,所以用了本文几乎全程使用无符号数来避开这个问题。

基本性质
若p|(a-b),则a≡b (% p)。例如 11 ≡ 4 (% 7), 18 ≡ 4(% 7)
(a % p)=(b % p)意味a≡b (% p)
对称性:a≡b (% p)等价于b≡a (% p)
传递性:若a≡b (% p)且b≡c (% p) ,则a≡c (% p)
运算规则
模运算与基本四则运算有些相似,但是除法例外。其规则如下:
(a + b) % p = (a % p + b % p) % p (1)
(a - b) % p = (a % p - b % p) % p (2)
(a * b) % p = (a % p * b % p) % p (3)
a ^ b % p = ((a % p)^b) % p (4)
结合律:
((a+b) % p + c) % p = (a + (b+c) % p) % p (5)
((a*b) % p * c)% p = (a * (b*c) % p) % p (6)
交换律:
(a + b) % p = (b+a) % p (7)
(a * b) % p = (b * a) % p (8)
分配律:
((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p (9)

证明

我们想要证明的是:我们利用缓存的算式和JML对ageVar的算式是等价的。

注意,JML中的加法也是计算机里的二进制加法。

我们以下提到的变量都指他们的二进制数据。

缓存:

\[ageVar = \frac{\dot{\sum} (x_i \dot{*}x_i) \dot{-} 2 \dot{*} \dot{\sum} x_i \dot{*} \bar{x} + n \dot{*} \bar{x} \dot{*} \bar{x} }{n}
\]

JML:

\[ageVar = \frac{\dot{\sum} ((x_i \dot{-} \bar{x}) \dot{*} (x_i \dot{-} \bar{x})) }{n}
\]

我们只需要证明分式的上半部分恒等。

为了方便,我们记\(M(U(d)) = U(d) \% 0x1\_0000\_0000\)

证明平方式对二进制运算成立

首先,针对每一个小单元

\[((x_i \dot{-} \bar{x}) \dot{*} (x_i \dot{-} \bar{x})) = UD(M(U(x_i \dot{-} \bar{x}) * U(x_i \dot{-} \bar{x})))
\]
\[=UD(M(U(UD(M(U(x_i) - U(\bar{x})))) * U(UD(M(U(x_i) - U(\bar{x}))))))
\]

(这什么鬼玩意)

我们注意到 \(U(UD(x)) = x\),x为一个无符号数,配合利用(1)(2)(3)三条运算规律可以去掉多余的\(M()\)记号,化简一下:

\[原式=UD(M((U(x_i) - U(\bar{x})) * (U(x_i) - U(\bar{x}))))
\]
\[= UD(M(U(x_i) * U(x_i) - 2 * U(x_i) * U(\bar{x}) + U(\bar{x}) * U(\bar{x})))
\]

再次利用(1)(2)(3)三条运算规律,在适当位置增加冗余的\(M()\)记号来运用我们的无符号数-二进制数互换。我们就有了:

\[((x_i \dot{-} \bar{x}) \dot{*} (x_i \dot{-} \bar{x}))=(x_i \dot{*} x_i \dot{+} 2 \dot{*}x_i \dot{*} \bar{x} + \bar{x} \dot{*} \bar{x})
\]

P.S.:写到后面我发现直接证二进制运算有分配律,加上后面的交换律结合律不就直接成了(瘫

证明交换律对二进制运算成立

\[a \dot{+} b = UD(M(U(a) + U(b))) = UD(M(U(b) + U(a))) = b \dot{+} a
\]

证明结合律对二进制运算成立

\[(a \dot{+} b) \dot{+} c = UD(M(U(a) + U(b))) \dot{+} c
\]
\[= UD(M(U(UD(M(U(a) + U(b)))) + U(c)))
\]
\[= UD(M(M(U(a) + U(b)) + U(c)))
\]
\[= UD(M(U(a) + U(b) + U(c)))
\]
\[= UD(M(U(a) + M(U(b) + U(c))))
\]
\[= UD(M(U(a) + U(b \dot{+} c)))
\]
\[= a \dot{+} (b \dot{+} c)
\]

即证得

之后就是简单的变形了,就不再赘述。

总结

有了交换律,结合律和平方公式,我们很容易就能将JML给出的计算公式转化为我们用的利用了缓存的公式。而且这个转化过程经过数学证明是恒等的。不用管是否溢出,同样的二进制age数据,在二进制运算的条件下,用JML和缓存方法得到的ageVar数据一定是相等的。就算ageVar本身溢出了,我们得到的溢出后结果也会是一样的。

本文通篇使用无符号整数,是因为无符号整数运算和二进制数据运算的转换比较好处理,学过数论的大佬们也许可以用有符号整数的运算导出来类似的结果吧(咱是不会了。

至于担心age是负数的时候能不能用的,肯定是能用的,因为无符号数在这里就是一个介质。我们知道无符号数的运算性质,知道无符号数和二进制数据之间的关系,然后通过这些来求二进制数据的运算规律。换一个别的,应该也是能做的(不过可能更麻烦点)。

顺便,有的人说用了long可能会炸,其实是因为先做了除法再转成int。直观的例子就是一个刚好溢出int一点点,但是/n之后在int范围内的结果的数,long先做除法再转int就会是正的,long先转int再除就是负的了。数学上来说,因为除法的模p运算性质不像加减乘那么好,而long相当于%0x1_0000_0000_0000_0000和int不同,就炸了。

咱数学学的不好,也没搞过数论啊这些,所以如果有错还请不要过分嘲笑(正色(x)),发现问题的话就麻烦在讨论区提点一手,我会及时更正。

2020-BUAA OO-面向对象设计与构造-HW11中对ageVar采用缓存优化的等价性证明(包括溢出情况)的更多相关文章

  1. 从结构和数字看OO——面向对象设计与构造第一章总结

    不知不觉中,我已经接触OO五周了,顺利地完成了第一章节的学习,回顾三次编程作业,惊喜于自身在设计思路和编程习惯已有了一定的改变,下面我将从度量分析.自身Bug.互测和设计模式四个方向对自己第一章的学习 ...

  2. BUAA面向对象设计与构造——第二单元总结

    BUAA面向对象设计与构造——第二单元总结 第一阶段:单部傻瓜电梯的调度 第二阶段:单部可捎带电梯的调度 (由于我第一次写的作业就是可捎带模式,第二次只是增加了负数楼层,修改了一部分参数,因此一起总结 ...

  3. BUAA面向对象设计与构造——第一单元总结

    BUAA面向对象设计与构造——第一单元总结 第一阶段:只支持一元多项式的表达式求导 1. 程序结构 由于是第一次接触面向对象的编程,加之题目要求不算复杂,我在第一次作业中并没有很好利用面向对象的特点, ...

  4. 面向对象设计与构造:oo课程总结

    面向对象设计与构造:OO课程总结 第一部分:UML单元架构设计 第一次作业 UML图 MyUmlInteraction类实现接口方法,ClassUnit和InterfaceUnit管理UML图中的类和 ...

  5. 【设计模式系列】之OO面向对象设计七大原则

    1  概述 本章叙述面向向对象设计的七大原则,七大原则分为:单一职责原则.开闭原则.里氏替换原则.依赖倒置原则.接口隔离原则.合成/聚合复用原则.迪米特法则. 2  七大OO面向对象设计 2.1 单一 ...

  6. 面向对象设计与构造:JML规格单元作业总结

    面向对象设计与构造:JML规格单元作业总结 第一部分:JML语言理论基础 JML语言是什么:对Java程序进行规格化设计的一种表示语言 使用JML语言有什么好处: 用逻辑严格的规格取代自然语言,照顾马 ...

  7. 2020-BUAA OO-面向对象设计与构造-第三单元总结

    Part-1 JML总结 Section-1 理论基础 The Java Modeling Language (JML) is a behavioral interface specification ...

  8. 「BUAA OO Pre」 Pre 2总结回顾概览

    「BUAA OO Pre」 Pre 2总结回顾概览 目录 「BUAA OO Pre」 Pre 2总结回顾概览 Part 0 前言 写作背景 定位 您可以在这里期望获得 您在这里无法期望获得 对读者前置 ...

  9. (转) 面向对象设计原则(二):开放-封闭原则(OCP)

    原文:https://blog.csdn.net/tjiyu/article/details/57079927 面向对象设计原则(二):开放-封闭原则(OCP) 开放-封闭原则(Open-closed ...

随机推荐

  1. webpack + ts 配置路径别名无死角方法总结

    webpack + ts 配置路径别名总结 自我体验加总结:在配置脚手架时,定制别名很有必要,可以使得代码更优雅,可读性更强.但在使用ts的时候,即便项目能够运行,vscode 确时长会提示 can' ...

  2. [转]百度Appollo无人车Perception Module 分析

    https://github.com/ApolloAuto/apollo/blob/master/docs/howto/modules/apollo1.5_perception_module_stud ...

  3. 24_MySQL插入insert语句

    本节涉及SQL语句: -- MYSQL 基础操作 1.插入insert语句 INSERT INTO t_dept(deptno,dname,loc) VALUES(70,"后勤部" ...

  4. C#如何防止程序多次运行的技巧(精典)

    一.引言最近发现很多人在论坛中问到如何防止程序被多次运行的问题的,所以这里就记录下来,希望给遇到同样问题的朋友有所参考的,同时也是对自己的一个积累.在介绍具体实现代码之前,我们必须明确解决这个问题的思 ...

  5. the import java.util cannot be resolve

    重新配置一下build path 的jre,如果不行的话就重新设置jre(在add library中installed JREs)

  6. 痞子衡嵌入式:自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动. 接着上篇文章 <了解i.M ...

  7. python使用requests模块下载文件并获取进度提示

    一.概述 使用python3写了一个获取某网站文件的小脚本,使用了requests模块的get方法得到内容,然后通过文件读写的方式保存到硬盘同时需要实现下载进度的显示 二.代码实现 安装模块 pip3 ...

  8. wxWidgets源码分析(1) - App启动过程

    目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wx ...

  9. Kafka集群消息积压问题及处理策略

    通常情况下,企业中会采取轮询或者随机的方式,通过Kafka的producer向Kafka集群生产数据,来尽可能保证Kafka分区之间的数据是均匀分布的. 在分区数据均匀分布的前提下,如果我们针对要处理 ...

  10. ACM STU week3

    STU ACM训练week3(2.5-2.15) By@Xiezeju 训练计划的CP4配套资源库 训练时间安排 定期任务 任务 每日 进行1小时的盲打训练锻练手速 打字网站,最好注册账号以保存进度 ...