Skip to content

Commit

Permalink
add mutating versions of randperm & randcycle (#22723)
Browse files Browse the repository at this point in the history
  • Loading branch information
rfourquet authored Jul 23, 2017
1 parent 2f3bef1 commit 7287321
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 19 deletions.
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

2 comments on commit 7287321

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

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

Executing the daily benchmark build, I will reply here when finished:

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

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

Something went wrong when running your job:

NanosoldierError: failed to run benchmarks against primary commit: failed process: Process(`sudo cset shield -e su nanosoldier -- -c ./benchscript.sh`, ProcessExited(1)) [1]

Logs and partial data can be found here
cc @ararslan

Please sign in to comment.