perl6:为什么数组在声明中跳过计算值?

时间:2019-01-15 16:50:13

标签: perl6

我正在从Perl5学习Perl6。

为了进行编译,我将发布整个程序:

sub lgamma ( Num(Real) \n --> Num ){
  use NativeCall;
  sub lgamma (num64 --> num64) is native {}
  lgamma( n )
}

sub pvalue (@a, @b) {
  if @a.elems <= 1 {
    return 1.0;
  }
  if @b.elems <= 1 {
    return 1.0;
  }
  my Rat $mean1 = @a.sum / @a.elems;
  my Rat $mean2 = @b.sum / @b.elems;
  if $mean1 == $mean2 {
    return 1.0;
  }
  my Rat $variance1 = 0.0;
  my Rat $variance2 = 0.0;

  for @a -> $i {
    $variance1 += ($mean1 - $i)**2#";" unnecessary for last statement in block
  }
  for @b -> $i {
    $variance2 += ($mean2 - $i)**2
  }
  if ($variance1 == 0 && $variance2 == 0) {
    return 1.0;
  }
  $variance1 /= (@a.elems - 1);
  $variance2 /= (@b.elems - 1);

  my $WELCH_T_STATISTIC = ($mean1-$mean2)/sqrt($variance1/@a.elems+$variance2/@b.elems);
    my $DEGREES_OF_FREEDOM = (($variance1/@a.elems+$variance2/@b.elems)**2)
    /
    (
    ($variance1*$variance1)/(@a.elems*@a.elems*(@a.elems-1))+
    ($variance2*$variance2)/(@b.elems*@b.elems*(@b.elems-1))
    );
    my $A = $DEGREES_OF_FREEDOM/2;
    my $value = $DEGREES_OF_FREEDOM/($WELCH_T_STATISTIC*$WELCH_T_STATISTIC+$DEGREES_OF_FREEDOM);
  my Num $beta = lgamma($A)+0.57236494292470009-lgamma($A+0.5);
    my Rat $acu = 10**(-15);
    my ($ai,$cx,$indx,$ns,$pp,$psq,$qq,$rx,$temp,$term,$xx);
# Check the input arguments.
    return $value if $A <= 0.0;# || $q <= 0.0;
    return $value if $value < 0.0 || 1.0 < $value;
# Special cases
    return $value if $value == 0.0 || $value == 1.0;
    $psq = $A + 0.5;
    $cx = 1.0 - $value;
    if $A < $psq * $value {
        ($xx, $cx, $pp, $qq, $indx) = ($cx, $value, 0.5, $A, 1);
    } else {
        ($xx, $pp, $qq, $indx) = ($value, $A, 0.5, 0);
    }
    $term = 1.0;
    $ai = 1.0;
    $value = 1.0;
    $ns = $qq + $cx * $psq;
  $ns = $ns.Int;
#Soper reduction formula.
    $rx = $xx / $cx;
    $temp = $qq - $ai;
    $rx = $xx if $ns == 0;
    while (True) {
        $term = $term * $temp * $rx / ( $pp + $ai );
        $value = $value + $term;
        $temp = $term.abs;
        if $temp <= $acu && $temp <= $acu * $value {
        $value = $value * ($pp * $xx.log + ($qq - 1.0) * $cx.log - $beta).exp / $pp;
        $value = 1.0 - $value if $indx;
        last;
        }
        $ai++;
        $ns--;
        if 0 <= $ns {
            $temp = $qq - $ai;
            $rx = $xx if $ns == 0;
        } else {
            $temp = $psq;
            $psq = $psq + 1.0;
        }
    }
    return $value;
}

