Skip to content

Commit e142f09

Browse files
committed
speed-up randperm by using our current rand(1:n)
And similarly for `randcycle` and `shuffle`. We had a custom version of range generation for `randperm`, which was based on the ideas of our previous default range sampler `SamplerRangeFast` (generate `k`-bits integers using masking and reject out-of-range ones) and took advantage of the fact that `randperm` needs to generate `rand(1:i)` for `i = 2:n`. But our current range sampler ("Nearly Division Less") is usually better than this hack, and makes these functions more readable. Typically, for array lengths `< 2^20`, the new version is faster, but gets slightly slower beyond 2^22.
1 parent 215dca2 commit e142f09

File tree

1 file changed

+41
-55
lines changed

1 file changed

+41
-55
lines changed

stdlib/Random/src/misc.jl

Lines changed: 41 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,6 @@ julia> randsubseq(Xoshiro(123), 1:8, 0.3)
176176
randsubseq(A::AbstractArray, p::Real) = randsubseq(default_rng(), A, p)
177177

178178

179-
## rand Less Than Masked 52 bits (helper function)
180-
181-
"Return a sampler generating a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`."
182-
ltm52(n::Int, mask::Int=nextpow(2, n)-1) = LessThan(n-1, Masked(mask, UInt52Raw(Int)))
183-
184179
## shuffle & shuffle!
185180

186181
"""
@@ -191,31 +186,22 @@ optionally supplying the random-number generator `rng`.
191186
192187
# Examples
193188
```jldoctest
194-
julia> shuffle!(Xoshiro(123), Vector(1:10))
195-
10-element Vector{Int64}:
196-
5
197-
4
198-
2
199-
3
200-
6
201-
10
202-
8
203-
1
204-
9
205-
7
189+
julia> shuffle!(Xoshiro(0), Vector(1:6))
190+
6-element Vector{Int64}:
191+
5
192+
1
193+
2
194+
6
195+
3
196+
4
206197
```
207198
"""
208-
function shuffle!(r::AbstractRNG, a::AbstractArray)
199+
function shuffle!(rng::AbstractRNG, a::AbstractArray)
209200
# keep it consistent with `randperm!` and `randcycle!` if possible
210201
require_one_based_indexing(a)
211-
n = length(a)
212-
@assert n <= Int64(2)^52
213-
n == 0 && return a
214-
mask = 3
215-
@inbounds for i = 2:n
216-
j = 1 + rand(r, ltm52(i, mask))
202+
@inbounds for i = 2:length(a)
203+
j = rand(rng, 1:i)
217204
a[i], a[j] = a[j], a[i]
218-
i == 1 + mask && (mask = 2 * mask + 1)
219205
end
220206
return a
221207
end
@@ -247,18 +233,14 @@ indices, see [`randperm`](@ref).
247233
248234
# Examples
249235
```jldoctest
250-
julia> shuffle(Xoshiro(123), Vector(1:10))
251-
10-element Vector{Int64}:
252-
5
253-
4
254-
2
255-
3
256-
6
257-
10
258-
8
259-
1
260-
9
261-
7
236+
julia> shuffle(Xoshiro(0), 1:6)
237+
6-element Vector{Int64}:
238+
5
239+
1
240+
2
241+
6
242+
3
243+
4
262244
```
263245
"""
264246
shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a))
@@ -285,12 +267,14 @@ To randomly permute an arbitrary vector, see [`shuffle`](@ref) or
285267
286268
# Examples
287269
```jldoctest
288-
julia> randperm(Xoshiro(123), 4)
289-
4-element Vector{Int64}:
270+
julia> randperm(Xoshiro(0), 6)
271+
6-element Vector{Int64}:
272+
5
290273
1
291-
4
292274
2
275+
6
293276
3
277+
4
294278
```
295279
"""
296280
randperm(r::AbstractRNG, n::T) where {T <: Integer} = randperm!(r, Vector{T}(undef, n))
@@ -306,28 +290,27 @@ optional `rng` argument specifies a random number generator (see
306290
307291
# Examples
308292
```jldoctest
309-
julia> randperm!(Xoshiro(123), Vector{Int}(undef, 4))
310-
4-element Vector{Int64}:
293+
julia> randperm!(Xoshiro(0), Vector{Int}(undef, 6))
294+
6-element Vector{Int64}:
295+
5
311296
1
312-
4
313297
2
298+
6
314299
3
300+
4
315301
```
316302
"""
317-
function randperm!(r::AbstractRNG, a::Array{<:Integer})
303+
function randperm!(rng::AbstractRNG, a::Array{<:Integer})
318304
# keep it consistent with `shuffle!` and `randcycle!` if possible
319305
n = length(a)
320-
@assert n <= Int64(2)^52
321306
n == 0 && return a
322307
a[1] = 1
323-
mask = 3
324308
@inbounds for i = 2:n
325-
j = 1 + rand(r, ltm52(i, mask))
309+
j = rand(rng, 1:i)
326310
if i != j # a[i] is undef (and could be #undef)
327311
a[i] = a[j]
328312
end
329313
a[j] = i
330-
i == 1 + mask && (mask = 2 * mask + 1)
331314
end
332315
return a
333316
end
@@ -356,10 +339,13 @@ which are sampled uniformly. If `n == 0`, `randcycle` returns an empty vector.
356339
357340
# Examples
358341
```jldoctest
359-
julia> randcycle(Xoshiro(123), 6)
342+
julia> randcycle(Xoshiro(0), 6)
360343
6-element Vector{Int64}:
361344
5
345+
1
362346
4
347+
6
348+
3
363349
2
364350
6
365351
3
@@ -384,29 +370,29 @@ which are sampled uniformly. If `A` is empty, `randcycle!` leaves it unchanged.
384370
385371
# Examples
386372
```jldoctest
387-
julia> randcycle!(Xoshiro(123), Vector{Int}(undef, 6))
373+
julia> randcycle!(Xoshiro(0), Vector{Int}(undef, 6))
388374
6-element Vector{Int64}:
389375
5
376+
1
390377
4
378+
6
379+
3
391380
2
392381
6
393382
3
394383
1
395384
```
396385
"""
397-
function randcycle!(r::AbstractRNG, a::Array{<:Integer})
386+
function randcycle!(rng::AbstractRNG, a::Array{<:Integer})
398387
# keep it consistent with `shuffle!` and `randperm!` if possible
399388
n = length(a)
400-
@assert n <= Int64(2)^52
401389
n == 0 && return a
402390
a[1] = 1
403-
mask = 3
404391
# Sattolo's algorithm:
405392
@inbounds for i = 2:n
406-
j = 1 + rand(r, ltm52(i-1, mask))
393+
j = rand(rng, 1:i-1)
407394
a[i] = a[j]
408395
a[j] = i
409-
i == 1 + mask && (mask = 2 * mask + 1)
410396
end
411397
return a
412398
end

0 commit comments

Comments
 (0)