Các vòng lặp không tương đương, và bạn chủ yếu là đập vỡ gói bigint và nó không liên quan gì với for
vs while
mỗi lần.
Vòng lặp while sử dụng ký hiệu '$s *= $i
' nhưng vòng lặp for sử dụng '$s = $s * $i
'. Đơn giản là đủ để chứng minh rằng chúng không giống nhau. Ngoài ra, một vòng lặp đếm lên; người khác đếm ngược. Điều này ảnh hưởng đến cách nhân các số lớn. Đó là một hiệu ứng thứ hai - nhưng không hoàn toàn không đáng kể.
[Cập nhật: sửa đổi để chỉ hiển thị một phiên bản của mã, với thời gian phụ thứ hai. Có chỗ để nghĩ rằng việc in ấn nên được loại trừ khỏi các tính toán thời gian; mà làm cho mọi thứ rối tung hơn, vì vậy tôi đã không làm phiền. Tôi đã sửa lỗi trong phiên bản trước: vòng 4 cũng giống như vòng lặp 3 - bây giờ nó không phải là. Tôi cũng đã tarted lên định dạng đầu ra (mặc dù xử lý phụ thứ hai có thể được cải thiện - một bài tập cho người đọc), và có 'báo cáo tiến độ' tốt hơn.]
Kết quả thời gian trên máy Mac Mini (Snow Leopard 10.6.2) là:
Count up $s *= $i: 00:00:12.663337
Count up $s = $s * $i: 00:00:20.686111
Count down $s *= $i: 00:00:14.201797
Count down $s = $s * $i: 00:00:23.269874
Kịch bản:
use Time::HiRes qw(gettimeofday);
use strict;
use warnings;
use bigint;
use constant factorial_of => 13000;
sub delta_t
{
my($tag, $t1, $t2) = @_;
my($d) = int($t2 - $t1);
my($f) = ($t2 - $t1) - $d;
my($s) = sprintf("%.6f", $f);
$s =~ s/^0//;
printf "%-25s %02d:%02d:%02d%s\n",
$tag, int($d/3600), int(($d % 3600)/60), int($d % 60), $s;
}
my $t1 = gettimeofday;
{
my $n = factorial_of;
my $s = 1;
for (my $i = 2; $i <= $n; $i++)
{
$s *= $i;
}
print "$s\n: Loop 1\n";
}
my $t2 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
{
my $n = factorial_of;
my $s = 1;
for (my $i = 2; $i <= $n; $i++)
{
$s = $s * $i;
}
print "$s\n: Loop 2\n";
}
my $t3 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
delta_t('Count up $s = $s * $i:', $t2, $t3);
{
my $n = factorial_of;
my $s = 1;
for (my $i = $n; $i > 1; $i--)
{
$s *= $i;
}
print "$s\n: Loop 3\n";
}
my $t4 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
delta_t('Count up $s = $s * $i:', $t2, $t3);
delta_t('Count down $s *= $i:', $t3, $t4);
{
my $n = factorial_of;
my $s = 1;
for (my $i = $n; $i > 1; $i--)
{
$s = $s * $i;
}
print "$s\n: Loop 4\n";
}
my $t5 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
delta_t('Count up $s = $s * $i:', $t2, $t3);
delta_t('Count down $s *= $i:', $t3, $t4);
delta_t('Count down $s = $s * $i:', $t4, $t5);
Và đây là một phiên bản nhỏ gọn hơn nhiều của đoạn mã trên, được mở rộng để kiểm tra các vòng lặp 'while' cũng như 'for'. Nó cũng đề cập đến hầu hết các vấn đề về thời gian. Điều duy nhất không lý tưởng (với tôi) là nó sử dụng một vài biến toàn cầu, và tôi băm mã trong mã refs một chút để tất cả phù hợp trên một dòng mà không kích hoạt một thanh cuộn (trên màn hình của tôi, anyway). Rõ ràng, với công việc nhiều hơn một chút, thử nghiệm có thể được bao bọc thành một mảng, do đó việc kiểm tra sẽ được thực hiện lặp đi lặp lại - một vòng lặp thông qua mảng chạy chức năng hẹn giờ trên thông tin trong mảng. Vv ...đó là SMOP - Vấn đề lập trình đơn giản. (Nó in băm MD5 của giai thừa, chứ không phải giai thừa, vì nó dễ dàng so sánh kết quả, vv. Nó đã chỉ ra một vài lỗi khi tôi đang tái cấu trúc mã ở trên. Có, MD5 không an toàn - nhưng tôi không sử dụng nó cho an ninh; chỉ để phát hiện những thay đổi chủ ý)
use Time::HiRes qw(gettimeofday);
use Digest::MD5 qw(md5_hex);
use strict;
use warnings;
use bigint;
use constant factorial_of => 13000;
my ($s, $i);
my $l1 = sub {my($n) = @_; for ($i = 2; $i <= $n; $i++) { $s *= $i; }};
my $l2 = sub {my($n) = @_; for ($i = 2; $i <= $n; $i++) { $s = $s * $i; }};
my $l3 = sub {my($n) = @_; for ($i = $n; $i > 1; $i--) { $s *= $i; }};
my $l4 = sub {my($n) = @_; for ($i = $n; $i > 1; $i--) { $s = $s * $i; }};
my $l5 = sub {my($n) = @_; $i = 2; while ($i <= $n) { $s *= $i; $i++; }};
my $l6 = sub {my($n) = @_; $i = 2; while ($i <= $n) { $s = $s * $i; $i++; }};
my $l7 = sub {my($n) = @_; $i = $n; while ($i > 1) { $s *= $i; $i--; }};
my $l8 = sub {my($n) = @_; $i = $n; while ($i > 1) { $s = $s * $i; $i--; }};
sub timer
{
my($n, $code, $tag) = @_;
my $t1 = gettimeofday;
$s = 1;
&$code(factorial_of);
my $t2 = gettimeofday;
my $md5 = md5_hex($s);
printf "Loop %d: %-33s %09.6f (%s)\n", $n, $tag, $t2 - $t1, $md5;
}
my $count = 1;
timer($count++, $l1, 'for - Count up $s *= $i:');
timer($count++, $l2, 'for - Count up $s = $s * $i:');
timer($count++, $l3, 'for - Count down $s *= $i:');
timer($count++, $l4, 'for - Count down $s = $s * $i:');
timer($count++, $l5, 'while - Count up $s *= $i:');
timer($count++, $l6, 'while - Count up $s = $s * $i:');
timer($count++, $l7, 'while - Count down $s *= $i:');
timer($count++, $l8, 'while - Count down $s = $s * $i:');
Ví dụ đầu ra (MD5 checksum nén để tránh dòng bẻ - giá trị đầy đủ là 584b3ab832577fd1390970043efc0ec8
):
Loop 1: for - Count up $s *= $i: 12.853630 (584b3ab8...3efc0ec8)
Loop 2: for - Count up $s = $s * $i: 20.854735 (584b3ab8...3efc0ec8)
Loop 3: for - Count down $s *= $i: 14.798155 (584b3ab8...3efc0ec8)
Loop 4: for - Count down $s = $s * $i: 23.699913 (584b3ab8...3efc0ec8)
Loop 5: while - Count up $s *= $i: 12.972428 (584b3ab8...3efc0ec8)
Loop 6: while - Count up $s = $s * $i: 21.192956 (584b3ab8...3efc0ec8)
Loop 7: while - Count down $s *= $i: 14.555620 (584b3ab8...3efc0ec8)
Loop 8: while - Count down $s = $s * $i: 23.790795 (584b3ab8...3efc0ec8)
tôi. liên tục thấy một hình phạt nhỏ (< 1%) cho vòng lặp 'while' qua vòng lặp 'for' tương ứng, nhưng tôi không có giải thích tốt cho nó.
Có thể bạn đang cố gắng tối ưu hóa điều sai. Tôi tự hỏi tại sao bạn nghĩ rằng phần đặc biệt của ngôn ngữ này lại quan trọng đến vậy? – Ether
Hãy chạy chúng một vài lần, và chúng có thể trung bình với cùng một khoảng – CaffGeek
@Chad, thực sự tôi đã thử nghiệm mã này một vài lần. Họ đã mất nhiều thời gian khác nhau để hoàn thành công việc tương tự. Tôi nghĩ rằng lời giải thích của @Jonathan Leffler với mã minh họa có ý nghĩa rất tốt. – Mike