my @array2d = ([27.5,21.0,19.0,23.6,17.0,17.9,16.9,20.1,21.9,22.6,23.1,19.6,19.0,21.7,21.4],
 [27.1,22.0,20.8,23.4,23.4,23.5,25.8,22.0,24.8,20.2,21.9,22.1,22.9,20.5,24.4],#0.

 [17.2,20.9,22.6,18.1,21.7,21.4,23.5,24.2,14.7,21.8],
 [21.5,22.8,21.0,23.0,21.6,23.6,22.5,20.7,23.4,21.8,20.7,21.7,21.5,22.5,23.6,21.5,22.5,23.5,21.5,21.8],

 [19.8,20.4,19.6,17.8,18.5,18.9,18.3,18.9,19.5,22.0],
 [28.2,26.6,20.1,23.3,25.2,22.1,17.7,27.6,20.6,13.7,23.2,17.5,20.6,18.0,23.9,21.6,24.3,20.4,24.0,13.2],

 [30.02,29.99,30.11,29.97,30.01,29.99],
 [29.89,29.93,29.72,29.98,30.02,29.98],

 [3.0,4.0,1.0,2.1],
 [490.2,340.0,433.9],

 [<1.0/15.0>, <10.0/62.0>],
 [<1.0/10>, <2/50.0>],

 [0.010268,0.000167,0.000167],
 [0.159258,0.136278,0.122389],

 [9/23.0,21/45.0,0/38.0],
 [0/44.0,42/94.0,0/22.0]);

 say @array2d[11][0];

 my @CORRECT_ANSWERS = (0.021378001462867,
 0.148841696605327,
 0.0359722710297968,
 0.090773324285671,
 0.0107515611497845,
 0.00339907162713746,
 0.52726574965384,
 0.545266866977794);

my UInt $i = 0;
my Real $error = 0.0;
for @array2d -> @left, @right {
  my $pvalue = pvalue(@left, @right);
  $error += ($pvalue - @CORRECT_ANSWERS[$i]).abs;
  say "$i [" ~ @left.join(',') ~ '] [' ~ @right ~ "] = $pvalue";
  if $error > 10**-9 {
    say "\$p = $pvalue, but should be @CORRECT_ANSWERS[$i]";
    die;
  }
#  printf("Test sets %u p-value = %.14g\n",$i+1,$pvalue);
  $i++
}

printf("the cumulative error is %g\n", $error);

此子数组与众不同的原因是它具有用于分隔的“ /”。如何让Perl6 for循环评估此子数组?

编辑:我正在努力构成一个最小的工作示例。我正在发布整个代码,以便对其进行编译。

3 个答案:

答案 0 :(得分:8)

(事实证明,这个答案完全错过了问题@con。但是我不会删除它,因为它收集了一些关于有理数值的希望有用的链接。)

怎么了?

  

为什么数组在声明内跳过计算值?

不是。

  

我正在学习Perl6 ...

在数学和Perl 6中,诸如1.3之类的数字都是小数。一些解释:

  

...来自Perl5

与此有关的事情属于the Perl 5 to Perl 6 guide docs中的一个。您愿意打开a new doc issue吗?

  

[1.0/15.0, 10.0/62.0],#this isn't evaluated

     

[1.0/10, 2/50.0],#neither is this

它们都经过评估。

在数学和P6中,文字1.0是十进制是有道理的。 foo / bar也是有道理的。

(好吧,如果foobar都是整数或有理数,并且结果的分母保持64位以下或为foo或{{1}中的一个}是FatRat有理数的任意精度。)

  

但是,Perl6似乎不喜欢我在这里指出的值。

您还没有解释您看到的东西使您认为P6不喜欢它们的情况。

很有可能是,根据Brad Gilbert++'s comment,您看到的是bar之类的值,而不是<1/15>。前者是P6文字值,可以100%准确地表示0.066667。要100%准确地表示,十进制显示必须为1/15,并带有0.06666666666666...或末尾的某个类似字符,以表示无限重复的最终...。但是6表示相同的数字,并且更短且更简单,因此<1/15>dd使用.perl形式代替。

  

在声明2D数组时,如何让Perl6评估这样的表达式?

您无需执行任何操作。它正在评估他们。 :)

答案 1 :(得分:6)

(这是一个nanswer,本身不是一个答案。它最初是在@con重新编写了他们的问题以包括他们的所有代码之后写的,这是迈向我的Third and final answer的一步。现在希望对于那些学习Perl 6的人来说是有用的资源。)

我对此nanswer的第一次编辑的原始介绍

这是很多代码! :)

到目前为止,我的想法是:您是否绝对肯定地100%确定您还没有错过一些输入数据?您似乎比P6跳过数据的可能性要大得多,尤其是考虑到计算得出的值恰好是您期望获得下一个正确结果的值。

更新确实确实是问题出在输入数据不正确。)

有关代码的注释的翻译

