Skip to content

Commit b2eb86a

Browse files
committed
add shuffle(::NTuple) to Random
Fixes #56728
1 parent 5198c06 commit b2eb86a

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

stdlib/Random/src/misc.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,36 @@ ltm52(n::Int, mask::Int=nextpow(2, n)-1) = LessThan(n-1, Masked(mask, UInt52Raw(
183183

184184
## shuffle & shuffle!
185185

186+
function _tuple_without_element_at_index(tup::(Tuple{T, Vararg{T, N}} where {T}), i::Int) where {N}
187+
clo = let tup = tup, i = i
188+
function closure(j::Int)
189+
k = if j < i
190+
j
191+
else
192+
j + 1
193+
end
194+
tup[k]
195+
end
196+
end
197+
ntuple(clo, Val{N}())
198+
end
199+
200+
function shuffle(rng::AbstractRNG, tup::(Tuple{Vararg{T, N}} where {T})) where {N}
201+
if tup isa Tuple{Any, Vararg}
202+
let i = rand(rng, Base.OneTo(N)) # Fisher–Yates shuffle
203+
s = _tuple_without_element_at_index(tup, i)
204+
t = shuffle(rng, s)
205+
(tup[i], t...)
206+
end
207+
else
208+
tup
209+
end::typeof(tup)
210+
end
211+
212+
function shuffle(tup::NTuple)
213+
shuffle(default_rng(), tup)
214+
end
215+
186216
"""
187217
shuffle!([rng=default_rng(),] v::AbstractArray)
188218

stdlib/Random/test/runtests.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,34 @@ end
10321032
@test maximum(m) <= 0.106
10331033
end
10341034

1035+
@testset "`shuffle(::NTuple)`" begin
1036+
@testset "sorted" begin
1037+
for n 0:20
1038+
tup = ntuple(identity, n)
1039+
@test tup === sort(@inferred shuffle(tup))
1040+
end
1041+
end
1042+
@testset "not identity" begin
1043+
function is_not_identity_at_least_once()
1044+
function f(::Any)
1045+
tup = ntuple(identity, 9)
1046+
tup !== shuffle(tup)
1047+
end
1048+
@test any(f, 1:1000000)
1049+
end
1050+
is_not_identity_at_least_once()
1051+
end
1052+
@testset "no heap allocation" begin
1053+
function no_heap_allocation(tup)
1054+
@test iszero(@allocated shuffle(tup))
1055+
end
1056+
for n 0:9
1057+
tup = ntuple(identity, n)
1058+
no_heap_allocation(tup)
1059+
end
1060+
end
1061+
end
1062+
10351063
# issue #42752
10361064
# test that running finalizers that launch tasks doesn't change RNG stream
10371065
function f42752(do_gc::Bool, cell = (()->Any[[]])())

0 commit comments

Comments
 (0)