2011-06-15 49 views
3

給定一串數字,我必須使用Perl儘可能快地總和所有數字。Perl中一個字符串的最快校驗位數例程是什麼?

我的第一個實現是用unpack()解開數字,然後用List :: Utils sum()和數字列表。 速度非常快,但是這個任務有更快的包裝/解包配方嗎?

我嘗試了一個pack/unpack組合,並對兩個實現進行了基準測試。 使用的CPU時間幾乎相同;也許有一些我不知道的快速技巧?

這裏是我做了我的標杆:

#!/usr/bin/env perl 

use 5.012; 
use strict; 
use List::Util qw/sum/; 
use Benchmark qw/timethese/; 

timethese (1000000, { 
    list_util => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = sum(unpack('AAAAAAAAA', $CheckDigit)); 
     } while ($CheckDigit > 9); 
    }, 
    perl_only => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = unpack('%16S*', pack('S9', unpack('AAAAAAAAA', $CheckDigit))); 
     } while ($CheckDigit > 9); 
    }, 
}); 
+0

也許[Inline :: C](http://p3rl.org/Inline::C-Cookbook) ?您可以使用SvIV宏將Perl標量變量轉換爲C int。 – daxim 2011-06-15 09:51:26

+0

我總是在這種情況下思考C(和Inline :: C),但是非常謙虛,我認爲List :: Util sum()是迄今爲止最快的實現。 – 2011-06-15 10:35:00

回答

4

如何不與包太聰明/解壓縮,並使用一個簡單的拆分,或者是輕度數學聰明和使用模,其擊敗屁滾尿流所有其他方法?

#!/usr/bin/env perl 

use strict; 
use List::Util qw/sum/; 
use Benchmark qw/timethese/; 

my $D="99949596"; 

timethese (1000000, { 
    naive => sub { 
     my $CheckDigit= $D; 
     do { 
      $CheckDigit = sum(split//, $CheckDigit); 
     } while ($CheckDigit > 9); 
    }, 
    list_util => sub { 
     my $CheckDigit = $D; 
     do { 
      $CheckDigit = sum(unpack('AAAAAAAAA', $CheckDigit)); 
     } while ($CheckDigit > 9); 
    }, 
    perl_only => sub { 
     my $CheckDigit = $D; 
     do { 
      $CheckDigit = unpack('%16S*', pack('S9', unpack('AAAAAAAAA', $CheckDigit))); 
     } while ($CheckDigit > 9); 
    }, 
    modulo => sub { 
     my $CheckDigit = $D % 9; 
    }, 
}); 

結果:

Benchmark: timing 1000000 iterations of list_util, modulo, naive, perl_only... 
list_util: 5 wallclock secs (4.62 usr + 0.00 sys = 4.62 CPU) @ 216450.22/s (n=1000000) 
    modulo: -1 wallclock secs (0.07 usr + 0.00 sys = 0.07 CPU) @ 14285714.29/s (n=1000000) 
      (warning: too few iterations for a reliable count) 
    naive: 3 wallclock secs (2.79 usr + 0.00 sys = 2.79 CPU) @ 358422.94/s (n=1000000) 
perl_only: 6 wallclock secs (5.18 usr + 0.00 sys = 5.18 CPU) @ 193050.19/s (n=1000000) 
+0

「輕度巧妙」似乎是最快的。我不知道那個模數的財產,我的錯。 – 2011-06-15 09:54:24

+0

我知道這個地產,但我不認爲它會成爲最快的方法,所以我們今天都學到了一些東西。 – mirod 2011-06-15 09:56:24

+4

注意,當其他方法產生9時,「$ D%9」產生0,實際的等價函數是'($ D + 0)&&($ D%9 || 9)'。 – cjm 2011-06-15 10:16:13

6

解包不是分割字符串的最快方法:

#!/usr/bin/env perl 

use strict; 
use List::Util qw/sum/; 
use Benchmark qw/cmpthese/; 

cmpthese (-3, { 
    list_util => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = sum(unpack('AAAAAAAAA', $CheckDigit)); 
     } while ($CheckDigit > 9); 
    }, 
    unpack_star => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = sum(unpack('(A)*', $CheckDigit)); 
     } while ($CheckDigit > 9); 
    }, 
    re => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = sum($CheckDigit =~ /(.)/g); 
     } while ($CheckDigit > 9); 
    }, 
    split => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = sum(split //, $CheckDigit); 
     } while ($CheckDigit > 9); 
    }, 
    perl_only => sub { 
     my $CheckDigit = "999989989"; 
     do { 
      $CheckDigit = unpack('%16S*', pack('S9', unpack('AAAAAAAAA', $CheckDigit))); 
     } while ($CheckDigit > 9); 
    }, 
    modulo => sub { 
     my $CheckDigit = "999989989"; 
     $CheckDigit = ($CheckDigit+0) && ($CheckDigit % 9 || 9); 
    }, 
}); 

產地:

    Rate perl_only list_util  re unpack_star split modulo 
perl_only  89882/s  --  -15%  -30%  -45%  -54%  -97% 
list_util 105601/s  17%  --  -17%  -35%  -45%  -97% 
re   127656/s  42%  21%  --  -21%  -34%  -96% 
unpack_star 162308/s  81%  54%  27%   --  -16%  -95% 
split  193405/s  115%  83%  52%   19%  --  -94% 
modulo  3055254/s  3299%  2793% 2293%  1782% 1480%  -- 

所以split看起來像你最好的打賭,如果你不得不分裂串變成字符。

但重複總結數字是almost the same as taking the number mod 9(正如mirod指出的那樣)。不同之處在於$Digits % 9產生0而不是9.修正了($Digits-1) % 9 + 1的一個公式,但是對於全零情況(它產生9而不是0)不起作用(但至少在Perl中)。一個適用於Perl的表達式是($Digits+0) && ($Digits % 9 || 9)。第一項處理完全爲零的情況,第二項爲正常情況,第三項爲0至9.

+0

好吧,相當具有說服力,謝謝! – 2011-06-15 09:51:17

+0

看來,模相當不錯的任務;看看我必須執行的操作: _「...如果結果(數字總和)小於9那麼它是校驗數字,如果它是9,校驗數字是0;如果結果大於9重複...「_ 爲什麼他們沒有首先要求模數,我不知道 – 2011-06-15 13:26:00

+0

@Marco,這不是你實現的算法,如果校驗位不是9,那麼計算就是'$ Digits%9 '。我不知道他們爲什麼不這樣說。 – cjm 2011-06-15 19:56:52

相關問題