此nanswer的其余部分是问题代码的逐行“清理”(而非重构)。我有两个目标,第二个是最重要的,亲爱的读者,请阅读以下内容:

  • 我的翻译有效地向我证明并向@con证明我已经考虑了他们的所有代码。这是为了减少有关错误可能在哪里的不确定性。请注意,我的大部分更改可能与它们的错误没有直接关系,但是直到完成重写后,我才感到难以接受。

  • @con的代码以及我的翻译可能对学习Perl 6的任何人有用。。 @con的代码是Perl 5 code的P6转换。 P5代码又是C code的翻译。该代码还有其他翻译成其他语言的版本。我的代码接受@con的翻译,并将其翻译成更惯用的版本,并附有解释我为何更改其代码的注释。

我对相关代码的翻译,可以通过tio运行

My translation of @con's code, minus my commentary (see below), in tio

(我最初的计划是,我们将基于在tio中修改代码并共享指向修改后的tio版本的链接来进一步研究@con的错误。为此,您/他们只需在tio中单击位于以下位置的链接图标单击顶部的()即可获得指向代码的链接(当单击链接图标时。)Tio非常适合能够编写,编辑和运行以P6和其他语言编写的代码,然后进行共享。)

我对相关代码的翻译,并附有评论

我已经离开了开始lgamma例程。我在脚注中写了很多评论,主要针对其他读者,因为它包含了大量有趣的功能 1

sub lgamma ( Num(Real) \n --> Num ){
  use NativeCall;
  sub lgamma (num64 --> num64) is native {}
  lgamma( n )
}

我注意到lgamma代码全都与浮点数有关(在P6中为Num / num...类型)。


