Hello,
I am trying to update to v1.0 the code with_repetitions_permutations found on
rosettacode that was build for Julia v0.6.
This function simply gives all possible permutations with repetition (function that I did not found in the Combinatorics package).
The two codes works on their respective versions but somehow the v0.6 version is a bit faster (has less allocations) when I compare them with @time.
For example (on the same computer)
v0.6:
@time collect(with_repetitions_permutations([0,1,2],15))
4.427327 seconds (43.05 M allocations: 3.528 GiB, 56.30% gc time)
14348907-element Array{Array{Int64,1},1}:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
⋮
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
For the v1.0:
@time collect(with_repetitions_permutations([0,1,2],15))
7.878192 seconds (57.40 M allocations: 6.308 GiB, 63.05% gc time)
14348907-element Array{Array{Int64,1},1}:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
⋮
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
The original code v0.6 is
struct WithRepetitionsPermutations{T}
a::T
t::Int
end
with_repetitions_permutations(elements::T, len::Integer) where T =
WithRepetitionsPermutations{T}(unique(elements), len)
Base.iteratorsize(::WithRepetitionsPermutations) = Base.HasLength()
Base.length(p::WithRepetitionsPermutations) = length(p.a) ^ p.t
Base.iteratoreltype(::WithRepetitionsPermutations) = Base.HasEltype()
Base.eltype(::WithRepetitionsPermutations{T}) where T = T
Base.start(p::WithRepetitionsPermutations) = ones(Int, p.t)
Base.done(p::WithRepetitionsPermutations, s::Vector{Int}) = s[end] > endof(p.a)
function Base.next(p::WithRepetitionsPermutations, s::Vector{Int})
cur = p.a[s]
s[1] += 1
local i = 1
while i < endof(s) && s[i] > length(p.a)
s[i] = 1
s[i+1] += 1
i += 1
end
return cur, s
end
while the code for v1.0 that I wrote while being inspired by the Writing Iterators in Julia 0.7 basically I gave the new name to the iterator and insert the three method start, next, done in the main part.
struct WithRepetitionsPermutations{T}
a::T
t::Int
end
with_repetitions_permutations(elements::T, len::Integer) where T =
WithRepetitionsPermutations{T}(unique(elements), len)
Base.IteratorSize(::WithRepetitionsPermutations) = Base.HasLength()
Base.length(p::WithRepetitionsPermutations) = length(p.a) ^ p.t
Base.IteratorEltype(::WithRepetitionsPermutations) = Base.HasEltype()
Base.eltype(::WithRepetitionsPermutations{T}) where T = T
function Base.iterate(p::WithRepetitionsPermutations, state=ones(Int, p.t))
if s[end] > lastindex(p.a)
return nothing
end
cur = p.a[s]
s[1] += 1
local i = 1
while i < lastindex(s) && s[i] > length(p.a)
s[i] = 1
s[i+1] += 1
i += 1
end
return cur, s
end
My question is then did I did something wrong or not optimal in re writing the code that would explain the speed (allocation) difference? I am very new to Julia and I am not entirely sure to understand this iterator function so it very possible.
Thanks
edit: The problem here was that state
should have been replaced by s