diff --git a/NEWS.md b/NEWS.md index d35daff87fedb8..b25a8940475263 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 -------------------- @@ -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 ----------------------------- diff --git a/base/exports.jl b/base/exports.jl index 335a5c4bba3852..7f84a282e37cf4 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -508,7 +508,9 @@ export prod, promote_shape, randcycle, + randcycle!, randperm, + randperm!, randsubseq!, randsubseq, range, diff --git a/base/random.jl b/base/random.jl index 50c7cab06cafd8..d1a544d18ba029 100644 --- a/base/random.jl +++ b/base/random.jl @@ -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 @@ -1789,14 +1790,33 @@ 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 @@ -1804,15 +1824,13 @@ julia> randperm(rng, 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] @@ -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) @@ -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 @@ -1844,13 +1884,13 @@ 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 @@ -1858,6 +1898,7 @@ function randcycle(r::AbstractRNG, n::Integer) end return a end -randcycle(n::Integer) = randcycle(GLOBAL_RNG, n) + +randcycle!(a::Array{<:Integer}) = randcycle!(GLOBAL_RNG, a) end # module diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index b9cdb5dff672fa..aedb79e7f6a64b 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -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 diff --git a/test/random.jl b/test/random.jl index cb2b9bf635f640..46b2c6fce7474f 100644 --- a/test/random.jl +++ b/test/random.jl @@ -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)