Одновременный доступ к массиву с несколькими элементами и перенос в начало

#arrays #iteration #julia

#массивы #итерация #джулия

Вопрос:

У меня есть простой вопрос, на который должен быть простой ответ, но я пока не могу его придумать. Я хочу обработать массив, определенное количество элементов за раз, и перенести в начало.

Вот диаграмма, показывающая, когда n требуется 10 и каждый раз требуется три элемента:

итерации по 3 за раз

Мои попытки написать простую итерацию до сих пор не увенчались успехом: использование % n дает мне нули, которые не работают с одноиндексацией Джулии … 🙂

Комментарии:

1. Хорошая диаграмма. Обычно перед выполнением % n вам нужно вычесть 1, а затем добавить 1 (напоминает преобразование базиса матрицы в линейной алгебре). Кроме того, существуют пакеты, которые позволяют индексировать массивы на основе 0 (и другие), см. OffsetArrays ( github.com/alsam/OffsetArrays.jl ).

Ответ №1:

mod1 Функция предоставляется для обеспечения желаемого поведения:

 julia> mod1(1, 5)
1

julia> mod1(3, 5)
3

julia> mod1(5, 5)
5

julia> mod1(6, 5)
1
  

Довольно просто создать мод-индексированную функцию:

 modindex(A, i) = A[mod1(i, length(A))]
  

Или даже ваш собственный мод-индексированный тип массива:

 julia> immutable CircArray{T} <: AbstractArray{T,1}
           xs::Vector{T}
       end

julia> Base.size(x::CircArray) = (length(x.xs),)

julia> Base.getindex(x::CircArray, i) = x.xs[mod1(i, length(x.xs))]

julia> A = CircArray([1:2:10;])
CircArray{Array{Int64,1}}([1,3,5,7,9])

julia> A[0]
9

julia> A[5]
9

julia> A[7]
3
  

Реализовать нарезку поверх этого не так уж сложно. Как упоминалось в комментарии DNF, чистое и краткое решение

 modindex(A, i) = A[mod1.(i, end)]
  

или эквивалент for getindex , который обрабатывает как скалярную индексацию, так и нарезку.


Редактировать: поскольку в вашем вопросе упоминается итерация, я полагаю, что я бы предложил более общее решение, которое также работает с не-массивами для целей итерации, используя только функциональные итерации в Base :

 julia> threes(A) = let cy = cycle(A)
           take(zip(cy, drop(cy, 1), drop(cy, 2)), length(A))
       end
threes (generic function with 1 method)

julia> for (a, b, c) in threes([1, 2, 3, 4, 5])
           println(a, b, c)
       end
123
234
345
451
512
  

Комментарии:

1. Чистым решением может быть modindex(A, i) = A[mod1.(i, end)] . Это очень кратко, и вы можете выполнять нарезку.

2. Отличный ответ, именно то, что мне было нужно. Спасибо.

3. В определении CircArray не следует ли T в суперклассе заменить параметр T ? (или параметр, преобразованный в eltype значение массива, с дополнительным Vector модификатором на xs )

Ответ №2:

Iterators.jl предоставит вам простой итератор для «одновременного доступа к массиву по нескольким элементам»:

 julia> for i in partition(1:15, 3, 1)
          @show i
       end
i = (1,2,3)
i = (2,3,4)
i = (3,4,5)
i = (4,5,6)
i = (5,6,7)
i = (6,7,8)
i = (7,8,9)
i = (8,9,10)
i = (9,10,11)
i = (10,11,12)
i = (11,12,13)
i = (12,13,14)
i = (13,14,15)
  

и, как предположил Фэньян Ван, mod1 функция выполняет задание «перенос в начало». просто создайте комбинацию:

 julia> for i in partition(1:15, 3, 1)
          @show mod1.(collect(i), 10)
       end
mod1.(collect(i),10) = [1,2,3]
mod1.(collect(i),10) = [2,3,4]
mod1.(collect(i),10) = [3,4,5]
mod1.(collect(i),10) = [4,5,6]
mod1.(collect(i),10) = [5,6,7]
mod1.(collect(i),10) = [6,7,8]
mod1.(collect(i),10) = [7,8,9]
mod1.(collect(i),10) = [8,9,10]
mod1.(collect(i),10) = [9,10,1]
mod1.(collect(i),10) = [10,1,2]
mod1.(collect(i),10) = [1,2,3]
mod1.(collect(i),10) = [2,3,4]
mod1.(collect(i),10) = [3,4,5]
  

Комментарии:

1. Вы также можете рассмотреть возможность использования cycle в итераторе, а затем take обработки результата. Я включил аналогичное решение в свой ответ.

Ответ №3:

Вы могли бы просто определить это самостоятельно? Нравится:

 a = repmat(collect(1:10)', 10)
sza = size(a,1) #here 10
for i in 1:sza
   toget = collect(i:i 2)
   toget[toget.>sza] -= sza
   println(a[i, toget])
end
[1,2,3]
[2,3,4]
[3,4,5]
[4,5,6]
[5,6,7]
[6,7,8]
[7,8,9]
[8,9,10]
[9,10,1]
[10,1,2]
  

Где println может быть все, что вы хотите?

Ответ №4:

Для

 a = [1,2,3,4,5]
  

выполните

  repmat(a',5,2)
 5x10 Array{Int64,2}:
  1  2  3  4  5  1  2  3  4  5
  1  2  3  4  5  1  2  3  4  5
  1  2  3  4  5  1  2  3  4  5
  1  2  3  4  5  1  2  3  4  5
  1  2  3  4  5  1  2  3  4  5
  

и затем

 map(i -> [a[i,i], a[i,i 1], a[i,i 2]], 1:5)
5-element Array{Array{Int64,1},1}:
[1,2,3]
[2,3,4]
[3,4,5]
[4,5,1]
[5,1,2]
  

Бонус

Если вы хотите перепроектировать это, или проблема с памятью (repmat не совсем эффективен с точки зрения памяти)

 s = size(a)[2]

l(i,v) = i   v > s ? i   v - s : i   v

map(i -> [a[i,i], a[i, l(i,1)], a[i, l(i,2)]], 1:5)