Как я могу перетасовать диапазон чисел, а затем разбить его на подмассивы определенной длины? - PullRequest
4 голосов
/ 06 марта 2020

Предположим, у меня есть диапазон 1:N, я хочу перетасовать диапазон в случайный порядок, а затем разбить получившийся перетасованный массив на подмассивы, длина которых не превышает 128 элементов. Как я могу это сделать?

Этот вопрос основан на вопросе, который появился на слабом канале JuliaLang.

Ответы [ 3 ]

4 голосов
/ 06 марта 2020

Функция shuffle из стандартной библиотеки Random может использоваться для перемешивания контейнера в случайном порядке:

julia> using Random: shuffle

julia> shuffle(1:10)
10-element Array{Int64,1}:
  6
  9
  3
  2
 10
  1
  8
  7
  5
  4

Функция Iterators.partition из Base Джулии может использоваться для итерации. для итерируемой порции фиксированной длины:

julia> using Base.Iterators: partition

julia> partition(1:20, 7)
Base.Iterators.PartitionIterator{UnitRange{Int64}}(1:20, 7)

Однако partition возвращает ленивый итератор по умолчанию, поэтому, если мы хотим реализовать фактический результат, нам нужно collect it:

julia> collect(partition(1:20, 7))
3-element Array{UnitRange{Int64},1}:
 1:7
 8:14
 15:20

Собрав все это вместе, мы получим

julia> using Random: shuffle

julia> using Base.Iterators: partition

julia> shuffle_partition(N; chunk_size=128) = (collect ∘ partition)(shuffle(1:N), chunk_size)
shuffle_partition (generic function with 1 method)

julia> shuffle_partition(503)
4-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
 [313, 51, 117, 373, 381, 340, 342, 415, 423, 453  …  201, 178, 167, 242, 2, 76, 146, 439, 363, 448]
 [115, 121, 306, 440, 295, 181, 30, 280, 388, 227  …  362, 39, 317, 171, 55, 214, 261, 251, 96, 9]
 [486, 248, 161, 319, 325, 176, 80, 369, 434, 209  …  442, 350, 273, 419, 130, 305, 192, 482, 265, 234]
 [460, 31, 400, 466, 220, 447, 119, 446, 198, 141  …  226, 438, 74, 152, 203, 303, 378, 231, 458, 194]

julia> length.(ans)
4-element Array{Int64,1}:
 128
 128
 128
 119

Этот ответ основан на ответе, найденном в Slack.

2 голосов
/ 06 марта 2020

с использованием итераторов: я думаю, что наиболее простым является использование randperm (если значения находятся в диапазоне от 1 до N), поэтому

using Base.Iterators: partition
using Random: randperm

N = 513
k = 128

collect(partition(randperm(N), k))

должно работать.

1 голос
/ 06 марта 2020
parts = view.(Ref(shuffle(1:N)),(i:min(i+k-1, N) for i in 1:k:N))

Предполагается, что N - это количество элементов, а размер раздела равен k. Полученный результат представляет собой список просмотров (следовательно, перемешанное 1: N сохраняется только один раз в памяти). Обратите внимание, как Ref используется, чтобы избежать векторизации в перетасованном списке.

Пример тестового кода:

julia> using Random

julia> N, k = 20, 4;

julia> parts = view.(Ref(shuffle(1:N)),(i:min(i+k-1, N) for i in 1:k:N))
5-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
 [18, 15, 1, 6]
 [10, 20, 4, 14]
 [17, 9, 19, 16]
 [5, 8, 12, 3]
 [11, 13, 2, 7]
...