Skip to content

Commit 7bb811d

Browse files
committed
add shuffle(::NTuple) to Random
Fixes #56728
1 parent 62d3371 commit 7bb811d

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
@@ -1063,6 +1063,34 @@ end
10631063
@test maximum(m) <= 0.106
10641064
end
10651065

1066+
@testset "`shuffle(::NTuple)`" begin
1067+
@testset "sorted" begin
1068+
for n 0:20
1069+
tup = ntuple(identity, n)
1070+
@test tup === sort(@inferred shuffle(tup))
1071+
end
1072+
end
1073+
@testset "not identity" begin
1074+
function is_not_identity_at_least_once()
1075+
function f(::Any)
1076+
tup = ntuple(identity, 9)
1077+
tup !== shuffle(tup)
1078+
end
1079+
@test any(f, 1:1000000)
1080+
end
1081+
is_not_identity_at_least_once()
1082+
end
1083+
@testset "no heap allocation" begin
1084+
function no_heap_allocation(tup)
1085+
@test iszero(@allocated shuffle(tup))
1086+
end
1087+
for n 0:9
1088+
tup = ntuple(identity, n)
1089+
no_heap_allocation(tup)
1090+
end
1091+
end
1092+
end
1093+
10661094
# issue #42752
10671095
# test that running finalizers that launch tasks doesn't change RNG stream
10681096
function f42752(do_gc::Bool, cell = (()->Any[[]])())

0 commit comments

Comments
 (0)