每当我查看 $z 时,它的计算结果为 6 而不是 7,
use bigint;导致数字文字被 Math::BigInt 对象替换。例如,
1
被替换为
Math::BigInt->new(1)
当将 Math::BigInt 对象用作操作数时,Math::BigInt 又会覆盖许多运算符。
像这样,
use bigint;
my $z = ceil(sqrt($n-1));
相当于
use Math::BigInt;
my $z = ceil(sqrt($n-Math::BigInt->new(1)));
这相当于
use Math::BigInt;
my $temp = Math::BigInt->new(1); # 1 [BigInt]
$temp->bneg; # -1 [BigInt]
$temp->badd($n); # 40 [BigInt]
$temp->bsqrt(); # 6 [BigInt] <--- XXX
$temp = $temp->numify; # 6 [Primitive]
my $z = ceil($temp); # 6 [Primitive]
因此,当您不想使用时,您正在使用 Math::BigInt。不要那样做!!!只需使用
# No "use bigint;"!!!
my $z = ceil(sqrt($n-1));
当然,您链接的算法实际上需要
# No "use bigint;"!!!
my $z = ceil(sqrt($n));
因为use bigint;可以在远处产生很大的影响,我个人觉得use bigint;太神奇了。我宁愿Math::BigInt->new(...)在适当的地方使用,也不愿use bigint;将所有数值常量转换为 Math::BigInt 对象。我也宁愿使用 Math::BigInt 的方法而不是重载的运算符。这样的惊喜要少得多(例如,使用 时失去大量支持ceil)。
use warnings;
use strict;
use feature qw( say );
use Config qw( %Config );
use Math::BigInt qw( );
use POSIX qw( ceil );
# Each of the arguments is expected to be in [0, 2^32).
# Should use exponentiation by squaring instead of larger number support.
sub pow_m {
my ($base, $exp, $mod) = @_;
my $n = Math::BigInt->new($base);
$n->bpow($exp);
$n->bmod($mod);
return $n->numify();
}
# Each of the arguments is expected to be in [0, 2^32).
# Requires a 64-bit integers or $e might overflow.
sub babystep_giantstep {
my ($g, $h, $mod) = @_;
my $m = ceil(sqrt($mod));
my %table;
my $e = 1;
for my $i (0..$m-1) {
$table{$e} = $i;
$e = ($e * $g) % $mod;
}
my $factor = pow_m($g, $mod-$m-1, $mod);
$e = $h;
for my $i (0..$m-1) {
if (exists($table{$e})) {
return $i*$m + $table{$e};
}
$e = ($e * $factor) % $mod;
}
return undef;
}
{
$Config{uvsize} >= 8
or warn("Results may overflow\n");
my ($g, $h, $mod) = @ARGV;
my $log = babystep_giantstep($g, $h, $mod);
say $log;
my $test = Math::BigInt->new($g);
$test->bpow($log);
$test->bmod($mod);
$test = $test->numify;
say $test == $h ? "ok" : "not ok";
}