From aa3512cf3019ebb4cacc7fd1e3d475ea93964270 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Mon, 12 Aug 2013 01:57:41 -0700 Subject: [PATCH] Combinatorics updates * Removed unnecessary copy from combinations() * Added permutations() * Added start, stop to reverse, reverse! * Added simple tests for combinatorics, reverse --- base/array.jl | 6 +++--- base/combinatorics.jl | 50 ++++++++++++++++++++++++++++++++++++------- base/exports.jl | 1 + doc/helpdb.jl | 25 +++++++++++++++------- doc/stdlib/base.rst | 23 +++++++++++++------- test/Makefile | 3 ++- test/arrayops.jl | 11 ++++++++++ test/combinatorics.jl | 9 ++++++++ 8 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 test/combinatorics.jl diff --git a/base/array.jl b/base/array.jl index f06c02bdb34d2..1ab283334aa77 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1161,10 +1161,10 @@ rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k) rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A) reverse(v::StridedVector) = (n=length(v); [ v[n-i+1] for i=1:n ]) -function reverse!(v::AbstractVector) - n = length(v) +reverse(v::StridedVector, s=1, n=length(v)) = [[v[i] for i=1:s-1], [v[n-i] for i = 0:n-s], [v[i] for i = n+1:endof(v)]] +function reverse!(v::AbstractVector, s=1, n=length(v)) r = n - for i=1:div(n,2) + for i=s:div(s+n-1,2) v[i], v[r] = v[r], v[i] r -= 1 end diff --git a/base/combinatorics.jl b/base/combinatorics.jl index e94f4512b1262..57b507d5b5db7 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -186,6 +186,8 @@ immutable Combinations{T} end eltype(c::Combinations) = typeof(c.a) +eltype{T}(c::Combinations{Range1{T}}) = Array{T,1} +eltype{T}(c::Combinations{Range{T}}) = Array{T,1} function combinations(a, t::Integer) if t < 0 @@ -197,25 +199,57 @@ end start(c::Combinations) = [1:c.t] function next(c::Combinations, s) + comb = c.a[s] if c.t == 0 # special case to generate 1 result for t==0 - return (c.a[s],[length(c.a)+2]) + return (comb,[length(c.a)+2]) end - sn = copy(s) - for i = length(sn):-1:1 - sn[i] += 1 - if sn[i] > (length(c.a) - (length(sn)-i)) + for i = length(s):-1:1 + s[i] += 1 + if s[i] > (length(c.a) - (length(s)-i)) continue end - for j = i+1:endof(sn) - sn[j] = sn[j-1]+1 + for j = i+1:endof(s) + s[j] = s[j-1]+1 end break end - (c.a[s],sn) + (comb,s) end done(c::Combinations, s) = !isempty(s) && s[1] > length(c.a)-c.t+1 +immutable Permutations{T} + a::T +end + +eltype(c::Permutations) = typeof(c.a) +eltype{T}(c::Permutations{Range1{T}}) = Array{T,1} +eltype{T}(c::Permutations{Range{T}}) = Array{T,1} + +permutations(a) = Permutations(a) + +start(p::Permutations) = [1:length(p.a)] +function next(p::Permutations, s) + if length(p.a) == 0 + # special case to generate 1 result for len==0 + return (p.a,[1]) + end + perm = p.a[s] + k = length(s)-1 + while k > 0 && s[k] > s[k+1]; k -= 1; end + if k == 0 + s[1] = length(s)+1 # done + else + l = length(s) + while s[k] >= s[l]; l -= 1; end + s[k],s[l] = s[l],s[k] + reverse!(s,k+1) + end + (perm,s) +end +done(p::Permutations, s) = !isempty(s) && s[1] > length(p.a) + + # Algorithm H from TAoCP 7.2.1.4 # Partition n into m parts function integer_partitions{T<:Integer}(n::T, m::T) diff --git a/base/exports.jl b/base/exports.jl index 99a956b9b4029..c2b513a85c816 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -524,6 +524,7 @@ export parentindexes, partitions, pascal, + permutations, permute!, permutedims, prod, diff --git a/doc/helpdb.jl b/doc/helpdb.jl index f71912d03b402..ea211cd845a23 100644 --- a/doc/helpdb.jl +++ b/doc/helpdb.jl @@ -4569,24 +4569,33 @@ popdisplay(d::Display) "), -("Combinatorics","Base","reverse","reverse(v) +("Combinatorics","Base","reverse","reverse(v[, start=1[, stop=length(v)]]) - Reverse vector \"v\". + Reverse vector \"v\", optionally from start to stop. "), -("Combinatorics","Base","reverse!","reverse!(v) -> v +("Combinatorics","Base","reverse!","reverse!(v[, start=1[, stop=length(v)]]) -> v In-place version of \"reverse()\". "), -("Combinatorics","Base","combinations","combinations(array, n) +("Combinatorics","Base","combinations","combinations(itr, n) - Generate all combinations of \"n\" elements from a given array. - Because the number of combinations can be very large, this function - returns an iterator object. Use \"collect(combinations(a,n))\" to - get an array of all combinations. + Generate all combinations of \"n\" elements from a given iterable + object. Because the number of combinations can be very large, this + function returns an iterator object. Use + \"collect(combinations(a,n))\" to get an array of all combinations. + +"), + +("Combinatorics","Base","permutations","permutations(itr) + + Generate all permutations of a given iterable object. Because the + number of permutations can be very large, this function returns an + iterator object. Use \"collect(permutations(a,n))\" to get an array + of all permutations. "), diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 314eb2e7983f2..18bba3fdbc7ec 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -3108,20 +3108,27 @@ Combinatorics In-place version of :func:`shuffle`. -.. function:: reverse(v) +.. function:: reverse(v [, start=1 [, stop=length(v) ]] ) - Reverse vector ``v``. + Reverse vector ``v``, optionally from start to stop. -.. function:: reverse!(v) -> v +.. function:: reverse!(v [, start=1 [, stop=length(v) ]]) -> v In-place version of :func:`reverse`. -.. function:: combinations(array, n) +.. function:: combinations(itr, n) - Generate all combinations of ``n`` elements from a given array. Because - the number of combinations can be very large, this function returns an - iterator object. Use ``collect(combinations(a,n))`` to get an array of all - combinations. + Generate all combinations of ``n`` elements from a given iterable + object. Because the number of combinations can be very large, this + function returns an iterator object. Use + ``collect(combinations(a,n))`` to get an array of all combinations. + +.. function:: permutations(itr) + + Generate all permutations of a given iterable object. Because the + number of permutations can be very large, this function returns an + iterator object. Use ``collect(permutations(a,n))`` to get an array + of all permutations. .. function:: integer_partitions(n, m) diff --git a/test/Makefile b/test/Makefile index a7f781149422f..32bd7f26728df 100644 --- a/test/Makefile +++ b/test/Makefile @@ -5,7 +5,8 @@ TESTS = all core keywordargs numbers strings unicode collections hashing \ remote iostring arrayops linalg blas fft dsp sparse bitarray random \ math functional bigint sorting statistics spawn parallel arpack file \ git pkg pkg2 resolve resolve2 suitesparse complex version pollfd mpfr \ - broadcast socket floatapprox priorityqueue readdlm regex float16 + broadcast socket floatapprox priorityqueue readdlm regex float16 \ + combinations $(TESTS) :: @$(PRINT_JULIA) $(call spawn,$(JULIA_EXECUTABLE)) ./runtests.jl $@ diff --git a/test/arrayops.jl b/test/arrayops.jl index 4cd70d51debe7..e72d7748a2184 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -662,3 +662,14 @@ end @test_throws (10.^[-1])[1] == 0.1 @test (10.^[-1.])[1] == 0.1 + +# reverse +@test reverse([2,3,1]) == [1,3,2] +@test reverse([1:10],1,4) == [4,3,2,1,5,6,7,8,9,10] +@test reverse([1:10],3,6) == [1,2,6,5,4,3,7,8,9,10] +@test reverse([1:10],6,10) == [1,2,3,4,5,10,9,8,7,6] +@test reverse!([1:10],1,4) == [4,3,2,1,5,6,7,8,9,10] +@test reverse!([1:10],3,6) == [1,2,6,5,4,3,7,8,9,10] +@test reverse!([1:10],6,10) == [1,2,3,4,5,10,9,8,7,6] + + diff --git a/test/combinatorics.jl b/test/combinatorics.jl new file mode 100644 index 0000000000000..2c342730ad08d --- /dev/null +++ b/test/combinatorics.jl @@ -0,0 +1,9 @@ + +@test factorial(7) = 5040 +@test factorial(7,3) == 7*6*5*4 +@test binomial(5,3) == 10 +@test isperm(shuffle([1:1000])) +a = randcycle(10) +@test ipermute!(permute!([1:10], a),a) == [1:10] +@test collect(combinations("abc",2)) == ["ab","ac","bc"] +@test collect(collect(permutations("abc"))) == ["abc","acb","bac","bca","cab","cba"]