sub pvalue (@a, @b) {

对于不了解Perls的用户,sub关键字引入了子例程(又称函数)。

这个有两个“ listy”(Positional)参数。

(当您看到@(例如,以"sigil"@foo或例如@($bar)的操作符)时,请考虑“列表”。 )


为了加快代码读取速度并减少重复代码,我将其替换为:

  if @a.elems <= 1 {
    return 1.0;
  }
  if @b.elems <= 1 {
    return 1.0;
  }

与此:

  return 1 if @a | @b <= 1;

如果列表1或列表@a中的元素数小于或等于1,则用英语将其读为“返回@b”。


我使用|运算符构造了一个any junction

(不要浪费时间试图围绕结点的作用原理以及它们如何做的理论。只需以简单和/或简洁的方式在实践中使用它们即可。如果保持简单,请阅读以一种简单的方式,它们很棒,并且只做了显而易见的事情。如果使用它们使代码在特定情况下不明显,则可以考虑针对该特定情况使用其他构造。)


数字上下文中的数组将求值为其元素数。像<=这样的数字运算符会强加数字上下文。因此,我放下了.elems

数字上下文和在数字上下文中评估其长度的数组是P6的基本方面,因此这是惯用编码,适用于除最基本的入门新手示例以外的所有示例。


我从1.0切换到1

我的猜测是@con编写了1.0,因为这是将值按字面意义用它们正在翻译的代码编写的方式,和/或意图是它代表浮点值。但是在P6中,像e这样的普通十进制文字(没有1.0指数)会产生有理数。

在此代码中使用1.0而不是1将较简单的较快类型(整数)替换为较复杂的较慢类型(理性)。当在 any 组件值为浮点的公式中使用此较慢类型的值时,将强制将其转换为浮点数(比整数慢)。此代码中的大多数或所有公式都会发生这种情况,因为lgamma返回浮点数。

更一般地说,如果您没有指定变量和值的类型未指定,则P6的工作和读取效果最佳,除非有令人信服的理由指定它们,否则编译器可以自行确定。保留未指定的类型可以减少读者的认知负担,并增加了代码重用和编译器代码优化的灵活性。

这导致一对互补的格言:

  • 默认情况下,保留类型信息隐式 。如果您不知道给定值或变量的类型是什么还是无关紧要的,请不要指定它。

  • 如果您将值,变量或参数的类型设为显式,则P6将使用该类型,强制遵守该类型,无论是否改善代码或不必要地降低代码速度或完全停止代码。

如果您完全知道,您需要使用其他类型或添加类型约束以使代码正确,那么请务必继续。同样,如果您已经已经验证您的代码正确 而没有更具体的类型,但是您想使其变得更快,更安全或更清晰,请继续。但是,如果您不知道,则将其保留为通用名称。正如Ben曾经说过的那样,P6的设计符合您的意思,并且成功的频率将超出您的想象。

premature optimization相似,在P6中,premature typing is an anti-pattern用一次性代码和长期生产代码来更强有力地说明这一点。


  my Rat $mean1 = @a.sum / @a.elems;
  my Rat $mean2 = @b.sum / @b.elems;
  if $mean1 == $mean2 {
    return 1.0;
  }

成为:

  my (\mean_a, \mean_b) = @a.sum / @a, @b.sum / @b;
  return 1 if mean_a == mean_b;

除非我知道需要贴记号,否则我通常将“变量”“减号”。如果“变量”实际上没有变化,则可能不需要标记。

我将$mean1重命名为mean_a,因为它显然对应于@a


  my Rat $variance1 = 0.0;
  my Rat $variance2 = 0.0;

  for @a -> $i {
    $variance1 += ($mean1 - $i)**2#";" unnecessary for last statement in block
  }
  for @b -> $i {
    $variance2 += ($mean2 - $i)**2
  }

成为:

  my ($vari_a, $vari_b);

  $vari_a += (mean_a - $_)² for @a;
  $vari_b += (mean_b - $_)² for @b;

将变量$_读为“ it”。


  if ($variance1 == 0 && $variance2 == 0) {
    return 1.0;
  }
  $variance1 /= (@a.elems - 1);
  $variance2 /= (@b.elems - 1);

成为:

  return 1 unless ($vari_a or $vari_b);

  $vari_a /= (@a - 1);
  $vari_b /= (@b - 1);

在Perls中,0在布尔测试上下文中的值为False。并且像@a这样的数组或列表在数字上下文中对其元素计数进行求值。


  my $WELCH_T_STATISTIC = ($mean1-$mean2)/sqrt($variance1/@a.elems+$variance2/@b.elems);
  my $DEGREES_OF_FREEDOM = (($variance1/@a.elems+$variance2/@b.elems)**2)
  /
  (
  ($variance1*$variance1)/(@a.elems*@a.elems*(@a.elems-1))+
  ($variance2*$variance2)/(@b.elems*@b.elems*(@b.elems-1))
  );
  my $A = $DEGREES_OF_FREEDOM/2;
  my $value = $DEGREES_OF_FREEDOM/($WELCH_T_STATISTIC*$WELCH_T_STATISTIC+$DEGREES_OF_FREEDOM);
  my Num $beta = lgamma($A)+0.57236494292470009-lgamma($A+0.5);
  my Rat $acu = 10**(-15);
  my ($ai,$cx,$indx,$ns,$pp,$psq,$qq,$rx,$temp,$term,$xx);
  # Check the input arguments.
  return $value if $A <= 0.0;# || $q <= 0.0;

成为:

  my \WELCH_T_STATISTIC  = (mean_a - mean_b)
                / ( $vari_a / @a   +   $vari_b / @b ).sqrt;

  my \DEGREES_OF_FREEDOM = ($vari_a / @a   +   $vari_b / @b)²
          / ($vari_a² / (@a² * (@a - 1))   +   $vari_b² / (@b² * (@b - 1)));

  my \A                  = DEGREES_OF_FREEDOM / 2;
  my $value              = DEGREES_OF_FREEDOM
             / (WELCH_T_STATISTIC² + DEGREES_OF_FREEDOM);

  my \magic-num          = 0.57236494292470009;
  my \beta               = lgamma(A) + magic-num - lgamma(A + 0.5);
  my \acu                = 1e-15;

  # Check the input arguments.
  return $value if A <= 0;# || $q <= 0.0;

(上一行中的$q是什么?)

请注意,我将类型分别放在了betaacu上。分配给beta 变量仍然是Num,因为lgamma返回了Num。分配给acu的值也将是Num,因为在数字文字中使用e指数意味着它构造的值是Num


  return $value if $value < 0.0 || 1.0 < $value;
  # Special cases
  return $value if $value == 0.0 || $value == 1.0;

成为:

  return $value unless $value ~~ 0^..^1;

0^读为“大于零”(不包括零),将^1读为“最大1”(不包括一个)。

~~是“智能匹配”运算符。如果其右侧的值接受其左侧的值,它将返回True

因此,如果$value小于或等于$value或大于或等于0,此return语句将返回1


作为一个小小的整理,我移动了一个变量my的声明,我在上面的重写中删除了变量,直到此时它们才变得相关,并也添加了$ns:< / p>

  my ($ai, $cx, $indx, $pp, $psq, $qq, $rx, $temp, $term, $xx, $ns);

  $psq = $A + 0.5;
  $cx = 1.0 - $value;
  if $A < $psq * $value {
      ($xx, $cx, $pp, $qq, $indx) = ($cx, $value, 0.5, $A, 1);
  } else {
      ($xx, $pp, $qq, $indx) = ($value, $A, 0.5, 0);
  }

成为:

  $psq = A + 0.5;
  $cx = 1 - $value;
  ($xx, $cx, $pp, $qq, $indx) =
        A < $psq * $value
          ?? ($cx, $value, 0.5, A, 1)
          !! ($value, $cx, A, 0.5, 0);

我将一堆变量的条件赋值重写为ternary,因此更容易看到将什么赋给了什么。


  $term = 1.0;
  $ai = 1.0;
  $value = 1.0;
  $ns = $qq + $cx * $psq;
  $ns = $ns.Int;

成为:

  $term = 1;
  $ai = 1;
  $value = 1;
  $ns = ($qq + $cx * $psq) .Int;

1.0替换为1,并将分配给$ns的表达式与.Int强制组合起来。

(我在翻译时从代码中剥离了类型,它继续计算出正确的结果,只是消除了上面的Int强制使代码变成了infiniloop。这最终导致我搜寻网络以查看是否我可以找到@con正在翻译的代码。那是我在rosettacode.org上找到的。在我看到的代码中,它明确地键入为整数,因此大概是确保算法有效的关键。)


  #Soper reduction formula.
  $rx = $xx / $cx;
  $temp = $qq - $ai;
  $rx = $xx if $ns == 0;

(不变)


  while (True) {

成为:

  loop {

    $term = $term * $temp * $rx / ( $pp + $ai );
    $value = $value + $term;
    $temp = $term.abs;

(不变)


    if $temp <= $acu && $temp <= $acu * $value {
      $value = $value * ($pp * $xx.log + ($qq - 1.0) * $cx.log - $beta).exp / $pp;
      $value = 1.0 - $value if $indx;
      last;
    }

成为:

    if $temp <= acu & acu * $value {
      $value = $value * ($pp * $xx.log + ($qq - 1) * $cx.log - beta).exp / $pp;
      $value = 1 - $value if $indx;
      last;
    }

这一次包含结点(&)的条件用英语读取为“如果temp小于或等于acu和acu times值”。


    $ai++;
    $ns--;
    if 0 <= $ns {
        $temp = $qq - $ai;
        $rx = $xx if $ns == 0;
    } else {
        $temp = $psq;
        $psq = $psq + 1;
    }
  }
  return $value;
}

我刚刚将1.0替换为1


现在有问题的数组。正如我在开始时所写的那样,我很确定您(或者您的数据提供者)忘记了几行:

my @array2d =

  [27.5,21.0,19.0,23.6,17.0,17.9,16.9,20.1,21.9,22.6,23.1,19.6,19.0,21.7,21.4],
  [27.1,22.0,20.8,23.4,23.4,23.5,25.8,22.0,24.8,20.2,21.9,22.1,22.9,20.5,24.4],

  [17.2,20.9,22.6,18.1,21.7,21.4,23.5,24.2,14.7,21.8],
  [21.5,22.8,21.0,23.0,21.6,23.6,22.5,20.7,23.4,21.8,20.7,21.7,21.5,22.5,23.6,21.5,22.5,23.5,21.5,21.8],

  [19.8,20.4,19.6,17.8,18.5,18.9,18.3,18.9,19.5,22.0],
  [28.2,26.6,20.1,23.3,25.2,22.1,17.7,27.6,20.6,13.7,23.2,17.5,20.6,18.0,23.9,21.6,24.3,20.4,24.0,13.2],

  [30.02,29.99,30.11,29.97,30.01,29.99],
  [29.89,29.93,29.72,29.98,30.02,29.98],

  [3.0,4.0,1.0,2.1],
  [490.2,340.0,433.9],

  [<1.0/15.0>, <10.0/62.0>],
  [<1.0/10>, <2/50.0>],

  [0.010268,0.000167,0.000167],
  [0.159258,0.136278,0.122389],

  [9/23.0,21/45.0,0/38.0],
  [0/44.0,42/94.0,0/22.0];

say @array2d[11][0];

这些是谁的回答?您是否100%确定0.0033...答案与[<1.0/15.0>, <10.0/62.0>],[<1.0/10>, <2/50.0>]数据一起使用?

my @CORRECT_ANSWERS =
      0.021378001462867,
      0.148841696605327,
      0.0359722710297968,
      0.090773324285671,
      0.0107515611497845,
      0.00339907162713746,
      0.52726574965384,
      0.545266866977794;

最后,我只是删除了类型,并再次为更漂亮的值加指数(10⁻⁹)使用上标:

my $i = 0;
my $error = 0;
for @array2d -> @left, @right {
  my $pvalue = pvalue(@left, @right);
  $error += ($pvalue - @CORRECT_ANSWERS[$i]).abs;
  say "$i [" ~ @left.join(',') ~ '] [' ~ @right ~ "] = $pvalue";
  if $error > 10⁻⁹ {
    say "\$p = $pvalue, but should be @CORRECT_ANSWERS[$i]";
    die;
  }
  #  printf("Test sets %u p-value = %.14g\n",$i+1,$pvalue);
  $i++
}

printf("the cumulative error is %g\n", $error);

脚语

1 喜欢这个不错的lgamma例程! (结果为Brad Gilbert wrote it。)

sub lgamma ( Num(Real) \n --> Num ){
  use NativeCall;
  sub lgamma (num64 --> num64) is native {}
  lgamma( n )
}

展示:

  • 使用同名的高级P6(Perl 6)例程对低级(C)例程进行阴影处理,以在调用C函数之前添加进一步的处理。

  • Explicitly converting the input type从较宽的P6类型Real到较窄的Num。 Perls是该术语的原始技术定义,"strongly typed",但是P6每几个other interpretations of "strong typing"都会提供附加的选项,并且相对于语言而言,这些语言对这些其他“强类型”的解释能力较弱(如Perl 5,Python和C)。显式类型转换是P6引入的这种功能转变的一部分。

  • 削减印记。我将在本文其他地方进行的相同操作中对此进行进一步讨论。

  • 对库use进行词法作用域划分。内部lgamma例程和use Nativecall;导入的符号在任何地方都看不到,但在包含它的外部lgamma例程内部则看不到。

  • 使用NativeCall(P6 C FFI)允许高级P6代码直接甜蜜地映射到C代码,包括从P6的IEEE双浮点兼容boxed type { {1}}到与未装箱的machine data type等价的num64

全部5行!非常好。 :)

