检查函数的无穷大
Check for limit infinity of a function
几周前我写了一个考试。第一项任务是找到具有给定属性的函数的正确近似值:Properties of the function
我必须用我为属性编写的测试来检查每个近似值。属性 2、3 和 4 没问题。但是我不知道如何使用用 Java 编写的 JUnit 测试来检查 属性 1。我的方法是这样做的:
@Test
void test1() {
Double x = 0.01;
Double res = underTest.apply(x);
for(int i = 0; i < 100000; ++i) {
x = rnd(0, x);
Double lastRes = res;
res = underTest.apply(x);
assertTrue(res <= lastRes);
}
}
其中rnd(0, x)
是函数调用,生成一个(0,x]内的随机数。
但这不可能是正确的方法,因为它只是检查 x 变小,结果比上一个小。 IE。如果第一个 res
等于 -5 并且下一个结果比所有 100000 次迭代的前一个小一点,测试也会成功。所以 100000 次迭代后的结果可能是 -5.5(或其他)。这意味着对于 0 的正确限制为 -6 的函数,测试也会成功。有没有办法检查 属性 1?
Is there a way to check for property 1?
琐碎?不,当然不。微积分是关于在无穷大/无限接近任何特定点时发生的事情——基本算术在试图将一个越来越小的数字除以一个越来越小的范围时遇到困难,而基本算术不能做“0除以” 0'.
这里的计算机就像基本算术,或者至少,你在粘贴的代码中应用基本算术,让计算机进行实际的微积分比你在这里做的要复杂得多。如中,多年的编程经验要求更复杂;我非常怀疑你是否应该编写几十万行代码来构建一个完整的计算机化数学平台来编写这个测试,当然。
更一般地说,您似乎在努力相信测试可以证明一切。
他们没有。 测试永远无法证明正确性。就像科学永远无法证明任何事情一样,它们只能反驳(并且可以建立更坚实的基础和证实,任何理性的人通常都认为这些已经足够了,以至于他们会,例如,假定万有引力定律将成立,即使没有证据证明这一点,也永远不会。
测试只能证明你有bug。它永远不能证明你没有。
因此,您在这里可以做的任何事情都只是灰色阴影场景:您可以让这个测试捕获 更多 场景,其中您知道 underTest
算法是不正确的, 但不可能全部捕捉到。
您粘贴的内容已经很有用:此测试证明,当您使用越来越接近 0 的双精度值时,您的测试可确保函数的输出变得越来越小。这是值得的。您似乎认为这种特殊的灰色阴影对您来说不够暗。您想证明它非常接近无穷大。
那是……你的选择。你无法证明它会达到无穷大,因为计算机就像基本算术,其中包含一些近似值。
在这里您无能为力:是的,您可以测试最终的 res
值是否为 'less than -1000000'。您甚至可以测试它是否真的是负无穷大,但不能保证这甚至是正确的;一个定义为 'as the input goes to 0 from the positive end, the output will tend towards negative infinity' 的函数可以自由地只对一个非常小的输入这样做,以至于 double 根本无法表示它(计算机并不神奇;double 需要 64 位,这意味着最多有 2^ double
甚至可以表示的64个唯一数。2^64是一个非常大的数,但与[=的概念的双维无穷大相比算不了什么 43=](0 和 1 之间有无数个数字,毕竟在整条数字线上有无数个这样的数字)。因此,有很多非常小的数字 double
不能代表.根本.
为了您自己的理智,在单元测试中使用随机性不是一个好主意,并且对于 'unit test' 这个词的某些定义,字面意思是错误的(有些人认为单元测试必须可靠,否则它们不能被认为是单元测试,如果你看看 'unit test' 务实地说最终被用于什么,这并不是一个疯狂的概念: 自动拥有测试环境 运行 单元测试,重复和接近不断地,以便在有人破坏时尽快标记。如果 CI 服务器 运行 每天进行 1819 次单元测试,如果纯粹是随机的机会,它会变得非常烦人,20k 次中有一次失败;然后它会假设最近的提交是罪魁祸首,而且据我所知,没有任何框架会重复单元测试几次。最后,如果你远离证明和冷硬定义的概念,编程效果最好,并转向 'do what the community thinks things mean'。对于单元测试,这意味着:不要使用随机性)。
首先:您无法可靠地测试任何属性
- 函数 f 可能会破坏点 x 中的一个属性,由于精度有限,它甚至不能表示为双精度
- 测试点太多,实际需要选择域的子集
其次:
您对限制的定义是错误的。您检查函数是否单调递减。这不是限制定义所要求的 - 函数在接近限制时可能会波动。在一般情况下,我可能会遵循 Weierstrass definition
但是:
通过观察条件,您可以很快注意到对数函数(具有任何底数)满足条件。 (所以函数确实是单调递减的)。
让我们选择自然对数,并检查它在可以表示为双精度的最小 x 处的值:
System.out.println(Double.MIN_VALUE);
System.out.println(Math.log(Double.MIN_VALUE));
System.out.println(Math.log(Double.MIN_VALUE) < -1000);
// prints
4.9E-324
-744.4400719213812
false
如您所见,该值大约为-744,与负无穷大相去甚远。您无法更接近 64 位表示的双精度数。
几周前我写了一个考试。第一项任务是找到具有给定属性的函数的正确近似值:Properties of the function
我必须用我为属性编写的测试来检查每个近似值。属性 2、3 和 4 没问题。但是我不知道如何使用用 Java 编写的 JUnit 测试来检查 属性 1。我的方法是这样做的:
@Test
void test1() {
Double x = 0.01;
Double res = underTest.apply(x);
for(int i = 0; i < 100000; ++i) {
x = rnd(0, x);
Double lastRes = res;
res = underTest.apply(x);
assertTrue(res <= lastRes);
}
}
其中rnd(0, x)
是函数调用,生成一个(0,x]内的随机数。
但这不可能是正确的方法,因为它只是检查 x 变小,结果比上一个小。 IE。如果第一个 res
等于 -5 并且下一个结果比所有 100000 次迭代的前一个小一点,测试也会成功。所以 100000 次迭代后的结果可能是 -5.5(或其他)。这意味着对于 0 的正确限制为 -6 的函数,测试也会成功。有没有办法检查 属性 1?
Is there a way to check for property 1?
琐碎?不,当然不。微积分是关于在无穷大/无限接近任何特定点时发生的事情——基本算术在试图将一个越来越小的数字除以一个越来越小的范围时遇到困难,而基本算术不能做“0除以” 0'.
这里的计算机就像基本算术,或者至少,你在粘贴的代码中应用基本算术,让计算机进行实际的微积分比你在这里做的要复杂得多。如中,多年的编程经验要求更复杂;我非常怀疑你是否应该编写几十万行代码来构建一个完整的计算机化数学平台来编写这个测试,当然。
更一般地说,您似乎在努力相信测试可以证明一切。
他们没有。 测试永远无法证明正确性。就像科学永远无法证明任何事情一样,它们只能反驳(并且可以建立更坚实的基础和证实,任何理性的人通常都认为这些已经足够了,以至于他们会,例如,假定万有引力定律将成立,即使没有证据证明这一点,也永远不会。
测试只能证明你有bug。它永远不能证明你没有。
因此,您在这里可以做的任何事情都只是灰色阴影场景:您可以让这个测试捕获 更多 场景,其中您知道 underTest
算法是不正确的, 但不可能全部捕捉到。
您粘贴的内容已经很有用:此测试证明,当您使用越来越接近 0 的双精度值时,您的测试可确保函数的输出变得越来越小。这是值得的。您似乎认为这种特殊的灰色阴影对您来说不够暗。您想证明它非常接近无穷大。
那是……你的选择。你无法证明它会达到无穷大,因为计算机就像基本算术,其中包含一些近似值。
在这里您无能为力:是的,您可以测试最终的 res
值是否为 'less than -1000000'。您甚至可以测试它是否真的是负无穷大,但不能保证这甚至是正确的;一个定义为 'as the input goes to 0 from the positive end, the output will tend towards negative infinity' 的函数可以自由地只对一个非常小的输入这样做,以至于 double 根本无法表示它(计算机并不神奇;double 需要 64 位,这意味着最多有 2^ double
甚至可以表示的64个唯一数。2^64是一个非常大的数,但与[=的概念的双维无穷大相比算不了什么 43=](0 和 1 之间有无数个数字,毕竟在整条数字线上有无数个这样的数字)。因此,有很多非常小的数字 double
不能代表.根本.
为了您自己的理智,在单元测试中使用随机性不是一个好主意,并且对于 'unit test' 这个词的某些定义,字面意思是错误的(有些人认为单元测试必须可靠,否则它们不能被认为是单元测试,如果你看看 'unit test' 务实地说最终被用于什么,这并不是一个疯狂的概念: 自动拥有测试环境 运行 单元测试,重复和接近不断地,以便在有人破坏时尽快标记。如果 CI 服务器 运行 每天进行 1819 次单元测试,如果纯粹是随机的机会,它会变得非常烦人,20k 次中有一次失败;然后它会假设最近的提交是罪魁祸首,而且据我所知,没有任何框架会重复单元测试几次。最后,如果你远离证明和冷硬定义的概念,编程效果最好,并转向 'do what the community thinks things mean'。对于单元测试,这意味着:不要使用随机性)。
首先:您无法可靠地测试任何属性
- 函数 f 可能会破坏点 x 中的一个属性,由于精度有限,它甚至不能表示为双精度
- 测试点太多,实际需要选择域的子集
其次: 您对限制的定义是错误的。您检查函数是否单调递减。这不是限制定义所要求的 - 函数在接近限制时可能会波动。在一般情况下,我可能会遵循 Weierstrass definition
但是: 通过观察条件,您可以很快注意到对数函数(具有任何底数)满足条件。 (所以函数确实是单调递减的)。
让我们选择自然对数,并检查它在可以表示为双精度的最小 x 处的值:
System.out.println(Double.MIN_VALUE);
System.out.println(Math.log(Double.MIN_VALUE));
System.out.println(Math.log(Double.MIN_VALUE) < -1000);
// prints
4.9E-324
-744.4400719213812
false
如您所见,该值大约为-744,与负无穷大相去甚远。您无法更接近 64 位表示的双精度数。