├── REQUIRE ├── test ├── runtests.jl ├── youngdiagrams.jl ├── numbers.jl ├── factorials.jl ├── partitions.jl ├── combinations.jl └── permutations.jl ├── .travis.yml ├── src ├── Combinatorics.jl ├── factorials.jl ├── numbers.jl ├── youngdiagrams.jl ├── permutations.jl ├── combinations.jl └── partitions.jl ├── LICENSE.md └── README.md /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.5- 2 | Polynomials 3 | Iterators 4 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | include("numbers.jl") 5 | include("factorials.jl") 6 | include("combinations.jl") 7 | include("permutations.jl") 8 | include("partitions.jl") 9 | include("youngdiagrams.jl") 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: julia 2 | os: 3 | - linux 4 | julia: 5 | - 0.4 6 | - nightly 7 | sudo: false 8 | notifications: 9 | email: false 10 | script: 11 | - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi 12 | - julia -e 'Pkg.clone(pwd())' 13 | - julia -e 'Pkg.test("Combinatorics", coverage=true)' 14 | after_success: 15 | - julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("Combinatorics")); using Coverage; Coveralls.submit(Coveralls.process_folder()); Codecov.submit(process_folder())' 16 | 17 | -------------------------------------------------------------------------------- /test/youngdiagrams.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | @test ([5,4,2,2]\[2,1]) == ([5, 4, 2, 2],[2, 1]) 5 | 6 | @test leglength([5,4,2,2], [2,1]) == leglength(([5, 4, 2, 2],[2, 1])) == 3 7 | @test leglength([1], [1]) == -1 8 | @test leglength(Int[], Int[]) == -1 9 | 10 | @test isrimhook([4,3,2], [2,2,2]) 11 | @test !isrimhook([4,3,2], [2,2,1]) 12 | @test !isrimhook([4,3,2], [1,1]) 13 | 14 | let λ = [5,4,2,1] 15 | @test partitionsequence(λ) == [1, 0, 1, 0, 1, 1, 0, 1, 0] 16 | @test character(λ, [4,3,2,2,1]) == 0 17 | end 18 | @test character([1], [1]) == 1 19 | 20 | -------------------------------------------------------------------------------- /src/Combinatorics.jl: -------------------------------------------------------------------------------- 1 | module Combinatorics 2 | 3 | using Compat, Polynomials, Iterators 4 | 5 | import Base: start, next, done, length, eltype 6 | 7 | #These 8 functions were removed from Julia 0.5 as part of JuliaLang/julia#13897, 8 | #so check if it's necessary to import them to overload the stub methods left in 9 | #Base. 10 | if isdefined(Base, :combinations) 11 | import Base: combinations, partitions, prevprod, levicivita, nthperm, 12 | nthperm!, parity, permutations 13 | end 14 | 15 | include("numbers.jl") 16 | include("factorials.jl") 17 | include("combinations.jl") 18 | include("permutations.jl") 19 | include("partitions.jl") 20 | include("youngdiagrams.jl") 21 | 22 | end #module 23 | -------------------------------------------------------------------------------- /test/numbers.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | # catalan 5 | @test catalannum(5) == 42 6 | @test catalannum(30) == parse(BigInt,"3814986502092304") 7 | @test_throws DomainError catalannum(-1) 8 | 9 | # fibonacci 10 | @test fibonaccinum(5) == 5 11 | @test fibonaccinum(101) == parse(BigInt,"573147844013817084101") 12 | @test_throws DomainError fibonaccinum(-1) 13 | 14 | # lassalle 15 | @test lassallenum(14) == parse(BigInt,"270316008395632253340") 16 | 17 | # legendresymbol 18 | @test legendresymbol(1001,9907) == jacobisymbol(1001,9907) == -1 19 | 20 | # lucas 21 | @test lucasnum(10) == 123 22 | @test lucasnum(100) == parse(BigInt,"792070839848372253127") 23 | @test_throws DomainError lucasnum(-1) 24 | 25 | # stirlings1 26 | @test sum([abs(stirlings1(8, i)) for i = 0:8]) == factorial(8) 27 | 28 | # bell 29 | @test bellnum(7) == 877 30 | @test bellnum(42) == parse(BigInt,"35742549198872617291353508656626642567") 31 | @test_throws DomainError bellnum(-1) 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/factorials.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | @test factorial(7,3) == 7*6*5*4 5 | @test_throws DomainError factorial(3,7) 6 | @test_throws DomainError factorial(-3,-7) 7 | @test_throws DomainError factorial(-7,-3) 8 | #JuliaLang/julia#9943 9 | @test factorial(big(100), (80)) == 1303995018204712451095685346159820800000 10 | #JuliaLang/julia#9950 11 | @test_throws OverflowError factorial(1000,80) 12 | 13 | # derangement 14 | @test derangement(4) == subfactorial(4) == 9 15 | @test derangement(24) == parse(BigInt,"228250211305338670494289") 16 | 17 | # doublefactorial 18 | @test doublefactorial(70) == parse(BigInt,"355044260642859198243475901411974413130137600000000") 19 | @test_throws DomainError doublefactorial(-1) 20 | 21 | # hyperfactorial 22 | @test hyperfactorial(8) == parse(BigInt,"55696437941726556979200000") 23 | 24 | # multifactorial 25 | @test multifactorial(40,2) == doublefactorial(40) 26 | @test_throws DomainError multifactorial(-1,1) 27 | 28 | # multinomial 29 | @test multinomial(1,4,4,2) == 34650 30 | 31 | # primorial 32 | @test primorial(17) == 510510 33 | @test_throws DomainError primorial(-1) 34 | 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Combinatorics.jl is licensed under the MIT License: 2 | 3 | > Copyright (c) 2013-2015: Alessandro Andrioni, Jiahao Chen and other 4 | > contributors. 5 | > 6 | > Permission is hereby granted, free of charge, to any person obtaining 7 | > a copy of this software and associated documentation files (the 8 | > "Software"), to deal in the Software without restriction, including 9 | > without limitation the rights to use, copy, modify, merge, publish, 10 | > distribute, sublicense, and/or sell copies of the Software, and to 11 | > permit persons to whom the Software is furnished to do so, subject to 12 | > the following conditions: 13 | > 14 | > The above copyright notice and this permission notice shall be 15 | > included in all copies or substantial portions of the Software. 16 | > 17 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | > NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | > LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | > OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | > WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /test/partitions.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | @test collect(partitions(4)) == Any[[4], [3,1], [2,2], [2,1,1], [1,1,1,1]] 5 | @test collect(partitions(8,3)) == Any[[6,1,1], [5,2,1], [4,3,1], [4,2,2], [3,3,2]] 6 | @test collect(partitions(8, 1)) == Any[[8]] 7 | @test collect(partitions(8, 9)) == [] 8 | @test collect(partitions([1,2,3])) == Any[Any[[1,2,3]], Any[[1,2],[3]], Any[[1,3],[2]], Any[[1],[2,3]], Any[[1],[2],[3]]] 9 | @test collect(partitions([1,2,3,4],3)) == Any[Any[[1,2],[3],[4]], Any[[1,3],[2],[4]], Any[[1],[2,3],[4]], 10 | Any[[1,4],[2],[3]], Any[[1],[2,4],[3]], Any[[1],[2],[3,4]]] 11 | @test collect(partitions([1,2,3,4],1)) == Any[Any[[1, 2, 3, 4]]] 12 | @test collect(partitions([1,2,3,4],5)) == [] 13 | 14 | @test length(partitions(0)) == 1 15 | @test length(partitions(-1)) == 0 16 | @test length(collect(partitions(30))) == length(partitions(30)) 17 | @test length(collect(partitions(90,4))) == length(partitions(90,4)) 18 | @test length(collect(partitions('a':'h'))) == length(partitions('a':'h')) 19 | @test length(collect(partitions('a':'h',5))) == length(partitions('a':'h',5)) 20 | 21 | # integer_partitions 22 | @test integer_partitions(0) == [] 23 | @test integer_partitions(5) == Any[[1, 1, 1, 1, 1], [2, 1, 1, 1], [2, 2, 1], [3, 1, 1], [3, 2], [4, 1], [5]] 24 | @test_throws DomainError integer_partitions(-1) 25 | 26 | @test_throws ArgumentError prevprod([2,3,5],Int128(typemax(Int))+1) 27 | @test prevprod([2,3,5],30) == 30 28 | @test prevprod([2,3,5],33) == 32 29 | 30 | # noncrossing partitions 31 | let nc4 = ncpartitions(4) 32 | @test nc4 == Any[Any[[1],[2],[3],[4]], Any[[1],[2],[3,4]], Any[[1],[2,3],[4]], Any[[1],[2,4],[3]], Any[[1],[2,3,4]], 33 | Any[[1,2],[3],[4]], Any[[1,2],[3,4]], 34 | Any[[1,3],[2],[4]], Any[[1,4],[2],[3]], Any[[1,4],[2,3]], 35 | Any[[1,2,3],[4]], Any[[1,3,4],[2]], Any[[1,2,4],[3]], 36 | Any[[1,2,3,4]]] 37 | @test length(nc4) == catalannum(4) 38 | end 39 | -------------------------------------------------------------------------------- /src/factorials.jl: -------------------------------------------------------------------------------- 1 | #Factorials and elementary coefficients 2 | 3 | export 4 | derangement, 5 | factorial, 6 | subfactorial, 7 | doublefactorial, 8 | hyperfactorial, 9 | multifactorial, 10 | gamma, 11 | primorial, 12 | multinomial 13 | 14 | import Base: factorial 15 | 16 | "computes n!/k!" 17 | function factorial{T<:Integer}(n::T, k::T) 18 | if k < 0 || n < 0 || k > n 19 | throw(DomainError()) 20 | end 21 | f = one(T) 22 | while n > k 23 | f = Base.checked_mul(f,n) 24 | n -= 1 25 | end 26 | return f 27 | end 28 | factorial(n::Integer, k::Integer) = factorial(promote(n, k)...) 29 | 30 | 31 | "The number of permutations of n with no fixed points (subfactorial)" 32 | function derangement(sn::Integer) 33 | n = BigInt(sn) 34 | return num(factorial(n)*sum([(-1)^k//factorial(k) for k=0:n])) 35 | end 36 | subfactorial(n::Integer) = derangement(n) 37 | 38 | function doublefactorial(n::Integer) 39 | if n < 0 40 | throw(DomainError()) 41 | end 42 | z = BigInt() 43 | ccall((:__gmpz_2fac_ui, :libgmp), Void, 44 | (Ptr{BigInt}, UInt), &z, UInt(n)) 45 | return z 46 | end 47 | 48 | # Hyperfactorial 49 | hyperfactorial(n::Integer) = prod([i^i for i = BigInt(2):n]) 50 | 51 | function multifactorial(n::Integer, m::Integer) 52 | if n < 0 53 | throw(DomainError()) 54 | end 55 | z = BigInt() 56 | ccall((:__gmpz_mfac_uiui, :libgmp), Void, 57 | (Ptr{BigInt}, UInt, UInt), &z, UInt(n), UInt(m)) 58 | return z 59 | end 60 | 61 | function primorial(n::Integer) 62 | if n < 0 63 | throw(DomainError()) 64 | end 65 | z = BigInt() 66 | ccall((:__gmpz_primorial_ui, :libgmp), Void, 67 | (Ptr{BigInt}, UInt), &z, UInt(n)) 68 | return z 69 | end 70 | 71 | "Multinomial coefficient where n = sum(k)" 72 | function multinomial(k...) 73 | s = 0 74 | result = 1 75 | @inbounds for i in k 76 | s += i 77 | result *= binomial(s, i) 78 | end 79 | result 80 | end 81 | 82 | 83 | -------------------------------------------------------------------------------- /test/combinations.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | @test collect(combinations([])) == [] 5 | @test collect(combinations(['a', 'b', 'c'])) == Any[['a'],['b'],['c'],['a','b'],['a','c'],['b','c'],['a','b','c']] 6 | 7 | @test collect(combinations("abc",3)) == Any[['a','b','c']] 8 | @test collect(combinations("abc",2)) == Any[['a','b'],['a','c'],['b','c']] 9 | @test collect(combinations("abc",1)) == Any[['a'],['b'],['c']] 10 | @test collect(combinations("abc",0)) == Any[[]] 11 | @test collect(combinations("abc",-1)) == [] 12 | 13 | @test collect(filter(x->(iseven(x[1])),combinations([1,2,3],2))) == Any[[2,3]] 14 | 15 | # multiset_combinations 16 | @test collect(multiset_combinations("aabc", 5)) == Any[] 17 | @test collect(multiset_combinations("aabc", 2)) == Any[['a','a'],['a','b'],['a','c'],['b','c']] 18 | @test collect(multiset_combinations("aabc", 1)) == Any[['a'],['b'],['c']] 19 | @test collect(multiset_combinations("aabc", 0)) == Any[Char[]] 20 | @test collect(multiset_combinations("aabc", -1)) == Any[] 21 | @test collect(multiset_combinations("", 1)) == Any[] 22 | @test collect(multiset_combinations("", 0)) == Any[Char[]] 23 | @test collect(multiset_combinations("", -1)) == Any[] 24 | 25 | # with_replacement_combinations 26 | @test collect(with_replacement_combinations("abc", 2)) == Any[['a','a'],['a','b'],['a','c'], 27 | ['b','b'],['b','c'],['c','c']] 28 | @test collect(with_replacement_combinations("abc", 1)) == Any[['a'],['b'],['c']] 29 | @test collect(with_replacement_combinations("abc", 0)) == Any[Char[]] 30 | @test collect(with_replacement_combinations("abc", -1)) == Any[] 31 | @test collect(with_replacement_combinations("", 1)) == Any[] 32 | @test collect(with_replacement_combinations("", 0)) == Any[Char[]] 33 | @test collect(with_replacement_combinations("", -1)) == Any[] 34 | 35 | 36 | #cool-lex iterator 37 | @test_throws DomainError collect(CoolLexCombinations(-1, 1)) 38 | @test_throws DomainError collect(CoolLexCombinations(5, 0)) 39 | @test collect(CoolLexCombinations(4,2)) == Vector[[1,2], [2,3], [1,3], [2,4], [3,4], [1,4]] 40 | @test isa(start(CoolLexCombinations(1000, 20)), Combinatorics.CoolLexIterState{BigInt}) 41 | -------------------------------------------------------------------------------- /src/numbers.jl: -------------------------------------------------------------------------------- 1 | #Special named numbers and symbols 2 | 3 | export bellnum, 4 | catalannum, 5 | fibonaccinum, 6 | jacobisymbol, 7 | lassallenum, 8 | legendresymbol, 9 | lucasnum, 10 | stirlings1 11 | 12 | "Returns the n-th Bell number" 13 | function bellnum(bn::Integer) 14 | if bn < 0 15 | throw(DomainError()) 16 | else 17 | n = BigInt(bn) 18 | end 19 | list = Array(BigInt, div(n*(n+1), 2)) 20 | list[1] = 1 21 | for i = 2:n 22 | beg = div(i*(i-1),2) 23 | list[beg+1] = list[beg] 24 | for j = 2:i 25 | list[beg+j] = list[beg+j-1]+list[beg+j-i] 26 | end 27 | end 28 | return list[end] 29 | end 30 | 31 | "Returns the n-th Catalan number" 32 | function catalannum(bn::Integer) 33 | if bn<0 34 | throw(DomainError()) 35 | else 36 | n = BigInt(bn) 37 | end 38 | div(binomial(2*n, n), (n + 1)) 39 | end 40 | 41 | function fibonaccinum(n::Integer) 42 | if n < 0 43 | throw(DomainError()) 44 | end 45 | z = BigInt() 46 | ccall((:__gmpz_fib_ui, :libgmp), Void, 47 | (Ptr{BigInt}, UInt), &z, UInt(n)) 48 | return z 49 | end 50 | 51 | 52 | function jacobisymbol(a::Integer, b::Integer) 53 | ba = BigInt(a) 54 | bb = BigInt(b) 55 | return ccall((:__gmpz_jacobi, :libgmp), Cint, 56 | (Ptr{BigInt}, Ptr{BigInt}), &ba, &bb) 57 | end 58 | 59 | """ 60 | Computes Lassalle's sequence 61 | OEIS entry A180874 62 | """ 63 | function lassallenum(m::Integer) 64 | A = ones(BigInt,m) 65 | for n=2:m 66 | A[n]=(-1)^(n-1) * (catalannum(n) + sum([(-1)^j*binomial(2n-1, 2j-1)*A[j]*catalannum(n-j) for j=1:n-1])) 67 | end 68 | A[m] 69 | end 70 | 71 | function legendresymbol(a::Integer, b::Integer) 72 | ba = BigInt(a) 73 | bb = BigInt(b) 74 | return ccall((:__gmpz_legendre, :libgmp), Cint, 75 | (Ptr{BigInt}, Ptr{BigInt}), &ba, &bb) 76 | end 77 | 78 | function lucasnum(n::Integer) 79 | if n < 0 80 | throw(DomainError()) 81 | end 82 | z = BigInt() 83 | ccall((:__gmpz_lucnum_ui, :libgmp), Void, 84 | (Ptr{BigInt}, UInt), &z, UInt(n)) 85 | return z 86 | end 87 | 88 | "Returns s(n, k), the signed Stirling number of first kind" 89 | function stirlings1(n::Integer, k::Integer) 90 | p = poly(0:(n-1)) 91 | p[n - k + 1] 92 | end 93 | 94 | -------------------------------------------------------------------------------- /test/permutations.jl: -------------------------------------------------------------------------------- 1 | using Combinatorics 2 | using Base.Test 3 | 4 | # permutations 5 | @test collect(permutations("abc")) == Any[['a','b','c'],['a','c','b'],['b','a','c'], 6 | ['b','c','a'],['c','a','b'],['c','b','a']] 7 | 8 | @test collect(filter(x->(iseven(x[1])),permutations([1,2,3]))) == Any[[2,1,3],[2,3,1]] 9 | @test collect(filter(x->(iseven(x[3])),permutations([1,2,3]))) == Any[[1,3,2],[3,1,2]] 10 | 11 | @test length(permutations(0)) == 1 12 | 13 | @test collect(permutations("abc", 4)) == Any[] 14 | @test collect(permutations("abc", 2)) == Any[['a','b'],['a','c'],['b','a'], 15 | ['b','c'],['c','a'],['c','b']] 16 | @test collect(permutations("abc", 0)) == Any[Char[]] 17 | @test collect(permutations("abc", -1)) == Any[] 18 | @test collect(permutations("", 1)) == Any[] 19 | @test collect(permutations("", 0)) == Any[Char[]] 20 | @test collect(permutations("", -1)) == Any[] 21 | 22 | # multiset_permutations 23 | @test collect(multiset_permutations("aabc", 5)) == Any[] 24 | @test collect(multiset_permutations("aabc", 2)) == Any[['a','a'],['a','b'], ['a','c'],['b','a'], 25 | ['b','c'],['c','a'],['c','b']] 26 | @test collect(multiset_permutations("aabc", 0)) == Any[Char[]] 27 | @test collect(multiset_permutations("aabc", -1)) == Any[] 28 | @test collect(multiset_permutations("", 1)) == Any[] 29 | @test collect(multiset_permutations("", 0)) == Any[Char[]] 30 | @test collect(multiset_permutations("", -1)) == Any[] 31 | @test length(multiset_permutations("aaaaaaaaaaaaaaaaaaaaab", 21)) == 22 32 | 33 | #nthperm! 34 | for n = 0:7, k = 1:factorial(n) 35 | p = nthperm!([1:n;], k) 36 | @test isperm(p) 37 | @test nthperm(p) == k 38 | end 39 | #Euler Problem #24 40 | @test nthperm!([0:9;],1000000) == [2,7,8,3,9,1,5,4,6,0] 41 | 42 | @test nthperm([0,1,2],3) == [1,0,2] 43 | 44 | @test_throws ArgumentError parity([0]) 45 | @test_throws ArgumentError parity([1,2,3,3]) 46 | @test levicivita([1,1,2,3]) == 0 47 | @test levicivita([1]) == 1 && parity([1]) == 0 48 | @test map(levicivita, collect(permutations([1,2,3]))) == [1, -1, -1, 1, 1, -1] 49 | @test let p = [3, 4, 6, 10, 5, 2, 1, 7, 8, 9]; levicivita(p) == 1 && parity(p) == 0; end 50 | @test let p = [4, 3, 6, 10, 5, 2, 1, 7, 8, 9]; levicivita(p) == -1 && parity(p) == 1; end 51 | 52 | @test Combinatorics.nsetpartitions(-1) == 0 53 | @test collect(permutations([])) == Vector[[]] 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Combinatorics 2 | 3 | [![Combinatorics](http://pkg.julialang.org/badges/Combinatorics_0.4.svg)](http://pkg.julialang.org/?pkg=Combinatorics&ver=0.4) 4 | [![Build Status](https://travis-ci.org/JuliaLang/Combinatorics.jl.svg?branch=master)](https://travis-ci.org/JuliaLang/Combinatorics.jl) 5 | [![Coverage Status](https://coveralls.io/repos/JuliaLang/Combinatorics.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/JuliaLang/Combinatorics.jl?branch=master) 6 | [![codecov.io](https://codecov.io/github/JuliaLang/Combinatorics.jl/coverage.svg?branch=master)](https://codecov.io/github/JuliaLang/Combinatorics.jl?branch=master) 7 | 8 | A combinatorics library for Julia, focusing mostly (as of now) on enumerative 9 | combinatorics and permutations. As overflows are expected even for low values, 10 | most of the functions always return `BigInt`, and are marked as such below. 11 | 12 | This library provides the following functions: 13 | - `bellnum(n)`: returns the n-th Bell number; always returns a `BigInt`; 14 | - `catalannum(n)`: returns the n-th Catalan number; always returns a `BigInt`; 15 | - `combinations(a)`: returns combinations of all order by chaining calls to `Base.combinations(a,n); 16 | - `derangement(n)`/`subfactorial(n)`: returns the number of permutations of n with no fixed points; always returns a `BigInt`; 17 | - `doublefactorial(n)`: returns the double factorial n!!; always returns a `BigInt`; 18 | - `fibonacci(n)`: the n-th Fibonacci number; always returns a `BigInt`; 19 | - `hyperfactorial(n)`: the n-th hyperfactorial, i.e. prod([i^i for i = 2:n]; always returns a `BigInt`; 20 | - `integer_partitions(n)`: returns a `Vector{Int}` consisting of the partitions of the number `n`. 21 | - `jacobisymbol(a,b)`: returns the Jacobi symbol (a/b); 22 | - `lassallenum(n)`: returns the nth Lassalle number An defined in [arXiv:1009.4225](http://arxiv.org/abs/1009.4225) ([OEIS A180874](http://oeis.org/A180874)); always returns a `BigInt`; 23 | - `legendresymbol(a,p)`: returns the Legendre symbol (a/p); 24 | - `lucasnum(n)`: the n-th Lucas number; always returns a `BigInt`; 25 | - `multifactorial(n)`: returns the m-multifactorial n(!^m); always returns a `BigInt`; 26 | - `multinomial(k...)`: receives a tuple of `k_1, ..., k_n` and calculates the multinomial coefficient `(n k)`, where `n = sum(k)`; returns a `BigInt` only if given a `BigInt`; 27 | - `primorial(n)`: returns the product of all positive prime numbers <= n; always returns a `BigInt`; 28 | - `stirlings1(n, k)`: the signed `(n,k)`-th Stirling number of the first kind; returns a `BigInt` only if given a `BigInt`. 29 | 30 | Young diagrams 31 | -------------- 32 | Limited support for working with Young diagrams is provided. 33 | 34 | - `partitionsequence(a)`: computes partition sequence for an integer partition `a` 35 | - `x = a \ b` creates the skew diagram for partitions (tuples) `a`, `b` 36 | - `isrimhook(x)`: checks if skew diagram `x` is a rim hook 37 | - `leglength(x)`: computes leg length of rim hook `x` 38 | - `character(a, b)`: computes character the partition `b` in the `a`th irrep of Sn 39 | -------------------------------------------------------------------------------- /src/youngdiagrams.jl: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Young diagrams, partitions of unity and characters of the symmetric group Sn # 3 | ################################################################################ 4 | 5 | typealias Partition Vector{Int} 6 | typealias YoungDiagram Array{Int,2} 7 | typealias SkewDiagram Tuple{Partition, Partition} 8 | 9 | export Partition, 10 | YoungDiagram, #represents shape of Young diagram 11 | SkewDiagram, #skew diagrams 12 | partitionsequence, 13 | isrimhook, #Check if skew diagram is rim hook 14 | leglength, 15 | character #Computes character of irrep of Sn 16 | 17 | import Base.\ 18 | 19 | ################# 20 | # Skew diagrams # 21 | ################# 22 | 23 | "This uses a very simple internal representation for skew diagrams" 24 | \(λ::Partition, μ::Partition) = MakeSkewDiagram(λ, μ) 25 | function MakeSkewDiagram(λ::Partition, μ::Partition) 26 | m, n = length(λ), length(μ) 27 | if n>m error("Cannot construct skew diagram") end 28 | (λ, μ) 29 | end 30 | 31 | "Checks if skew diagram is a rim hook" 32 | isrimhook(λ::Partition, μ::Partition)=isrimhook(λ \ μ) 33 | function isrimhook(ξ::SkewDiagram) 34 | λ, μ = ξ 35 | m, n = length(λ), length(μ) 36 | if n>m error("Cannot construct skew diagram") end 37 | #Construct matrix representation of diagram 38 | #XXX This is a horribly inefficient way of checking condition 1! 39 | l = maximum(λ) 40 | youngdiagram=zeros(Int, m, l) 41 | for i=1:n 42 | youngdiagram[i, μ[i]+1:λ[i]]=1 43 | end 44 | for i=n+1:m 45 | youngdiagram[i, 1:λ[i]]=1 46 | end 47 | #Condition 1. Must be edgewise connected 48 | youngdiagramList=[] 49 | for i=1:m 50 | for j=1:l 51 | if youngdiagram[i, j]==1 youngdiagramList = [youngdiagramList; (i, j)] end 52 | end 53 | end 54 | for k=1:length(youngdiagramList) 55 | i, j = youngdiagramList[k] 56 | numNeighbors = 0 57 | for kp=1:length(youngdiagramList) 58 | ip,jp= youngdiagramList[kp] 59 | if abs(i-ip) + abs(j-jp) == 1 numNeighbors += 1 end 60 | end 61 | if numNeighbors == 0 return false end #Found a cell with no adjacent neighbors 62 | end 63 | #Condition 2. Must not contain 2x2 square of cells 64 | for i=1:m-1 65 | for j=1:l-1 66 | if youngdiagram[i, j]== 0 continue end 67 | if youngdiagram[i, j+1] == youngdiagram[i+1, j] == youngdiagram[i+1, j+1] == 1 68 | return false 69 | end 70 | end 71 | end 72 | return true 73 | end 74 | 75 | 76 | "Strictly speaking, defined for rim hook only, but here we define it for all skew diagrams" 77 | leglength(λ::Partition, μ::Partition)=leglength((λ \ μ)) 78 | function leglength(ξ::SkewDiagram) 79 | λ, μ = ξ 80 | m, n = length(λ), length(μ) 81 | #Construct matrix representation of diagram 82 | if m==0 return -1 end 83 | l = maximum(λ) 84 | youngdiagram=zeros(Int, m, l) 85 | for i=1:n 86 | youngdiagram[i, μ[i]+1:λ[i]]=1 87 | end 88 | for i=n+1:m 89 | youngdiagram[i, 1:λ[i]]=1 90 | end 91 | for i=m:-1:1 92 | if any([x==1 for x in youngdiagram[i,:]]) return i-1 end 93 | end 94 | return -1 #If entire matrix is empty 95 | end 96 | 97 | 98 | ####################### 99 | # partition sequences # 100 | ####################### 101 | 102 | "Computes essential part of the partition sequence of lambda" 103 | function partitionsequence(lambda::Partition) 104 | Λ▔ = Int[] 105 | λ = [lambda; 0] 106 | m = length(lambda) 107 | for i=m:-1:1 108 | for k=1:(λ[i]-λ[i+1]) 109 | Λ▔ = [Λ▔; 1] 110 | end 111 | Λ▔ = [Λ▔ ; 0] 112 | end 113 | Λ▔ 114 | end 115 | 116 | "Takes two elements of a partition sequence, with a to the left of b" 117 | isrimhook(a::Int, b::Int) = (a==1) && (b==0) 118 | 119 | 120 | ############################# 121 | # Character of irreps of Sn # 122 | ############################# 123 | 124 | "Computes recursively using the Murnaghan-Nakayama rule." 125 | function MN1inner(R::Vector{Int}, T::Dict, μ::Partition, t::Integer) 126 | s=length(R) 127 | χ::Integer=1 128 | if t<=length(μ) 129 | χ, σ::Integer = 0, 1 130 | for j=1:μ[t]-1 131 | if R[j]==0 σ=-σ end 132 | end 133 | for i=1:s-μ[t] 134 | if R[i] != R[i+μ[t]-1] σ=-σ end 135 | if isrimhook(R[i], R[i+μ[t]]) 136 | R[i], R[i+μ[t]] = R[i+μ[t]], R[i] 137 | rhohat = R[i:i+μ[t]] 138 | if !haskey(T, rhohat) #Cache result in lookup table 139 | T[rhohat] = MN1inner(R, T, μ, t+1) 140 | end 141 | χ += σ * T[rhohat] 142 | R[i], R[i+μ[t]] = R[i+μ[t]], R[i] 143 | end 144 | end 145 | end 146 | χ 147 | end 148 | 149 | doc""" 150 | Computes character $χ^λ(μ)$ of the partition μ in the λth irrep of the 151 | symmetric group $S_n$ 152 | 153 | Implements the Murnaghan-Nakayama algorithm as described in: 154 | Dan Bernstein, 155 | "The computational complexity of rules for the character table of Sn", 156 | Journal of Symbolic Computation, vol. 37 iss. 6 (2004), pp 727-748. 157 | doi:10.1016/j.jsc.2003.11.001 158 | """ 159 | function character(λ::Partition, μ::Partition) 160 | T = Dict{Any,Int}(()=>0) #Sparse array implemented as dict 161 | Λ▔ = partitionsequence(λ) 162 | MN1inner(Λ▔, T, μ, 1) 163 | end 164 | -------------------------------------------------------------------------------- /src/permutations.jl: -------------------------------------------------------------------------------- 1 | #Permutations 2 | 3 | export 4 | levicivita, 5 | multiset_permutations, 6 | nthperm!, 7 | nthperm, 8 | parity, 9 | permutations 10 | 11 | 12 | immutable Permutations{T} 13 | a::T 14 | t::Int 15 | end 16 | 17 | eltype{T}(::Type{Permutations{T}}) = Vector{eltype(T)} 18 | 19 | length(p::Permutations) = (0 <= p.t <= length(p.a))?factorial(length(p.a), length(p.a)-p.t):0 20 | 21 | """ 22 | Generate all permutations of an indexable object. Because the number of permutations can be very large, this function returns an iterator object. Use `collect(permutations(array))` to get an array of all permutations. 23 | """ 24 | permutations(a) = Permutations(a, length(a)) 25 | 26 | """ 27 | Generate all size t permutations of an indexable object. 28 | """ 29 | function permutations(a, t::Integer) 30 | if t < 0 31 | t = length(a) + 1 32 | end 33 | Permutations(a, t) 34 | end 35 | 36 | start(p::Permutations) = [1:length(p.a);] 37 | next(p::Permutations, s) = nextpermutation(p.a, p.t ,s) 38 | 39 | function nextpermutation(m, t, s) 40 | perm = [m[s[i]] for i in 1:t] 41 | n = length(s) 42 | if t <= 0 43 | return(perm, [n+1]) 44 | end 45 | s = copy(s) 46 | if t < n 47 | j = t + 1 48 | while j <= n && s[t] >= s[j]; j+=1; end 49 | end 50 | if t < n && j <= n 51 | s[t], s[j] = s[j], s[t] 52 | else 53 | if t < n 54 | reverse!(s, t+1) 55 | end 56 | i = t - 1 57 | while i>=1 && s[i] >= s[i+1]; i -= 1; end 58 | if i > 0 59 | j = n 60 | while j>i && s[i] >= s[j]; j -= 1; end 61 | s[i], s[j] = s[j], s[i] 62 | reverse!(s, i+1) 63 | else 64 | s[1] = n+1 65 | end 66 | end 67 | return(perm, s) 68 | end 69 | 70 | done(p::Permutations, s) = !isempty(s) && max(s[1], p.t) > length(p.a) || (isempty(s) && p.t > 0) 71 | 72 | immutable MultiSetPermutations{T} 73 | m::T 74 | f::Vector{Int} 75 | t::Int 76 | ref::Vector{Int} 77 | end 78 | 79 | eltype{T}(::Type{MultiSetPermutations{T}}) = Vector{eltype(T)} 80 | 81 | function length(c::MultiSetPermutations) 82 | t = c.t 83 | if t > length(c.ref) 84 | return 0 85 | end 86 | if t > 20 87 | g = [factorial(big(i)) for i in 0:t] 88 | else 89 | g = [factorial(i) for i in 0:t] 90 | end 91 | p = [g[t+1]; zeros(Float64,t)] 92 | for i in 1:length(c.f) 93 | f = c.f[i] 94 | if i == 1 95 | for j in 1:min(f, t) 96 | p[j+1] = g[t+1]/g[j+1] 97 | end 98 | else 99 | for j in t:-1:1 100 | q = 0 101 | for k in (j+1):-1:max(1,j+1-f) 102 | q += p[k]/g[j+2-k] 103 | end 104 | p[j+1] = q 105 | end 106 | end 107 | end 108 | return round(Int, p[t+1]) 109 | end 110 | 111 | "generate all permutations of size t from an array a with possibly duplicated elements." 112 | function multiset_permutations{T<:Integer}(m, f::Vector{T}, t::Integer) 113 | length(m) == length(f) || error("Lengths of m and f are not the same.") 114 | ref = length(f) > 0 ? vcat([[i for j in 1:f[i] ] for i in 1:length(f)]...) : Int[] 115 | if t < 0 116 | t = length(ref) + 1 117 | end 118 | MultiSetPermutations(m, f, t, ref) 119 | end 120 | 121 | function multiset_permutations{T}(a::T, t::Integer) 122 | m = unique(collect(a)) 123 | f = [sum([c == x for c in a]) for x in m] 124 | multiset_permutations(m, f, t) 125 | end 126 | 127 | start(p::MultiSetPermutations) = p.ref 128 | next(p::MultiSetPermutations, s) = nextpermutation(p.m, p.t, s) 129 | done(p::MultiSetPermutations, s) = 130 | !isempty(s) && max(s[1], p.t) > length(p.ref) || (isempty(s) && p.t > 0) 131 | 132 | 133 | 134 | "In-place version of nthperm." 135 | function nthperm!(a::AbstractVector, k::Integer) 136 | k -= 1 # make k 1-indexed 137 | k < 0 && throw(ArgumentError("permutation k must be ≥ 0, got $k")) 138 | n = length(a) 139 | n == 0 && return a 140 | f = factorial(oftype(k, n-1)) 141 | for i=1:n-1 142 | j = div(k, f) + 1 143 | k = k % f 144 | f = div(f, n-i) 145 | 146 | j = j+i-1 147 | elt = a[j] 148 | for d = j:-1:i+1 149 | a[d] = a[d-1] 150 | end 151 | a[i] = elt 152 | end 153 | a 154 | end 155 | 156 | "Compute the kth lexicographic permutation of the vector a." 157 | nthperm(a::AbstractVector, k::Integer) = nthperm!(copy(a),k) 158 | 159 | "Return the `k` that generated permutation `p`. Note that `nthperm(nthperm([1:n], k)) == k` for `1 <= k <= factorial(n)`." 160 | function nthperm{T<:Integer}(p::AbstractVector{T}) 161 | isperm(p) || throw(ArgumentError("argument is not a permutation")) 162 | k, n = 1, length(p) 163 | for i = 1:n-1 164 | f = factorial(n-i) 165 | for j = i+1:n 166 | k += ifelse(p[j] < p[i], f, 0) 167 | end 168 | end 169 | return k 170 | end 171 | 172 | 173 | # Parity of permutations 174 | 175 | const levicivita_lut = cat(3, [0 0 0; 0 0 1; 0 -1 0], 176 | [0 0 -1; 0 0 0; 1 0 0], 177 | [0 1 0; -1 0 0; 0 0 0]) 178 | 179 | """ 180 | Levi-Civita symbol of a permutation. 181 | 182 | Returns 1 is the permutation is even, -1 if it is odd and 0 otherwise. 183 | 184 | The parity is computed by using the fact that a permutation is odd if and 185 | only if the number of even-length cycles is odd. 186 | """ 187 | function levicivita{T<:Integer}(p::AbstractVector{T}) 188 | n = length(p) 189 | 190 | if n == 3 191 | @inbounds valid = (0 < p[1] <= 3) * (0 < p[2] <= 3) * (0 < p[3] <= 3) 192 | return valid ? levicivita_lut[p[1], p[2], p[3]] : 0 193 | end 194 | 195 | todo = trues(n) 196 | first = 1 197 | cycles = flips = 0 198 | 199 | while cycles + flips < n 200 | first = findnext(todo, first) 201 | (todo[first] $= true) && return 0 202 | j = p[first] 203 | (0 < j <= n) || return 0 204 | cycles += 1 205 | while j ≠ first 206 | (todo[j] $= true) && return 0 207 | j = p[j] 208 | (0 < j <= n) || return 0 209 | flips += 1 210 | end 211 | end 212 | 213 | return iseven(flips) ? 1 : -1 214 | end 215 | 216 | """ 217 | Computes the parity of a permutation using the levicivita function, 218 | so you can ask iseven(parity(p)). If p is not a permutation throws an error. 219 | """ 220 | function parity{T<:Integer}(p::AbstractVector{T}) 221 | epsilon = levicivita(p) 222 | epsilon == 0 && throw(ArgumentError("Not a permutation")) 223 | epsilon == 1 ? 0 : 1 224 | end 225 | -------------------------------------------------------------------------------- /src/combinations.jl: -------------------------------------------------------------------------------- 1 | export combinations, 2 | CoolLexCombinations, 3 | multiset_combinations, 4 | with_replacement_combinations 5 | 6 | #The Combinations iterator 7 | 8 | immutable Combinations{T} 9 | a::T 10 | t::Int 11 | end 12 | 13 | start(c::Combinations) = [1:c.t;] 14 | function next(c::Combinations, s) 15 | comb = [c.a[si] for si in s] 16 | if c.t == 0 17 | # special case to generate 1 result for t==0 18 | return (comb,[length(c.a)+2]) 19 | end 20 | s = copy(s) 21 | for i = length(s):-1:1 22 | s[i] += 1 23 | if s[i] > (length(c.a) - (length(s)-i)) 24 | continue 25 | end 26 | for j = i+1:endof(s) 27 | s[j] = s[j-1]+1 28 | end 29 | break 30 | end 31 | (comb,s) 32 | end 33 | done(c::Combinations, s) = !isempty(s) && s[1] > length(c.a)-c.t+1 34 | 35 | length(c::Combinations) = binomial(length(c.a),c.t) 36 | 37 | eltype{T}(::Type{Combinations{T}}) = Vector{eltype(T)} 38 | 39 | "Generate all combinations of `n` elements from an indexable object. Because the number of combinations can be very large, this function returns an iterator object. Use `collect(combinations(array,n))` to get an array of all combinations. 40 | " 41 | function combinations(a, t::Integer) 42 | if t < 0 43 | # generate 0 combinations for negative argument 44 | t = length(a)+1 45 | end 46 | Combinations(a, t) 47 | end 48 | 49 | 50 | """ 51 | generate combinations of all orders, chaining of order iterators is eager, 52 | but sequence at each order is lazy 53 | """ 54 | combinations(a) = chain([combinations(a,k) for k=1:length(a)]...) 55 | 56 | 57 | 58 | # cool-lex combinations iterator 59 | 60 | """ 61 | Produces (n,k)-combinations in cool-lex order 62 | 63 | Implements the cool-lex algorithm to generate (n,k)-combinations 64 | @article{Ruskey:2009fk, 65 | Author = {Frank Ruskey and Aaron Williams}, 66 | Doi = {10.1016/j.disc.2007.11.048}, 67 | Journal = {Discrete Mathematics}, 68 | Month = {September}, 69 | Number = {17}, 70 | Pages = {5305-5320}, 71 | Title = {The coolest way to generate combinations}, 72 | Url = {http://www.sciencedirect.com/science/article/pii/S0012365X07009570}, 73 | Volume = {309}, 74 | Year = {2009}} 75 | """ 76 | immutable CoolLexCombinations 77 | n :: Int 78 | t :: Int 79 | end 80 | 81 | immutable CoolLexIterState{T<:Integer} 82 | R0:: T 83 | R1:: T 84 | R2:: T 85 | R3:: T 86 | end 87 | 88 | function start(C::CoolLexCombinations) 89 | if C.n < 0 90 | throw(DomainError()) 91 | end 92 | if C.t ≤ 0 93 | throw(DomainError()) 94 | end 95 | 96 | #What integer size should I use? 97 | if C.n < 8sizeof(Int) 98 | T = Int 99 | else 100 | T = BigInt 101 | end 102 | 103 | CoolLexIterState{T}(0, 0, T(1) << C.n, (T(1) << C.t) - 1) 104 | end 105 | 106 | function next(C::CoolLexCombinations, S::CoolLexIterState) 107 | R0 = S.R0 108 | R1 = S.R1 109 | R2 = S.R2 110 | R3 = S.R3 111 | 112 | R0 = R3 & (R3 + 1) 113 | R1 = R0 $ (R0 - 1) 114 | R0 = R1 + 1 115 | R1 &= R3 116 | R0 = max((R0 & R3) - 1, 0) 117 | R3 += R1 - R0 118 | 119 | _cool_lex_visit(S.R3), CoolLexIterState(R0, R1, R2, R3) 120 | end 121 | 122 | #Converts an integer bit pattern X into a subset 123 | #If X & 2^k == 1, then k is in the subset 124 | function _cool_lex_visit(X::Int) 125 | subset = Int[] 126 | n=1 127 | while X != 0 128 | if X & 1 == 1 push!(subset, n) end 129 | X >>= 1 130 | n += 1 131 | end 132 | subset 133 | end 134 | 135 | done(C::CoolLexCombinations, S::CoolLexIterState) = (S.R3 & S.R2 != 0) 136 | 137 | length(C::CoolLexCombinations) = max(0, binomial(C.n, C.t)) 138 | 139 | 140 | immutable MultiSetCombinations{T} 141 | m::T 142 | f::Vector{Int} 143 | t::Int 144 | ref::Vector{Int} 145 | end 146 | 147 | eltype{T}(::Type{MultiSetCombinations{T}}) = Vector{eltype(T)} 148 | 149 | function length(c::MultiSetCombinations) 150 | t = c.t 151 | if t > length(c.ref) 152 | return 0 153 | end 154 | p = [1; zeros(Int,t)] 155 | for i in 1:length(c.f) 156 | f = c.f[i] 157 | if i == 1 158 | for j in 1:min(f, t) 159 | p[j+1] = 1 160 | end 161 | else 162 | for j in t:-1:1 163 | p[j+1] = sum(p[max(1,j+1-f):(j+1)]) 164 | end 165 | end 166 | end 167 | return p[t+1] 168 | end 169 | 170 | function multiset_combinations{T<:Integer}(m, f::Vector{T}, t::Integer) 171 | length(m) == length(f) || error("Lengths of m and f are not the same.") 172 | ref = length(f) > 0 ? vcat([[i for j in 1:f[i] ] for i in 1:length(f)]...) : Int[] 173 | if t < 0 174 | t = length(ref) + 1 175 | end 176 | MultiSetCombinations(m, f, t, ref) 177 | end 178 | 179 | "generate all combinations of size t from an array a with possibly duplicated elements." 180 | function multiset_combinations{T}(a::T, t::Integer) 181 | m = unique(collect(a)) 182 | f = Int[sum([c == x for c in a]) for x in m] 183 | multiset_combinations(m, f, t) 184 | end 185 | 186 | start(c::MultiSetCombinations) = c.ref 187 | function next(c::MultiSetCombinations, s) 188 | ref = c.ref 189 | n = length(ref) 190 | t = c.t 191 | changed = false 192 | comb = [c.m[s[i]] for i in 1:t] 193 | if t > 0 194 | s = copy(s) 195 | for i in t:-1:1 196 | if s[i] < ref[i + (n - t)] 197 | j = 1 198 | while ref[j] <= s[i]; j += 1; end 199 | s[i] = ref[j] 200 | for l in (i+1):t 201 | s[l] = ref[j+=1] 202 | end 203 | changed = true 204 | break 205 | end 206 | end 207 | !changed && (s[1] = n+1) 208 | else 209 | s = [n+1] 210 | end 211 | (comb, s) 212 | end 213 | done(c::MultiSetCombinations, s) = 214 | (!isempty(s) && max(s[1], c.t) > length(c.ref)) || (isempty(s) && c.t > 0) 215 | 216 | immutable WithReplacementCombinations{T} 217 | a::T 218 | t::Int 219 | end 220 | 221 | eltype{T}(::Type{WithReplacementCombinations{T}}) = Vector{eltype(T)} 222 | 223 | length(c::WithReplacementCombinations) = binomial(length(c.a)+c.t-1, c.t) 224 | 225 | "generate all combinations with replacement of size t from an array a." 226 | with_replacement_combinations(a, t::Integer) = WithReplacementCombinations(a, t) 227 | 228 | start(c::WithReplacementCombinations) = [1 for i in 1:c.t] 229 | function next(c::WithReplacementCombinations, s) 230 | n = length(c.a) 231 | t = c.t 232 | comb = [c.a[si] for si in s] 233 | if t > 0 234 | s = copy(s) 235 | changed = false 236 | for i in t:-1:1 237 | if s[i] < n 238 | s[i] += 1 239 | for j in (i+1):t 240 | s[j] = s[i] 241 | end 242 | changed = true 243 | break 244 | end 245 | end 246 | !changed && (s[1] = n+1) 247 | else 248 | s = [n+1] 249 | end 250 | (comb, s) 251 | end 252 | done(c::WithReplacementCombinations, s) = !isempty(s) && s[1] > length(c.a) || c.t < 0 253 | -------------------------------------------------------------------------------- /src/partitions.jl: -------------------------------------------------------------------------------- 1 | #Partitions 2 | 3 | export 4 | integer_partitions, 5 | ncpartitions, 6 | partitions, 7 | prevprod 8 | #nextprod, 9 | 10 | 11 | #integer partitions 12 | 13 | immutable IntegerPartitions 14 | n::Int 15 | end 16 | 17 | length(p::IntegerPartitions) = npartitions(p.n) 18 | 19 | start(p::IntegerPartitions) = Int[] 20 | done(p::IntegerPartitions, xs) = length(xs) == p.n 21 | next(p::IntegerPartitions, xs) = (xs = nextpartition(p.n,xs); (xs,xs)) 22 | 23 | """ 24 | Generate all integer arrays that sum to `n`. Because the number of partitions can be very large, this function returns an iterator object. Use `collect(partitions(n))` to get an array of all partitions. The number of partitions to generate can be efficiently computed using `length(partitions(n))`. 25 | """ 26 | partitions(n::Integer) = IntegerPartitions(n) 27 | 28 | 29 | 30 | function nextpartition(n, as) 31 | if isempty(as); return Int[n]; end 32 | 33 | xs = similar(as,0) 34 | sizehint!(xs,length(as)+1) 35 | 36 | for i = 1:length(as)-1 37 | if as[i+1] == 1 38 | x = as[i]-1 39 | push!(xs, x) 40 | n -= x 41 | while n > x 42 | push!(xs, x) 43 | n -= x 44 | end 45 | push!(xs, n) 46 | 47 | return xs 48 | end 49 | push!(xs, as[i]) 50 | n -= as[i] 51 | end 52 | push!(xs, as[end]-1) 53 | push!(xs, 1) 54 | 55 | xs 56 | end 57 | 58 | let _npartitions = Dict{Int,Int}() 59 | global npartitions 60 | function npartitions(n::Int) 61 | if n < 0 62 | 0 63 | elseif n < 2 64 | 1 65 | elseif (np = get(_npartitions, n, 0)) > 0 66 | np 67 | else 68 | np = 0 69 | sgn = 1 70 | for k = 1:n 71 | np += sgn * (npartitions(n-k*(3k-1)>>1) + npartitions(n-k*(3k+1)>>1)) 72 | sgn = -sgn 73 | end 74 | _npartitions[n] = np 75 | end 76 | end 77 | end 78 | 79 | # Algorithm H from TAoCP 7.2.1.4 80 | # Partition n into m parts 81 | # in colex order (lexicographic by reflected sequence) 82 | 83 | immutable FixedPartitions 84 | n::Int 85 | m::Int 86 | end 87 | 88 | length(f::FixedPartitions) = npartitions(f.n,f.m) 89 | 90 | """ 91 | Generate all arrays of `m` integers that sum to `n`. Because the number of partitions can be very large, this function returns an iterator object. Use `collect(partitions(n,m))` to get an array of all partitions. The number of partitions to generate can be efficiently computed using `length(partitions(n,m))`. 92 | """ 93 | partitions(n::Integer, m::Integer) = n >= 1 && m >= 1 ? FixedPartitions(n,m) : throw(DomainError()) 94 | 95 | start(f::FixedPartitions) = Int[] 96 | function done(f::FixedPartitions, s::Vector{Int}) 97 | f.m <= f.n || return true 98 | isempty(s) && return false 99 | return f.m == 1 || s[1]-1 <= s[end] 100 | end 101 | next(f::FixedPartitions, s::Vector{Int}) = (xs = nextfixedpartition(f.n,f.m,s); (xs,xs)) 102 | 103 | function nextfixedpartition(n, m, bs) 104 | as = copy(bs) 105 | if isempty(as) 106 | # First iteration 107 | as = [n-m+1; ones(Int, m-1)] 108 | elseif as[2] < as[1]-1 109 | # Most common iteration 110 | as[1] -= 1 111 | as[2] += 1 112 | else 113 | # Iterate 114 | local j 115 | s = as[1]+as[2]-1 116 | for j = 3:m 117 | if as[j] < as[1]-1; break; end 118 | s += as[j] 119 | end 120 | x = as[j] += 1 121 | for k = j-1:-1:2 122 | as[k] = x 123 | s -= x 124 | end 125 | as[1] = s 126 | end 127 | 128 | return as 129 | end 130 | 131 | let _nipartitions = Dict{Tuple{Int,Int},Int}() 132 | global npartitions 133 | function npartitions(n::Int,m::Int) 134 | if n < m || m == 0 135 | 0 136 | elseif n == m 137 | 1 138 | elseif (np = get(_nipartitions, (n,m), 0)) > 0 139 | np 140 | else 141 | _nipartitions[(n,m)] = npartitions(n-1,m-1) + npartitions(n-m,m) 142 | end 143 | end 144 | end 145 | 146 | # Algorithm H from TAoCP 7.2.1.5 147 | # Set partitions 148 | 149 | immutable SetPartitions{T<:AbstractVector} 150 | s::T 151 | end 152 | 153 | length(p::SetPartitions) = nsetpartitions(length(p.s)) 154 | 155 | """ 156 | Generate all set partitions of the elements of an array, represented as arrays of arrays. Because the number of partitions can be very large, this function returns an iterator object. Use `collect(partitions(array))` to get an array of all partitions. The number of partitions to generate can be efficiently computed using `length(partitions(array))`. 157 | """ 158 | partitions(s::AbstractVector) = SetPartitions(s) 159 | 160 | start(p::SetPartitions) = (n = length(p.s); (zeros(Int32, n), ones(Int32, n-1), n, 1)) 161 | done(p::SetPartitions, s) = s[1][1] > 0 162 | next(p::SetPartitions, s) = nextsetpartition(p.s, s...) 163 | 164 | function nextsetpartition(s::AbstractVector, a, b, n, m) 165 | function makeparts(s, a, m) 166 | temp = [ similar(s,0) for k = 0:m ] 167 | for i = 1:n 168 | push!(temp[a[i]+1], s[i]) 169 | end 170 | filter!(x->!isempty(x), temp) 171 | end 172 | 173 | if isempty(s); return ([s], ([1], Int[], n, 1)); end 174 | 175 | part = makeparts(s,a,m) 176 | 177 | if a[end] != m 178 | a[end] += 1 179 | else 180 | local j 181 | for j = n-1:-1:1 182 | if a[j] != b[j] 183 | break 184 | end 185 | end 186 | a[j] += 1 187 | m = b[j] + (a[j] == b[j]) 188 | for k = j+1:n-1 189 | a[k] = 0 190 | b[k] = m 191 | end 192 | a[end] = 0 193 | end 194 | 195 | return (part, (a,b,n,m)) 196 | 197 | end 198 | 199 | let _nsetpartitions = Dict{Int,Int}() 200 | global nsetpartitions 201 | function nsetpartitions(n::Int) 202 | if n < 0 203 | 0 204 | elseif n < 2 205 | 1 206 | elseif (wn = get(_nsetpartitions, n, 0)) > 0 207 | wn 208 | else 209 | wn = 0 210 | for k = 0:n-1 211 | wn += binomial(n-1,k)*nsetpartitions(n-1-k) 212 | end 213 | _nsetpartitions[n] = wn 214 | end 215 | end 216 | end 217 | 218 | immutable FixedSetPartitions{T<:AbstractVector} 219 | s::T 220 | m::Int 221 | end 222 | 223 | length(p::FixedSetPartitions) = nfixedsetpartitions(length(p.s),p.m) 224 | 225 | """ 226 | Generate all set partitions of the elements of an array into exactly m subsets, represented as arrays of arrays. Because the number of partitions can be very large, this function returns an iterator object. Use `collect(partitions(array,m))` to get an array of all partitions. The number of partitions into m subsets is equal to the Stirling number of the second kind and can be efficiently computed using `length(partitions(array,m))`. 227 | """ 228 | partitions(s::AbstractVector,m::Int) = length(s) >= 1 && m >= 1 ? FixedSetPartitions(s,m) : throw(DomainError()) 229 | 230 | function start(p::FixedSetPartitions) 231 | n = length(p.s) 232 | m = p.m 233 | m <= n ? (vcat(ones(Int, n-m),1:m), vcat(1,n-m+2:n), n) : (Int[], Int[], n) 234 | end 235 | # state consists of: 236 | # vector a of length n describing to which partition every element of s belongs 237 | # vector b of length n describing the first index b[i] that belongs to partition i 238 | # integer n 239 | 240 | done(p::FixedSetPartitions, s) = isempty(s[1]) || s[1][1] > 1 241 | next(p::FixedSetPartitions, s) = nextfixedsetpartition(p.s,p.m, s...) 242 | 243 | function nextfixedsetpartition(s::AbstractVector, m, a, b, n) 244 | function makeparts(s, a) 245 | part = [ similar(s,0) for k = 1:m ] 246 | for i = 1:n 247 | push!(part[a[i]], s[i]) 248 | end 249 | return part 250 | end 251 | 252 | part = makeparts(s,a) 253 | 254 | if m == 1 255 | a[1] = 2 256 | return (part, (a, b, n)) 257 | end 258 | 259 | if a[end] != m 260 | a[end] += 1 261 | else 262 | local j, k 263 | for j = n-1:-1:1 264 | if a[j]1 269 | a[j]+=1 270 | for p=j+1:n 271 | if b[a[p]]!=p 272 | a[p]=1 273 | end 274 | end 275 | else 276 | for k=m:-1:2 277 | if b[k-1]= x 306 | #for integer n1, n2, n3 307 | #""" 308 | #function nextprod(a::Vector{Int}, x) 309 | # if x > typemax(Int) 310 | # throw(ArgumentError("unsafe for x > typemax(Int), got $x")) 311 | # end 312 | # k = length(a) 313 | # v = ones(Int, k) # current value of each counter 314 | # mx = [nextpow(ai,x) for ai in a] # maximum value of each counter 315 | # v[1] = mx[1] # start at first case that is >= x 316 | # p::widen(Int) = mx[1] # initial value of product in this case 317 | # best = p 318 | # icarry = 1 319 | # 320 | # while v[end] < mx[end] 321 | # if p >= x 322 | # best = p < best ? p : best # keep the best found yet 323 | # carrytest = true 324 | # while carrytest 325 | # p = div(p, v[icarry]) 326 | # v[icarry] = 1 327 | # icarry += 1 328 | # p *= a[icarry] 329 | # v[icarry] *= a[icarry] 330 | # carrytest = v[icarry] > mx[icarry] && icarry < k 331 | # end 332 | # if p < x 333 | # icarry = 1 334 | # end 335 | # else 336 | # while p < x 337 | # p *= a[1] 338 | # v[1] *= a[1] 339 | # end 340 | # end 341 | # end 342 | # best = mx[end] < best ? mx[end] : best 343 | # return Int(best) # could overflow, but best to have predictable return type 344 | #end 345 | 346 | doc""" 347 | Previous integer not greater than `n` that can be written as $\prod k_i^{p_i}$ for integers $p_1$, $p_2$, etc. 348 | 349 | For a list of integers i1, i2, i3, find the largest 350 | i1^n1 * i2^n2 * i3^n3 <= x 351 | for integer n1, n2, n3 352 | """ 353 | function prevprod(a::Vector{Int}, x) 354 | if x > typemax(Int) 355 | throw(ArgumentError("unsafe for x > typemax(Int), got $x")) 356 | end 357 | k = length(a) 358 | v = ones(Int, k) # current value of each counter 359 | mx = [nextpow(ai,x) for ai in a] # allow each counter to exceed p (sentinel) 360 | first = Int(prevpow(a[1], x)) # start at best case in first factor 361 | v[1] = first 362 | p::widen(Int) = first 363 | best = p 364 | icarry = 1 365 | 366 | while v[end] < mx[end] 367 | while p <= x 368 | best = p > best ? p : best 369 | p *= a[1] 370 | v[1] *= a[1] 371 | end 372 | if p > x 373 | carrytest = true 374 | while carrytest 375 | p = div(p, v[icarry]) 376 | v[icarry] = 1 377 | icarry += 1 378 | p *= a[icarry] 379 | v[icarry] *= a[icarry] 380 | carrytest = v[icarry] > mx[icarry] && icarry < k 381 | end 382 | if p <= x 383 | icarry = 1 384 | end 385 | end 386 | end 387 | best = x >= p > best ? p : best 388 | return Int(best) 389 | end 390 | 391 | 392 | "Lists the partitions of the number n, the order is consistent with GAP" 393 | function integer_partitions(n::Integer) 394 | if n < 0 395 | throw(DomainError()) 396 | elseif n == 0 397 | return Vector{Int}[] 398 | elseif n == 1 399 | return Vector{Int}[[1]] 400 | end 401 | 402 | list = Vector{Int}[] 403 | 404 | for p in integer_partitions(n-1) 405 | push!(list, [p; 1]) 406 | if length(p) == 1 || p[end] < p[end-1] 407 | push!(list, [p[1:end-1]; p[end]+1]) 408 | end 409 | end 410 | 411 | list 412 | end 413 | 414 | 415 | 416 | #Noncrossing partitions 417 | 418 | #Produces noncrossing partitions of length n 419 | function ncpartitions(n::Int) 420 | partitions = Vector{Vector{Int}}[] 421 | _ncpart!(1,n,n,Vector{Int}[], partitions) 422 | partitions 423 | end 424 | 425 | function _ncpart!(a::Int, b::Int, nn::Int, 426 | x::Vector, partitions::Vector) 427 | 428 | n=b-a+1 429 | for k=1:n, root in CoolLexCombinations(n, k) 430 | root += a-1 431 | #Abort if construction is out of lex order 432 | if !isempty(x) && lexcmp(x[end], root)==1 return end 433 | 434 | #Save if we've filled all the holes 435 | sofar = Vector{Int}[x..., root] 436 | ssofaru = sort(union(sofar...)) 437 | if length(ssofaru)==nn && ssofaru==collect(1:nn) 438 | push!(partitions, sofar) 439 | return 440 | end 441 | 442 | #otherwise patch all remaining holes 443 | blob = [ssofaru; nn+1] 444 | for l=1:length(blob)-1 445 | ap, bp = blob[l]+1, blob[l+1]-1 446 | if ap <= bp _ncpart!(ap, bp, nn, sofar, partitions) end 447 | end 448 | end 449 | end 450 | 451 | --------------------------------------------------------------------------------