答案 2 :(得分:4)

第三个也是最后一个答案

.o(他希望地说。有人曾写过四个这样的问题答案吗?!?)

如果您在数据中交换这些行:

  [<1.0/15.0>, <10.0/62.0>],
  [<1.0/10>, <2/50.0>],

  [0.010268,0.000167,0.000167],
  [0.159258,0.136278,0.122389],

相反,相反:

  [0.010268,0.000167,0.000167],
  [0.159258,0.136278,0.122389],

  [<1.0/15.0>, <10.0/62.0>],
  [<1.0/10>, <2/50.0>],

然后,程序的输出(至少是我通过重写代码nanswer编写的程序的输出):

0.159258
0 [27.5,21,19,23.6,17,17.9,16.9,20.1,21.9,22.6,23.1,19.6,19,21.7,21.4] [27.1 22 20.8 23.4 23.4 23.5 25.8 22 24.8 20.2 21.9 22.1 22.9 20.5 24.4] = 0.02137800146286709
1 [17.2,20.9,22.6,18.1,21.7,21.4,23.5,24.2,14.7,21.8] [21.5 22.8 21 23 21.6 23.6 22.5 20.7 23.4 21.8 20.7 21.7 21.5 22.5 23.6 21.5 22.5 23.5 21.5 21.8] = 0.14884169660532756
2 [19.8,20.4,19.6,17.8,18.5,18.9,18.3,18.9,19.5,22] [28.2 26.6 20.1 23.3 25.2 22.1 17.7 27.6 20.6 13.7 23.2 17.5 20.6 18 23.9 21.6 24.3 20.4 24 13.2] = 0.035972271029797116
3 [30.02,29.99,30.11,29.97,30.01,29.99] [29.89 29.93 29.72 29.98 30.02 29.98] = 0.09077332428566681
4 [3,4,1,2.1] [490.2 340 433.9] = 0.010751561149784494
5 [0.010268,0.000167,0.000167] [0.159258 0.136278 0.122389] = 0.003399071627137453
6 [1.0/15.0,10.0/62.0] [1.0/10 2/50.0] = 0.5272657496538401
7 [0.391304,0.466667,0] [0 0.446809 0] = 0.5452668669777938
the cumulative error is 5.50254e-15

回想起来,这很明显是错误的。 2020年事后诸葛亮。 :)