]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: faster Int.Binomial(n, k) for k > n/2
authorRobert Griesemer <gri@golang.org>
Wed, 1 Apr 2015 18:49:12 +0000 (11:49 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 1 Apr 2015 19:35:35 +0000 (19:35 +0000)
benchmark             old ns/op     new ns/op     delta
BenchmarkBinomial     478664        4410          -99.08%

Fixes #10084.

Change-Id: Ib75034428e32c79c9a660ae9f9bd396afc6a7f11
Reviewed-on: https://go-review.googlesource.com/8351
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/int.go
src/math/big/int_test.go

index 0695d78973728dcf8d66dfff85c667de4765108c..3410ec47295864a1ffe60b5601fed6b72c2a1d65 100644 (file)
@@ -184,6 +184,10 @@ func (z *Int) MulRange(a, b int64) *Int {
 
 // Binomial sets z to the binomial coefficient of (n, k) and returns z.
 func (z *Int) Binomial(n, k int64) *Int {
+       // reduce the number of multiplications by reducing k
+       if n/2 < k && k <= n {
+               k = n - k // Binomial(n, k) == Binomial(n, n-k)
+       }
        var a, b Int
        a.MulRange(n-k+1, n)
        b.MulRange(1, k)
index 058dd96292cdde4e2a4512e6ee222042824dc8bf..a972a7249bbec4024688f31a1cd2aeb73802e6bf 100644 (file)
@@ -219,6 +219,45 @@ func TestMulRangeZ(t *testing.T) {
        }
 }
 
+func TestBinomial(t *testing.T) {
+       var z Int
+       for _, test := range []struct {
+               n, k int64
+               want string
+       }{
+               {0, 0, "1"},
+               {0, 1, "0"},
+               {1, 0, "1"},
+               {1, 1, "1"},
+               {1, 10, "0"},
+               {4, 0, "1"},
+               {4, 1, "4"},
+               {4, 2, "6"},
+               {4, 3, "4"},
+               {4, 4, "1"},
+               {10, 1, "10"},
+               {10, 9, "10"},
+               {10, 5, "252"},
+               {11, 5, "462"},
+               {11, 6, "462"},
+               {100, 10, "17310309456440"},
+               {100, 90, "17310309456440"},
+               {1000, 10, "263409560461970212832400"},
+               {1000, 990, "263409560461970212832400"},
+       } {
+               if got := z.Binomial(test.n, test.k).String(); got != test.want {
+                       t.Errorf("Binomial(%d, %d) = %s; want %s", test.n, test.k, got, test.want)
+               }
+       }
+}
+
+func BenchmarkBinomial(b *testing.B) {
+       var z Int
+       for i := b.N - 1; i >= 0; i-- {
+               z.Binomial(1000, 990)
+       }
+}
+
 // Examples from the Go Language Spec, section "Arithmetic operators"
 var divisionSignsTests = []struct {
        x, y int64