Skip to content

Commit

Permalink
Add multiexponents(m,n) for exponents of multinomial expansion (#57)
Browse files Browse the repository at this point in the history
* Add multiexponents(m,n) for exponents of multinomial expansion

* Implement iterator interface

* Add comments

* Refactor next(m::MultiExponents, s)

* Fix indentation

* Add tests for multiexponents()

* Use immutable instead of struct because of Julia v0.5

* Add Julia v0.6 to Travis CI build

* Avoid intermediate memory allocation in multiexponents

* Refactor comments

* Add URL for stars and bars technique
  • Loading branch information
juliohm authored and andreasnoack committed Nov 22, 2017
1 parent 84fe2fd commit aaaf5f4
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 0 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ os:
- linux
julia:
- 0.5
- 0.6
- nightly
sudo: false
notifications:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This library provides the following functions:
- `lucasnum(n)`: the n-th Lucas number; always returns a `BigInt`;
- `multifactorial(n)`: returns the m-multifactorial n(!^m); always returns a `BigInt`;
- `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`;
- `multiexponents(m,n)`: returns the exponents in the multinomial expansion (x₁ + x₂ + ... + xₘ)ⁿ;
- `primorial(n)`: returns the product of all positive prime numbers <= n; always returns a `BigInt`;
- `stirlings1(n, k, signed=false)`: returns the `(n,k)`-th Stirling number of the first kind; the number is signed if `signed` is true; returns a `BigInt` only if given a `BigInt`.
- `stirlings2(n, k)`: returns the `(n,k)`-th Stirling number of the second kind; returns a `BigInt` only if given a `BigInt`.
Expand Down
1 change: 1 addition & 0 deletions src/Combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ include("factorials.jl")
include("combinations.jl")
include("permutations.jl")
include("partitions.jl")
include("multinomials.jl")
include("youngdiagrams.jl")

end #module
55 changes: 55 additions & 0 deletions src/multinomials.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Multinomial theorem
# https://en.wikipedia.org/wiki/Multinomial_theorem

export multiexponents

immutable MultiExponents{T}
c::Combinations{T}
nterms::Int
end

start(m::MultiExponents) = start(m.c)

# Standard stars and bars:
# https://en.wikipedia.org/wiki/Stars_and_bars_(combinatorics)
function next(m::MultiExponents, s)
stars, ss = next(m.c, s)

# stars minus their consecutive
# position becomes their index
result = zeros(Int, m.nterms)
for (i,s) in enumerate(stars)
result[s-i+1] += 1
end

result, ss
end

done(m::MultiExponents, s) = done(m.c, s)

length(m::MultiExponents) = length(m.c)

"""
multiexponents(m, n)
Returns the exponents in the multinomial expansion (x₁ + x₂ + ... + xₘ)ⁿ.
For example, the expansion (x₁ + x₂ + x₃)² = x₁² + x₁x₂ + x₁x₃ + ...
has the exponents:
julia> collect(multiexponents(3, 2))
6-element Array{Any,1}:
[2, 0, 0]
[1, 1, 0]
[1, 0, 1]
[0, 2, 0]
[0, 1, 1]
[0, 0, 2]
"""
function multiexponents(m, n)
# number of stars and bars = m+n-1
c = combinations(1:m+n-1, n)

MultiExponents(c, m)
end
26 changes: 26 additions & 0 deletions test/multinomials.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Combinatorics
using Base.Test

# degenerate cases (x₁ + x₂ + ⋯ + xₘ)¹
@test [multiexponents(1,1)...] == [[1]]
@test [multiexponents(2,1)...] == [[1,0],[0,1]]
@test [multiexponents(3,1)...] == [[1,0,0],[0,1,0],[0,0,1]]
@test hcat([multiexponents(10,1)...]...) == eye(10)

# degenerate cases (x₁ + x₂ + ⋯ + xₘ)⁰
@test [multiexponents(1,0)...] == [[0]]
@test [multiexponents(2,0)...] == [[0,0]]
@test [multiexponents(3,0)...] == [[0,0,0]]
@test [multiexponents(10,0)...] == [zeros(Int, 10)]

# degenerate cases (x₁)ⁿ
@test [multiexponents(1,1)...] == [[1]]
@test [multiexponents(1,2)...] == [[2]]
@test [multiexponents(1,3)...] == [[3]]
@test [multiexponents(1,10)...] == [[10]]

# general cases
@test [multiexponents(3,2)...] == [[2,0,0],[1,1,0],[1,0,1],[0,2,0],[0,1,1],[0,0,2]]
@test [multiexponents(2,3)...] == [[3,0],[2,1],[1,2],[0,3]]
@test [multiexponents(2,2)...] == [[2,0],[1,1],[0,2]]
@test [multiexponents(3,3)...] == [[3,0,0],[2,1,0],[2,0,1],[1,2,0],[1,1,1],[1,0,2],[0,3,0],[0,2,1],[0,1,2],[0,0,3]]
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ include("factorials.jl")
include("combinations.jl")
include("permutations.jl")
include("partitions.jl")
include("multinomials.jl")
include("youngdiagrams.jl")

0 comments on commit aaaf5f4

Please sign in to comment.