Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mutating versions of randperm & randcycle #22723

Merged
merged 4 commits into from
Jul 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ This section lists changes that do not have deprecation warnings.
`@everywhere include_string(Main, $(read("filename", String)), "filename")`.
Improving upon this API is left as an opportunity for packages.

* `randperm(n)` and `randcycle(n)` now always return a `Vector{Int}` (independent of
the type of `n`). Use the corresponding mutating functions `randperm!` and `randcycle!`
to control the array type ([#22723]).

Library improvements
--------------------

Expand Down Expand Up @@ -128,6 +132,9 @@ Library improvements
* `Diagonal` is now parameterized on the type of the wrapped vector. This allows
for `Diagonal` matrices with arbitrary `AbstractVector`s ([#22718]).

* Mutating versions of `randperm` and `randcycle` have been added:
`randperm!` and `randcycle!` ([#22723]).

Compiler/Runtime improvements
-----------------------------

Expand Down
2 changes: 2 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,9 @@ export
prod,
promote_shape,
randcycle,
randcycle!,
randperm,
randperm!,
randsubseq!,
randsubseq,
range,
Expand Down
79 changes: 60 additions & 19 deletions base/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ export srand,
randexp, randexp!,
bitrand,
randstring,
randsubseq,randsubseq!,
shuffle,shuffle!,
randperm, randcycle,
randsubseq, randsubseq!,
shuffle, shuffle!,
randperm, randperm!,
randcycle, randcycle!,
AbstractRNG, MersenneTwister, RandomDevice,
GLOBAL_RNG, randjump

Expand Down Expand Up @@ -1789,30 +1790,47 @@ shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a)
Construct a random permutation of length `n`. The optional `rng` argument specifies a random
number generator (see [Random Numbers](@ref)).
To randomly permute a arbitrary vector, see [`shuffle`](@ref)
To randomly permute an arbitrary vector, see [`shuffle`](@ref)
or [`shuffle!`](@ref).
# Examples
```jldoctest
julia> rng = MersenneTwister(1234);
julia> randperm(MersenneTwister(1234), 4)
4-element Array{Int64,1}:
2
1
4
3
```
"""
randperm(r::AbstractRNG, n::Integer) = randperm!(r, Vector{Int}(n))
randperm(n::Integer) = randperm(GLOBAL_RNG, n)

"""
randperm!([rng=GLOBAL_RNG,] A::Array{<:Integer})
Construct in `A` a random permutation of length `length(A)`. The
optional `rng` argument specifies a random number generator (see
[Random Numbers](@ref)). To randomly permute an arbitrary vector, see
[`shuffle`](@ref) or [`shuffle!`](@ref).
julia> randperm(rng, 4)
# Examples
```jldoctest
julia> randperm!(MersenneTwister(1234), Vector{Int}(4))
4-element Array{Int64,1}:
2
1
4
3
```
"""
function randperm(r::AbstractRNG, n::Integer)
a = Vector{typeof(n)}(n)
function randperm!(r::AbstractRNG, a::Array{<:Integer})
n = length(a)
@assert n <= Int64(2)^52
if n == 0
return a
end
n == 0 && return a
a[1] = 1
mask = 3
@inbounds for i = 2:Int(n)
@inbounds for i = 2:n
j = 1 + rand_lt(r, i, mask)
if i != j # a[i] is uninitialized (and could be #undef)
a[i] = a[j]
Expand All @@ -1822,7 +1840,9 @@ function randperm(r::AbstractRNG, n::Integer)
end
return a
end
randperm(n::Integer) = randperm(GLOBAL_RNG, n)

randperm!(a::Array{<:Integer}) = randperm!(GLOBAL_RNG, a)


"""
randcycle([rng=GLOBAL_RNG,] n::Integer)
Expand All @@ -1832,9 +1852,29 @@ argument specifies a random number generator, see [Random Numbers](@ref).
# Examples
```jldoctest
julia> rng = MersenneTwister(1234);
julia> randcycle(MersenneTwister(1234), 6)
6-element Array{Int64,1}:
3
5
4
6
1
2
```
"""
randcycle(r::AbstractRNG, n::Integer) = randcycle!(r, Vector{Int}(n))
randcycle(n::Integer) = randcycle(GLOBAL_RNG, n)

"""
randcycle!([rng=GLOBAL_RNG,] A::Array{<:Integer})
julia> randcycle(rng, 6)
Construct in `A` a random cyclic permutation of length `length(A)`.
The optional `rng` argument specifies a random number generator, see
[Random Numbers](@ref).
# Examples
```jldoctest
julia> randcycle!(MersenneTwister(1234), Vector{Int}(6))
6-element Array{Int64,1}:
3
5
Expand All @@ -1844,20 +1884,21 @@ julia> randcycle(rng, 6)
2
```
"""
function randcycle(r::AbstractRNG, n::Integer)
a = Vector{typeof(n)}(n)
function randcycle!(r::AbstractRNG, a::Array{<:Integer})
n = length(a)
n == 0 && return a
@assert n <= Int64(2)^52
a[1] = 1
mask = 3
@inbounds for i = 2:Int(n)
@inbounds for i = 2:n
j = 1 + rand_lt(r, i-1, mask)
a[i] = a[j]
a[j] = i
i == 1+mask && (mask = 2mask + 1)
end
return a
end
randcycle(n::Integer) = randcycle(GLOBAL_RNG, n)

randcycle!(a::Array{<:Integer}) = randcycle!(GLOBAL_RNG, a)

end # module
2 changes: 2 additions & 0 deletions doc/src/stdlib/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,13 @@ Base.sum_kbn

```@docs
Base.Random.randperm
Base.Random.randperm!
Base.invperm
Base.isperm
Base.permute!(::Any, ::AbstractVector)
Base.ipermute!
Base.Random.randcycle
Base.Random.randcycle!
Base.Random.shuffle
Base.Random.shuffle!
Base.reverse
Expand Down
8 changes: 8 additions & 0 deletions test/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,17 @@ let mta = MersenneTwister(42), mtb = MersenneTwister(42)
@test sort!(randperm(10)) == sort!(shuffle(1:10)) == collect(1:10)
@test randperm(mta,big(10)) == randperm(mtb,big(10)) # cf. #16376
@test randperm(0) == []
@test eltype(randperm(UInt(1))) === Int
@test_throws ErrorException randperm(-1)

A, B = Vector{Int}(10), Vector{Int}(10)
@test randperm!(mta, A) == randperm!(mtb, B)
@test randperm!(A) === A

@test randcycle(mta,10) == randcycle(mtb,10)
@test eltype(randcycle(UInt(1))) === Int
@test randcycle!(mta, A) == randcycle!(mtb, B)
@test randcycle!(A) === A

@test sprand(mta,1,1,0.9) == sprand(mtb,1,1,0.9)
@test sprand(mta,10,10,0.3) == sprand(mtb,10,10,0.3)
Expand Down