# Shuffle columns of a matrix

**URL:** https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334
**Category:** General Usage
**Tags:** question
**Created:** [June 30, 2025, 1:21pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334 "2025-06-30T13:21:23Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 30, 2025, 1:21pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/1 "2025-06-30T13:21:23Z")

</div>

I want to shuffle (randomly rearrange) the columns of a large matrix. As much in place as possible, as the matrix is large; otherwise a naive

```julia
M[:, randperm(size(M, 2))]

```

would do.

I thought of modifying `Random.shuffle!`, but I don’t understand what `mask` does in the code. If I instantiate a random permutation, I can do

```julia
using Random

function shuffle_columns1!(M::AbstractMatrix{T}) where T
    n, m = size(M)
    buffer = Vector{T}(undef, n)
    p = randperm(m)
    for (i, j) in enumerate(p)
        buffer .= M[:, i]
        M[:, i] .= M[:, j]
        M[:, j] .= buffer
    end
    M
end

```

eg

```julia
julia> M = mapreduce(_ -> permutedims(1:10), vcat, 1:5)
5×10 Matrix{Int64}:
 1 2 3 4 5 6 7 8 9 10
 1 2 3 4 5 6 7 8 9 10
 1 2 3 4 5 6 7 8 9 10
 1 2 3 4 5 6 7 8 9 10
 1 2 3 4 5 6 7 8 9 10

julia> shuffle_columns1!(M)
5×10 Matrix{Int64}:
 2 3 5 6 4 1 10 9 7 8
 2 3 5 6 4 1 10 9 7 8
 2 3 5 6 4 1 10 9 7 8
 2 3 5 6 4 1 10 9 7 8
 2 3 5 6 4 1 10 9 7 8

```

but I would rather avoid allocating the `randperm`, too. Is there a package which has functionality for something like this?

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [June 30, 2025, 1:30pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/2 "2025-06-30T13:30:56Z")

</div>

Would `randperm!` solve your issue?

---

<div class="post-metadata">

### Author: ![karei](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/karei/32/214809_2.png) [@karei](https://discourse.julialang.org/u/karei)
#### Post date: [June 30, 2025, 1:40pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/3 "2025-06-30T13:40:23Z")

</div>

```julia
p = randperm(size(M,2))
Base.permutecols!!(M, randperm!(p))

```

😉

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 30, 2025, 1:45pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/5 "2025-06-30T13:45:46Z")

</div>

> [@gdalle](#):
>
> Would `randperm!` solve your issue?

I don’t think so, it shuffles all elements, not just columns.

> [@karei](#):
>
> `Base.permutecols!!`

Needless to say, I don’t want to use internal bindings.

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [June 30, 2025, 1:47pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/6 "2025-06-30T13:47:48Z")

</div>

> [@Tamas\_Papp](#):
>
> > [@gdalle](#):
> >
> > Would `randperm!` solve your issue?
> 
> I don’t think so, it shuffles all elements, not just columns.

I meant to avoid allocating `randperm` inside your `shuffle_columns1!`?

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [June 30, 2025, 1:49pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/7 "2025-06-30T13:49:11Z")

</div>

> [@Tamas\_Papp](#):
>
> I don’t want to use internal bindings.

Sure, but the code here is self-contained and something you could just copy directly into your own source (with attribution).

> <https://github.com/JuliaLang/julia/blob/8caacdb69e3a9d649016d95bd032a8bd17ad4992/base/combinatorics.jl#L103-L144>

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 30, 2025, 2:05pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/8 "2025-06-30T14:05:45Z")

</div>

> [@mbauman](#):
>
> the code here is self-contained and something you could just copy directly into your own source (with attribution)

Certainly, if as a last resort that can be done; but as I said above, I can also modify `shuffle!`.

One just needs to generalize the operation of “exchange i for j”, eg

```julia
# a generalization of `shuffle!` adopted from Random, exchange!(i, j) swaps
# whatever the user wants
function do_shuffle(exchange!, r::AbstractRNG, n)
    # keep it consistent with `randperm!` and `randcycle!` if possible
    @assert n <= Int64(2)^52
    n == 0 && return
    mask = 3
    @inbounds for i = 2:n
        j = 1 + rand(r, Random.ltm52(i, mask))
        exchange!(i, j)
        i == 1 + mask && (mask = 2 * mask + 1)
    end
end

```

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [June 30, 2025, 2:19pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/9 "2025-06-30T14:19:39Z")

</div>

Ah, sure, looks like passing a curried `swapcols!` as the `exchange!` function arg would do the ticket.

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [June 30, 2025, 4:36pm UTC](https://discourse.julialang.org/t/shuffle-columns-of-a-matrix/130334/10 "2025-06-30T16:36:22Z")

</div>

> [@Tamas\_Papp](#):
>
> but I don’t understand what `mask` does in the code.

I think this is a leftover from ancient times where people used RNGs with non-native word-size (ie mersenne twister, optimized for generating Float64). Back then, people used to do all kinds of misguided counter-productive contortions in order to save some cycles.

I’m saying misguided because our `shuffle!` fisher-yates ends up using naive rejection-sampling instead of the nice new fancy multiplication-based technique to generate numbers in a range. Lo and behold

```julia
julia> function baseshuffle!(r::Random.AbstractRNG, a::AbstractArray)
           # keep it consistent with `randperm!` and `randcycle!` if possible
           Base.require_one_based_indexing(a)
           n = length(a)
           @assert n <= Int64(2)^52
           n == 0 && return a
           mask = 3
           @inbounds for i = 2:n
               j = 1 + rand(r, Random.ltm52(i, mask))
               a[i], a[j] = a[j], a[i]
               i == 1 + mask && (mask = 2 * mask + 1)
           end
           return a
       end

julia> function simpleshuffle!(r::Random.AbstractRNG, a::AbstractArray)
           # keep it consistent with `randperm!` and `randcycle!` if possible
           Base.require_one_based_indexing(a)
           n = length(a)
           n == 0 && return a
           @inbounds for i = 2:n
               j = rand(r, 1:i)
               a[i], a[j] = a[j], a[i]
           end
           return a
       end

julia> r=Random.default_rng(); b = collect(1:10_000); @btime Random.shuffle!(b); @btime baseshuffle!(r, b); @btime simpleshuffle!(r,b);
  53.749 μs (0 allocations: 0 bytes)
  53.483 μs (0 allocations: 0 bytes)
  18.988 μs (0 allocations: 0 bytes)

```

We should simplify that code.

(not throwing shade on the julia devs, instead throwing shade on what passed for scientific consensus on randomness in the late 90s. Also hardware-dependent – the tradeoff would look different on a late 90s CPU with shallow pipeline and slow multiplication)
