diff --git a/choose/LICENSE b/choose/LICENSE new file mode 100644 index 0000000..8f9ca75 --- /dev/null +++ b/choose/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Aaron Cannon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/choose/README.md b/choose/README.md new file mode 100644 index 0000000..6abe380 --- /dev/null +++ b/choose/README.md @@ -0,0 +1,12 @@ +# N choose K for Go/Golang + +[![Go Report Card](https://goreportcard.com/badge/github.com/golang-standards/project-layout?style=flat-square)](https://goreportcard.com/report/github.com/cannona/choose) +[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/cannona/choose) +[![Release](https://img.shields.io/github/release/golang-standards/project-layout.svg?style=flat-square)](https://github.com/cannona/choose/releases/latest) + +package choose implements the N choose K formula, or the binomial coefficient formula. See https://en.wikipedia.org/wiki/Binomial_coefficient for more. + +### Installation + + go get github.com/cannona/choose + diff --git a/choose/choose.go b/choose/choose.go new file mode 100644 index 0000000..cd51420 --- /dev/null +++ b/choose/choose.go @@ -0,0 +1,32 @@ +// Copyright 2018, Aaron Cannon under the BSD license. +// See the license file for details. + +// Package choose implements the N choose K formula, or the binomial coefficient +// formula. See https://en.wikipedia.org/wiki/Binomial_coefficient for more. +package choose + +// Choose calculates n choose k. Overflows are not detected, and Choose panics +// if n >= k >= 0 is violated. +func Choose(n, k int64) int64 { + if k > n { + panic("Choose: k > n") + } + if k < 0 { + panic("Choose: k < 0") + } + if n <= 1 || k == 0 || n == k { + return 1 + } + if newK := n - k; newK < k { + k = newK + } + if k == 1 { + return n + } + // Our return value, and this allows us to skip the first iteration. + ret := int64(n - k + 1) + for i, j := ret+1, int64(2); j <= k; i, j = i+1, j+1 { + ret = ret * i / j + } + return ret +} diff --git a/choose/choose_test.go b/choose/choose_test.go new file mode 100644 index 0000000..18dfb6d --- /dev/null +++ b/choose/choose_test.go @@ -0,0 +1,49 @@ +package choose + +import "testing" + +func didPanic(f func()) (ret bool) { + defer func() { + if r := recover(); r != nil { + ret = true + } + }() + f() + return +} + +func TestChoose(t *testing.T) { + // Let's use Pascal's triangle for the values. + row := append([]int64(nil), 1) + for n := int64(0); n < 62; n++ { + for k, v := range row { + if x := Choose(n, int64(k)); x != v { + t.Fatalf("%v choose %v returned %v, not %v", n, k, x, v) + } + } + newRow := make([]int64, len(row)+1) + newRow[0] = 1 + for i := 1; i < len(row); i++ { + newRow[i] = row[i-1] + row[i] + } + newRow[len(row)] = 1 + row = newRow + } + // Ensure it panics when it should. + if !didPanic(func() { + Choose(3, 4) + }) { + t.Fatal("Choose did not panic when k > n.") + } + if !didPanic(func() { + Choose(3, -1) + }) { + t.Fatal("Choose did not panic when k < 0") + } +} + +func BenchmarkChoose(b *testing.B) { + for i := 0; i < b.N; i++ { + Choose(61, 30) + } +}