gonum.org/v1/gonum@v0.14.0/stat/combin/combin.go (about) 1 // Copyright ©2016 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package combin 6 7 import ( 8 "math" 9 "sort" 10 ) 11 12 const ( 13 errNegInput = "combin: negative input" 14 badSetSize = "combin: n < k" 15 badInput = "combin: wrong input slice length" 16 errNonpositiveDimension = "combin: non-positive dimension" 17 ) 18 19 // Binomial returns the binomial coefficient of (n,k), also commonly referred to 20 // as "n choose k". 21 // 22 // The binomial coefficient, C(n,k), is the number of unordered combinations of 23 // k elements in a set that is n elements big, and is defined as 24 // 25 // C(n,k) = n!/((n-k)!k!) 26 // 27 // n and k must be non-negative with n >= k, otherwise Binomial will panic. 28 // No check is made for overflow. 29 func Binomial(n, k int) int { 30 if n < 0 || k < 0 { 31 panic(errNegInput) 32 } 33 if n < k { 34 panic(badSetSize) 35 } 36 // (n,k) = (n, n-k) 37 if k > n/2 { 38 k = n - k 39 } 40 b := 1 41 for i := 1; i <= k; i++ { 42 b = (n - k + i) * b / i 43 } 44 return b 45 } 46 47 // GeneralizedBinomial returns the generalized binomial coefficient of (n, k), 48 // defined as 49 // 50 // Γ(n+1) / (Γ(k+1) Γ(n-k+1)) 51 // 52 // where Γ is the Gamma function. GeneralizedBinomial is useful for continuous 53 // relaxations of the binomial coefficient, or when the binomial coefficient value 54 // may overflow int. In the latter case, one may use math/big for an exact 55 // computation. 56 // 57 // n and k must be non-negative with n >= k, otherwise GeneralizedBinomial will panic. 58 func GeneralizedBinomial(n, k float64) float64 { 59 return math.Exp(LogGeneralizedBinomial(n, k)) 60 } 61 62 // LogGeneralizedBinomial returns the log of the generalized binomial coefficient. 63 // See GeneralizedBinomial for more information. 64 func LogGeneralizedBinomial(n, k float64) float64 { 65 if n < 0 || k < 0 { 66 panic(errNegInput) 67 } 68 if n < k { 69 panic(badSetSize) 70 } 71 a, _ := math.Lgamma(n + 1) 72 b, _ := math.Lgamma(k + 1) 73 c, _ := math.Lgamma(n - k + 1) 74 return a - b - c 75 } 76 77 // CombinationGenerator generates combinations iteratively. The Combinations 78 // function may be called to generate all combinations collectively. 79 type CombinationGenerator struct { 80 n int 81 k int 82 previous []int 83 remaining int 84 } 85 86 // NewCombinationGenerator returns a CombinationGenerator for generating the 87 // combinations of k elements from a set of size n. 88 // 89 // n and k must be non-negative with n >= k, otherwise NewCombinationGenerator 90 // will panic. 91 func NewCombinationGenerator(n, k int) *CombinationGenerator { 92 return &CombinationGenerator{ 93 n: n, 94 k: k, 95 remaining: Binomial(n, k), 96 } 97 } 98 99 // Next advances the iterator if there are combinations remaining to be generated, 100 // and returns false if all combinations have been generated. Next must be called 101 // to initialize the first value before calling Combination or Combination will 102 // panic. The value returned by Combination is only changed during calls to Next. 103 func (c *CombinationGenerator) Next() bool { 104 if c.remaining <= 0 { 105 // Next is called before combination, so c.remaining is set to zero before 106 // Combination is called. Thus, Combination cannot panic on zero, and a 107 // second sentinel value is needed. 108 c.remaining = -1 109 return false 110 } 111 if c.previous == nil { 112 c.previous = make([]int, c.k) 113 for i := range c.previous { 114 c.previous[i] = i 115 } 116 } else { 117 nextCombination(c.previous, c.n, c.k) 118 } 119 c.remaining-- 120 return true 121 } 122 123 // Combination returns the current combination. If dst is non-nil, it must have 124 // length k and the result will be stored in-place into dst. If dst 125 // is nil a new slice will be allocated and returned. If all of the combinations 126 // have already been constructed (Next() returns false), Combination will panic. 127 // 128 // Next must be called to initialize the first value before calling Combination 129 // or Combination will panic. The value returned by Combination is only changed 130 // during calls to Next. 131 func (c *CombinationGenerator) Combination(dst []int) []int { 132 if c.remaining == -1 { 133 panic("combin: all combinations have been generated") 134 } 135 if c.previous == nil { 136 panic("combin: Combination called before Next") 137 } 138 if dst == nil { 139 dst = make([]int, c.k) 140 } else if len(dst) != c.k { 141 panic(badInput) 142 } 143 copy(dst, c.previous) 144 return dst 145 } 146 147 // Combinations generates all of the combinations of k elements from a 148 // set of size n. The returned slice has length Binomial(n,k) and each inner slice 149 // has length k. 150 // 151 // n and k must be non-negative with n >= k, otherwise Combinations will panic. 152 // 153 // CombinationGenerator may alternatively be used to generate the combinations 154 // iteratively instead of collectively, or IndexToCombination for random access. 155 func Combinations(n, k int) [][]int { 156 combins := Binomial(n, k) 157 data := make([][]int, combins) 158 if len(data) == 0 { 159 return data 160 } 161 data[0] = make([]int, k) 162 for i := range data[0] { 163 data[0][i] = i 164 } 165 for i := 1; i < combins; i++ { 166 next := make([]int, k) 167 copy(next, data[i-1]) 168 nextCombination(next, n, k) 169 data[i] = next 170 } 171 return data 172 } 173 174 // nextCombination generates the combination after s, overwriting the input value. 175 func nextCombination(s []int, n, k int) { 176 for j := k - 1; j >= 0; j-- { 177 if s[j] == n+j-k { 178 continue 179 } 180 s[j]++ 181 for l := j + 1; l < k; l++ { 182 s[l] = s[j] + l - j 183 } 184 break 185 } 186 } 187 188 // CombinationIndex returns the index of the given combination. 189 // 190 // The functions CombinationIndex and IndexToCombination define a bijection 191 // between the integers and the Binomial(n, k) number of possible combinations. 192 // CombinationIndex returns the inverse of IndexToCombination. 193 // 194 // CombinationIndex panics if comb is not a sorted combination of the first 195 // [0,n) integers, if n or k are negative, or if k is greater than n. 196 func CombinationIndex(comb []int, n, k int) int { 197 if n < 0 || k < 0 { 198 panic(errNegInput) 199 } 200 if n < k { 201 panic(badSetSize) 202 } 203 if len(comb) != k { 204 panic("combin: bad length combination") 205 } 206 if !sort.IntsAreSorted(comb) { 207 panic("combin: input combination is not sorted") 208 } 209 contains := make(map[int]struct{}, k) 210 for _, v := range comb { 211 contains[v] = struct{}{} 212 } 213 if len(contains) != k { 214 panic("combin: comb contains non-unique elements") 215 } 216 // This algorithm iterates in reverse lexicograhpic order. 217 // Flip the index and values to swap the order. 218 rev := make([]int, k) 219 for i, v := range comb { 220 rev[len(comb)-i-1] = n - v - 1 221 } 222 idx := 0 223 for i, v := range rev { 224 if v >= i+1 { 225 idx += Binomial(v, i+1) 226 } 227 } 228 return Binomial(n, k) - 1 - idx 229 } 230 231 // IndexToCombination returns the combination corresponding to the given index. 232 // 233 // The functions CombinationIndex and IndexToCombination define a bijection 234 // between the integers and the Binomial(n, k) number of possible combinations. 235 // IndexToCombination returns the inverse of CombinationIndex (up to the order 236 // of the elements). 237 // 238 // The combination is stored in-place into dst if dst is non-nil, otherwise 239 // a new slice is allocated and returned. 240 // 241 // IndexToCombination panics if n or k are negative, if k is greater than n, 242 // or if idx is not in [0, Binomial(n,k)-1]. IndexToCombination will also panic 243 // if dst is non-nil and len(dst) is not k. 244 func IndexToCombination(dst []int, idx, n, k int) []int { 245 if idx < 0 || idx >= Binomial(n, k) { 246 panic("combin: invalid index") 247 } 248 if dst == nil { 249 dst = make([]int, k) 250 } else if len(dst) != k { 251 panic(badInput) 252 } 253 // The base algorithm indexes in reverse lexicographic order 254 // flip the values and the index. 255 idx = Binomial(n, k) - 1 - idx 256 for i := range dst { 257 // Find the largest number m such that Binomial(m, k-i) <= idx. 258 // This is one less than the first number such that it is larger. 259 m := sort.Search(n, func(m int) bool { 260 if m < k-i { 261 return false 262 } 263 return Binomial(m, k-i) > idx 264 }) 265 m-- 266 // Normally this is put m into the last free spot, but we 267 // reverse the index and the value. 268 dst[i] = n - m - 1 269 if m >= k-i { 270 idx -= Binomial(m, k-i) 271 } 272 } 273 return dst 274 } 275 276 // Cartesian returns the Cartesian product of the slices in data. The Cartesian 277 // product of two sets is the set of all combinations of the items. For example, 278 // given the input 279 // 280 // []int{2, 3, 1} 281 // 282 // the returned matrix will be 283 // 284 // [ 0 0 0 ] 285 // [ 0 1 0 ] 286 // [ 0 2 0 ] 287 // [ 1 0 0 ] 288 // [ 1 1 0 ] 289 // [ 1 2 0 ] 290 // 291 // Cartesian panics if any of the provided lengths are less than 1. 292 func Cartesian(lens []int) [][]int { 293 rows := Card(lens) 294 if rows == 0 { 295 panic("combin: empty lengths") 296 } 297 out := make([][]int, rows) 298 for i := 0; i < rows; i++ { 299 out[i] = SubFor(nil, i, lens) 300 } 301 return out 302 } 303 304 // Card computes the cardinality of the multi-dimensional space whose dimensions have size specified by dims 305 // All length values must be positive, otherwise this will panic. 306 func Card(dims []int) int { 307 if len(dims) == 0 { 308 return 0 309 } 310 card := 1 311 for _, v := range dims { 312 if v < 0 { 313 panic("combin: length less than zero") 314 } 315 card *= v 316 } 317 return card 318 } 319 320 // NewCartesianGenerator returns a CartesianGenerator for iterating over Cartesian products which are generated on the fly. 321 // All values in lens must be positive, otherwise this will panic. 322 func NewCartesianGenerator(lens []int) *CartesianGenerator { 323 return &CartesianGenerator{ 324 lens: lens, 325 rows: Card(lens), 326 idx: -1, 327 } 328 } 329 330 // CartesianGenerator iterates over a Cartesian product set. 331 type CartesianGenerator struct { 332 lens []int 333 rows int 334 idx int 335 } 336 337 // Next moves to the next product of the Cartesian set. 338 // It returns false if the generator reached the end of the Cartesian set end. 339 func (g *CartesianGenerator) Next() bool { 340 if g.idx+1 < g.rows { 341 g.idx++ 342 return true 343 } 344 g.idx = g.rows 345 return false 346 } 347 348 // Product generates one product of the Cartesian set according to the current index which is increased by Next(). 349 // Next needs to be called at least one time before this method, otherwise it will panic. 350 func (g *CartesianGenerator) Product(dst []int) []int { 351 return SubFor(dst, g.idx, g.lens) 352 } 353 354 // IdxFor converts a multi-dimensional index into a linear index for a 355 // multi-dimensional space. sub specifies the index for each dimension, and dims 356 // specifies the size of each dimension. IdxFor is the inverse of SubFor. 357 // IdxFor panics if any of the entries of sub are negative, any of the entries 358 // of dim are non-positive, or if sub[i] >= dims[i] for any i. 359 func IdxFor(sub, dims []int) int { 360 // The index returned is "row-major", that is the last index of sub is 361 // continuous. 362 var idx int 363 stride := 1 364 for i := len(dims) - 1; i >= 0; i-- { 365 v := sub[i] 366 d := dims[i] 367 if d <= 0 { 368 panic(errNonpositiveDimension) 369 } 370 if v < 0 || v >= d { 371 panic("combin: invalid subscript") 372 } 373 idx += v * stride 374 stride *= d 375 } 376 return idx 377 } 378 379 // SubFor returns the multi-dimensional subscript for the input linear index to 380 // the multi-dimensional space. dims specifies the size of each dimension, and 381 // idx specifies the linear index. SubFor is the inverse of IdxFor. 382 // 383 // If sub is non-nil the result is stored in-place into sub, and SubFor will panic 384 // if len(sub) != len(dims). If sub is nil a new slice of the appropriate length 385 // is allocated. SubFor panics if idx < 0 or if idx is greater than or equal to 386 // the product of the dimensions. 387 func SubFor(sub []int, idx int, dims []int) []int { 388 if sub == nil { 389 sub = make([]int, len(dims)) 390 } 391 if len(sub) != len(dims) { 392 panic(badInput) 393 } 394 if idx < 0 { 395 panic(errNegInput) 396 } 397 stride := 1 398 for i := len(dims) - 1; i >= 1; i-- { 399 stride *= dims[i] 400 } 401 for i := 0; i < len(dims)-1; i++ { 402 v := idx / stride 403 d := dims[i] 404 if d < 0 { 405 panic(errNonpositiveDimension) 406 } 407 if v >= dims[i] { 408 panic("combin: index too large") 409 } 410 sub[i] = v 411 idx -= v * stride 412 stride /= dims[i+1] 413 } 414 if idx > dims[len(sub)-1] { 415 panic("combin: index too large") 416 } 417 sub[len(sub)-1] = idx 418 return sub 419 } 420 421 // NumPermutations returns the number of permutations when selecting k 422 // objects from a set of n objects when the selection order matters. 423 // No check is made for overflow. 424 // 425 // NumPermutations panics if either n or k is negative, or if k is 426 // greater than n. 427 func NumPermutations(n, k int) int { 428 if n < 0 { 429 panic("combin: n is negative") 430 } 431 if k < 0 { 432 panic("combin: k is negative") 433 } 434 if k > n { 435 panic("combin: k is greater than n") 436 } 437 p := 1 438 for i := n - k + 1; i <= n; i++ { 439 p *= i 440 } 441 return p 442 } 443 444 // Permutations generates all of the permutations of k elements from a 445 // set of size n. The returned slice has length NumPermutations(n, k) 446 // and each inner slice has length k. 447 // 448 // n and k must be non-negative with n >= k, otherwise Permutations will panic. 449 // 450 // PermutationGenerator may alternatively be used to generate the permutations 451 // iteratively instead of collectively, or IndexToPermutation for random access. 452 func Permutations(n, k int) [][]int { 453 nPerms := NumPermutations(n, k) 454 data := make([][]int, nPerms) 455 if len(data) == 0 { 456 return data 457 } 458 for i := 0; i < nPerms; i++ { 459 data[i] = IndexToPermutation(nil, i, n, k) 460 } 461 return data 462 } 463 464 // PermutationGenerator generates permutations iteratively. The Permutations 465 // function may be called to generate all permutations collectively. 466 type PermutationGenerator struct { 467 n int 468 k int 469 nPerm int 470 idx int 471 permutation []int 472 } 473 474 // NewPermutationGenerator returns a PermutationGenerator for generating the 475 // permutations of k elements from a set of size n. 476 // 477 // n and k must be non-negative with n >= k, otherwise NewPermutationGenerator 478 // will panic. 479 func NewPermutationGenerator(n, k int) *PermutationGenerator { 480 return &PermutationGenerator{ 481 n: n, 482 k: k, 483 nPerm: NumPermutations(n, k), 484 idx: -1, 485 permutation: make([]int, k), 486 } 487 } 488 489 // Next advances the iterator if there are permutations remaining to be generated, 490 // and returns false if all permutations have been generated. Next must be called 491 // to initialize the first value before calling Permutation or Permutation will 492 // panic. The value returned by Permutation is only changed during calls to Next. 493 func (p *PermutationGenerator) Next() bool { 494 if p.idx >= p.nPerm-1 { 495 p.idx = p.nPerm // so Permutation can panic. 496 return false 497 } 498 p.idx++ 499 IndexToPermutation(p.permutation, p.idx, p.n, p.k) 500 return true 501 } 502 503 // Permutation returns the current permutation. If dst is non-nil, it must have 504 // length k and the result will be stored in-place into dst. If dst 505 // is nil a new slice will be allocated and returned. If all of the permutations 506 // have already been constructed (Next() returns false), Permutation will panic. 507 // 508 // Next must be called to initialize the first value before calling Permutation 509 // or Permutation will panic. The value returned by Permutation is only changed 510 // during calls to Next. 511 func (p *PermutationGenerator) Permutation(dst []int) []int { 512 if p.idx == p.nPerm { 513 panic("combin: all permutations have been generated") 514 } 515 if p.idx == -1 { 516 panic("combin: Permutation called before Next") 517 } 518 if dst == nil { 519 dst = make([]int, p.k) 520 } else if len(dst) != p.k { 521 panic(badInput) 522 } 523 copy(dst, p.permutation) 524 return dst 525 } 526 527 // PermutationIndex returns the index of the given permutation. 528 // 529 // The functions PermutationIndex and IndexToPermutation define a bijection 530 // between the integers and the NumPermutations(n, k) number of possible permutations. 531 // PermutationIndex returns the inverse of IndexToPermutation. 532 // 533 // PermutationIndex panics if perm is not a permutation of k of the first 534 // [0,n) integers, if n or k are negative, or if k is greater than n. 535 func PermutationIndex(perm []int, n, k int) int { 536 if n < 0 || k < 0 { 537 panic(errNegInput) 538 } 539 if n < k { 540 panic(badSetSize) 541 } 542 if len(perm) != k { 543 panic("combin: bad length permutation") 544 } 545 contains := make(map[int]struct{}, k) 546 for _, v := range perm { 547 if v < 0 || v >= n { 548 panic("combin: bad element") 549 } 550 contains[v] = struct{}{} 551 } 552 if len(contains) != k { 553 panic("combin: perm contains non-unique elements") 554 } 555 if n == k { 556 // The permutation is the ordering of the elements. 557 return equalPermutationIndex(perm) 558 } 559 560 // The permutation index is found by finding the combination index and the 561 // equalPermutation index. The combination index is found by just sorting 562 // the elements, and the permutation index is the ordering of the size 563 // of the elements. 564 tmp := make([]int, len(perm)) 565 copy(tmp, perm) 566 idx := make([]int, len(perm)) 567 for i := range idx { 568 idx[i] = i 569 } 570 s := sortInts{tmp, idx} 571 sort.Sort(s) 572 order := make([]int, len(perm)) 573 for i, v := range idx { 574 order[v] = i 575 } 576 combIdx := CombinationIndex(tmp, n, k) 577 permIdx := equalPermutationIndex(order) 578 return combIdx*NumPermutations(k, k) + permIdx 579 } 580 581 type sortInts struct { 582 data []int 583 idx []int 584 } 585 586 func (s sortInts) Len() int { 587 return len(s.data) 588 } 589 590 func (s sortInts) Less(i, j int) bool { 591 return s.data[i] < s.data[j] 592 } 593 594 func (s sortInts) Swap(i, j int) { 595 s.data[i], s.data[j] = s.data[j], s.data[i] 596 s.idx[i], s.idx[j] = s.idx[j], s.idx[i] 597 } 598 599 // IndexToPermutation returns the permutation corresponding to the given index. 600 // 601 // The functions PermutationIndex and IndexToPermutation define a bijection 602 // between the integers and the NumPermutations(n, k) number of possible permutations. 603 // IndexToPermutation returns the inverse of PermutationIndex. 604 // 605 // The permutation is stored in-place into dst if dst is non-nil, otherwise 606 // a new slice is allocated and returned. 607 // 608 // IndexToPermutation panics if n or k are negative, if k is greater than n, 609 // or if idx is not in [0, NumPermutations(n,k)-1]. IndexToPermutation will also panic 610 // if dst is non-nil and len(dst) is not k. 611 func IndexToPermutation(dst []int, idx, n, k int) []int { 612 nPerm := NumPermutations(n, k) 613 if idx < 0 || idx >= nPerm { 614 panic("combin: invalid index") 615 } 616 if dst == nil { 617 dst = make([]int, k) 618 } else if len(dst) != k { 619 panic(badInput) 620 } 621 if n == k { 622 indexToEqualPermutation(dst, idx) 623 return dst 624 } 625 626 // First, we index into the combination (which of the k items to choose) 627 // and then we index into the n == k permutation of those k items. The 628 // indexing acts like a matrix with nComb rows and factorial(k) columns. 629 kPerm := NumPermutations(k, k) 630 combIdx := idx / kPerm 631 permIdx := idx % kPerm 632 comb := IndexToCombination(nil, combIdx, n, k) // Gives us the set of integers. 633 perm := make([]int, len(dst)) 634 indexToEqualPermutation(perm, permIdx) // Gives their order. 635 for i, v := range perm { 636 dst[i] = comb[v] 637 } 638 return dst 639 } 640 641 // equalPermutationIndex returns the index of the given permutation of the 642 // first k integers. 643 func equalPermutationIndex(perm []int) int { 644 // Note(btracey): This is an n^2 algorithm, but factorial increases 645 // very quickly (25! overflows int64) so this is not a problem in 646 // practice. 647 idx := 0 648 for i, u := range perm { 649 less := 0 650 for _, v := range perm[i:] { 651 if v < u { 652 less++ 653 } 654 } 655 idx += less * factorial(len(perm)-i-1) 656 } 657 return idx 658 } 659 660 // indexToEqualPermutation returns the permutation for the first len(dst) 661 // integers for the given index. 662 func indexToEqualPermutation(dst []int, idx int) { 663 for i := range dst { 664 dst[i] = i 665 } 666 for i := range dst { 667 f := factorial(len(dst) - i - 1) 668 r := idx / f 669 v := dst[i+r] 670 copy(dst[i+1:i+r+1], dst[i:i+r]) 671 dst[i] = v 672 idx %= f 673 } 674 } 675 676 // factorial returns a!. 677 func factorial(a int) int { 678 f := 1 679 for i := 2; i <= a; i++ { 680 f *= i 681 } 682 return f 683 }