Skip to content

Commit 5d1ca6a

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 2d4d096 commit 5d1ca6a

File tree

1 file changed

+41
-71
lines changed

1 file changed

+41
-71
lines changed

stdlib/Random/src/misc.jl

Lines changed: 41 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,6 @@ julia> randsubseq(rng, 1:8, 0.3)
182182
randsubseq(A::AbstractArray, p::Real) = randsubseq(default_rng(), A, p)
183183

184184

185-
## rand Less Than Masked 52 bits (helper function)
186-
187-
"Return a sampler generating a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`."
188-
ltm52(n::Int, mask::Int=nextpow(2, n)-1) = LessThan(n-1, Masked(mask, UInt52Raw(Int)))
189-
190185
## shuffle & shuffle!
191186

192187
"""
@@ -197,39 +192,22 @@ optionally supplying the random-number generator `rng`.
197192
198193
# Examples
199194
```jldoctest
200-
julia> rng = MersenneTwister(1234);
201-
202-
julia> shuffle!(rng, Vector(1:16))
203-
16-element Vector{Int64}:
204-
16
205-
1
206-
14
207-
12
208-
5
209-
10
210-
4
211-
15
212-
13
213-
3
214-
7
215-
9
216-
6
217-
11
218-
8
219-
2
195+
julia> shuffle!(Xoshiro(0), Vector(1:6))
196+
6-element Vector{Int64}:
197+
5
198+
1
199+
2
200+
6
201+
3
202+
4
220203
```
221204
"""
222-
function shuffle!(r::AbstractRNG, a::AbstractArray)
205+
function shuffle!(rng::AbstractRNG, a::AbstractArray)
223206
# keep it consistent with `randperm!` and `randcycle!` if possible
224207
require_one_based_indexing(a)
225-
n = length(a)
226-
@assert n <= Int64(2)^52
227-
n == 0 && return a
228-
mask = 3
229-
@inbounds for i = 2:n
230-
j = 1 + rand(r, ltm52(i, mask))
208+
@inbounds for i = 2:length(a)
209+
j = rand(rng, 1:i)
231210
a[i], a[j] = a[j], a[i]
232-
i == 1 + mask && (mask = 2 * mask + 1)
233211
end
234212
return a
235213
end
@@ -246,20 +224,14 @@ indices, see [`randperm`](@ref).
246224
247225
# Examples
248226
```jldoctest
249-
julia> rng = MersenneTwister(1234);
250-
251-
julia> shuffle(rng, Vector(1:10))
252-
10-element Vector{Int64}:
253-
2
254-
1
255-
7
256-
9
257-
5
258-
10
259-
4
260-
8
261-
6
262-
3
227+
julia> shuffle(Xoshiro(0), 1:6)
228+
6-element Vector{Int64}:
229+
5
230+
1
231+
2
232+
6
233+
3
234+
4
263235
```
264236
"""
265237
shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a))
@@ -286,12 +258,14 @@ To randomly permute an arbitrary vector, see [`shuffle`](@ref) or
286258
287259
# Examples
288260
```jldoctest
289-
julia> randperm(MersenneTwister(1234), 4)
290-
4-element Vector{Int64}:
291-
2
261+
julia> randperm(Xoshiro(0), 6)
262+
6-element Vector{Int64}:
263+
5
292264
1
293-
4
265+
2
266+
6
294267
3
268+
4
295269
```
296270
"""
297271
randperm(r::AbstractRNG, n::T) where {T <: Integer} = randperm!(r, Vector{T}(undef, n))
@@ -307,28 +281,27 @@ optional `rng` argument specifies a random number generator (see
307281
308282
# Examples
309283
```jldoctest
310-
julia> randperm!(MersenneTwister(1234), Vector{Int}(undef, 4))
311-
4-element Vector{Int64}:
312-
2
284+
julia> randperm!(Xoshiro(0), Vector{Int}(undef, 6))
285+
6-element Vector{Int64}:
286+
5
313287
1
314-
4
288+
2
289+
6
315290
3
291+
4
316292
```
317293
"""
318-
function randperm!(r::AbstractRNG, a::Array{<:Integer})
294+
function randperm!(rng::AbstractRNG, a::Array{<:Integer})
319295
# keep it consistent with `shuffle!` and `randcycle!` if possible
320296
n = length(a)
321-
@assert n <= Int64(2)^52
322297
n == 0 && return a
323298
a[1] = 1
324-
mask = 3
325299
@inbounds for i = 2:n
326-
j = 1 + rand(r, ltm52(i, mask))
300+
j = rand(rng, 1:i)
327301
if i != j # a[i] is undef (and could be #undef)
328302
a[i] = a[j]
329303
end
330304
a[j] = i
331-
i == 1 + mask && (mask = 2 * mask + 1)
332305
end
333306
return a
334307
end
@@ -351,13 +324,13 @@ The element type of the result is the same as the type of `n`.
351324
352325
# Examples
353326
```jldoctest
354-
julia> randcycle(MersenneTwister(1234), 6)
327+
julia> randcycle(Xoshiro(0), 6)
355328
6-element Vector{Int64}:
356-
3
357329
5
330+
1
358331
4
359332
6
360-
1
333+
3
361334
2
362335
```
363336
"""
@@ -373,28 +346,25 @@ The optional `rng` argument specifies a random number generator, see
373346
374347
# Examples
375348
```jldoctest
376-
julia> randcycle!(MersenneTwister(1234), Vector{Int}(undef, 6))
349+
julia> randcycle!(Xoshiro(0), Vector{Int}(undef, 6))
377350
6-element Vector{Int64}:
378-
3
379351
5
352+
1
380353
4
381354
6
382-
1
355+
3
383356
2
384357
```
385358
"""
386-
function randcycle!(r::AbstractRNG, a::Array{<:Integer})
359+
function randcycle!(rng::AbstractRNG, a::Array{<:Integer})
387360
# keep it consistent with `shuffle!` and `randperm!` if possible
388361
n = length(a)
389-
@assert n <= Int64(2)^52
390362
n == 0 && return a
391363
a[1] = 1
392-
mask = 3
393364
@inbounds for i = 2:n
394-
j = 1 + rand(r, ltm52(i-1, mask))
365+
j = rand(rng, 1:i-1)
395366
a[i] = a[j]
396367
a[j] = i
397-
i == 1 + mask && (mask = 2 * mask + 1)
398368
end
399369
return a
400370
end

0 commit comments

Comments
 (0)