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