From aaaf5f48413f2003531b6d00918d68a139511c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Wed, 22 Nov 2017 11:01:00 -0800 Subject: [PATCH] Add multiexponents(m,n) for exponents of multinomial expansion (#57) * 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 --- .travis.yml | 1 + README.md | 1 + src/Combinatorics.jl | 1 + src/multinomials.jl | 55 ++++++++++++++++++++++++++++++++++++++++++++ test/multinomials.jl | 26 +++++++++++++++++++++ test/runtests.jl | 1 + 6 files changed, 85 insertions(+) create mode 100644 src/multinomials.jl create mode 100644 test/multinomials.jl diff --git a/.travis.yml b/.travis.yml index ea568cb..5297dc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ os: - linux julia: - 0.5 + - 0.6 - nightly sudo: false notifications: diff --git a/README.md b/README.md index 3646f42..a6ef9bd 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/src/Combinatorics.jl b/src/Combinatorics.jl index 679572c..770ba20 100644 --- a/src/Combinatorics.jl +++ b/src/Combinatorics.jl @@ -19,6 +19,7 @@ include("factorials.jl") include("combinations.jl") include("permutations.jl") include("partitions.jl") +include("multinomials.jl") include("youngdiagrams.jl") end #module diff --git a/src/multinomials.jl b/src/multinomials.jl new file mode 100644 index 0000000..012b241 --- /dev/null +++ b/src/multinomials.jl @@ -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 diff --git a/test/multinomials.jl b/test/multinomials.jl new file mode 100644 index 0000000..baa7211 --- /dev/null +++ b/test/multinomials.jl @@ -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]] diff --git a/test/runtests.jl b/test/runtests.jl index 7acd6ef..a2b96bb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,5 +6,6 @@ include("factorials.jl") include("combinations.jl") include("permutations.jl") include("partitions.jl") +include("multinomials.jl") include("youngdiagrams.jl")