Skip to content

Commit

Permalink
Combinatorics updates
Browse files Browse the repository at this point in the history
* Removed unnecessary copy from combinations()
* Added permutations()
* Added start, stop to reverse, reverse!
* Added simple tests for combinatorics, reverse
  • Loading branch information
kmsquire committed Aug 12, 2013
1 parent 66734e6 commit aa3512c
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 28 deletions.
6 changes: 3 additions & 3 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
50 changes: 42 additions & 8 deletions base/combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ export
parentindexes,
partitions,
pascal,
permutations,
permute!,
permutedims,
prod,
Expand Down
25 changes: 17 additions & 8 deletions doc/helpdb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"),

Expand Down
23 changes: 15 additions & 8 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 $@
Expand Down
11 changes: 11 additions & 0 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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]


9 changes: 9 additions & 0 deletions test/combinatorics.jl
Original file line number Diff line number Diff line change
@@ -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"]

2 comments on commit aa3512c

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great to have. Also, I bet it was fun to write :-)

@kmsquire
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-) It was.

Please sign in to comment.