我已经用Java编写了一些捕食者-猎物模拟。即使规则很复杂并且最终陷入混乱的系统中,使用的技术也很简单:
因此,我认为在使用相同参数初始化系统时,它应该输出相同的结果,但是没有,我想知道为什么。
关于这一点的一些想法:我的应用程序使用Random
s,但是对于该测试,我使用给定值将它们全部初始化,因此,根据我的理解,它们应为每次运行创建相同顺序的相同输出。
我正在遍历Set
s,并且我知道a Set
的迭代顺序未定义。但是我看不出为什么以Set
相同顺序填充相同值的a在几次运行中表现出不同的任何原因。可以?
我正在用很多float
s。我总是怀疑1 + 1 = 1.9999999999725的数据类型,但是即使我的行为对我来说很奇怪,也应该总是一样。是不是
垃圾回收不是确定性的,但是只要我不依赖析构函数,我就应该是安全的。
如上所述,取决于实际使用时间,没有并发和数据类型。
我无法在一个简单的示例中重现该行为。但是遍历我的代码,我看不到任何无法预测的东西。那么我上面的任何假设是错误的吗?有什么想法我可能会错过吗?
这是验证我的假设的测试:
public static void main(String[] args) {
Random r = new Random(1);
Set<Float> s = new HashSet<Float>();
for (int i = 0; i < 1000000; i++) {
s.add(r.nextFloat());
}
float ret = 1;
int cnt = 0;
for (Float f : s) {
float multiply = 0.3f;
if (cnt++ % 2 == 0) {
multiply = 0.7f;
}
float f2 = (f * multiply);
ret += f2;
}
System.out.println(ret);
}
对我来说,结果总是242455.25。
您可以使用Java编写确定性程序。您只需要消除不确定性的可能来源。
在不查看您的实际代码以及该确定性的具体证据的情况下,很难知道是什么导致了不确定性。
有许多库方法可能是不确定性行为的来源……这取决于您如何使用它们。
例如,Object.hashcode()
(第一次在实例上调用它)返回的值是不确定的。这渗入到任何使用哈希的库中。它绝对可以影响它的要素的顺序HashSet
或HashMap
当你重复他们返回......如果元素类没有重载hashcode()
。
随机数生成器可能是确定性的,也可能不是确定性的。如果它们是伪随机的,并且使用固定的种子进行初始化,则每个序列产生的数字序列将是确定的。
浮点算法应该是确定性的。对于算术表达式的任何(固定)输入集,结果应始终相同。(我不确定JLS是否可以保证浮点运算的确定性,但是如果它在实践中发生,那就太奇怪了。就像在...上运行的是损坏的硬件。)
FOLLOWUP ... on strictfp
和不确定性。
根据JLS 15.4:
“在不受FP限制的表达式中,为实现提供了一定的回旋余地,以使用扩展的指数范围来表示中间结果;大致而言,最终结果是,在以下情况下,计算可能会产生“正确答案”独占使用浮点值集或双精度值集可能会导致上溢或下溢。”
这并不能完全说明该实现在非FP严格表达式中有多少 “余地”。但是,我曾经以为,这种余地不会扩展到允许非确定性行为。我以为在特定平台上的JIT编译器将始终为同一表达式生成等效的本机代码,并且该代码将是确定性的。(除非硬件本身具有非确定性的浮点数,否则我看不到任何非确定性的原因。)其他不确定性的可能来源可能是JIT编译和解释的代码的行为可能不同。但坦率地说,我认为这将是“坚果”……我想我们已经听说过。
因此,尽管非FP严格的表达评估在理论上可能是不确定的,但我认为我们应该轻视它……除非有明确的证据表明它在实践中发生。
(请注意,我说的是真正的不确定性,而不是平台差异。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句