Итерация через динамическое число циклов for (Python)

Я использую python для последовательности некоторых чисел. Я хотел бы создать функцию, которая позволяет мне вводить значение (4, 8, 16, 32, 64, etc.), создайте массив чисел и измените их последовательность.

Я добавил цифры, которые подробно описывают, как определить последовательность для value = 4 и 8.

для значения = 4 массив (x = [0, 1, 2, 3]) должен быть разделен на два ([0,1] и [2,3]), а затем объединен на основе первого числа в каждом массиве ([0, 2 ,1 ,3]).

Figure with sequence for value = 4

для значения = 8 массив (x = [0, 1, 2, 3, 4, 5, 6, 7]) следует разделить на две части ([0, 1, 2, 3] и [4, 5, 6, 7]). Оба массива следует снова разделить на два([0, 1, 2, 3] в [0,1] и [2,3] и [4, 5, 6, 7] в [4,5] и [6,7]). Затем массивы должны быть объединены на основе первого числа в каждом массиве и последовательности 2-го набора массивов ([0, 4, 2, 6, 1, 5, 3, 7]).

Figure for sequence for value = 8

Я не знаю как обрабатывать рекурсия (динамически вложенные для циклов). Я пытаюсь выполнить цикл через каждый brach, который создается путем разбиения массива. Я просмотрел itertools и рекурсию (функция с различным количеством циклов For (python)), но я не мог заставить его работать. Ниже я добавил код, чтобы проиллюстрировать свой подход до сих пор.

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

Я использую python 2.7.6 и и NumPy.

код:

import numpy
value = 4
a = []
x = numpy.arange(value)
y = numpy.array_split(x, 2)
for i in range(2):
    for j in y:
        a.append(j.tolist()[i])
print(a)

выход:

[0, 2, 1, 3]

код:

import numpy
value = 8
a = []
x = numpy.arange(value)
y = numpy.array_split(x, 2)
for i in range(2):
    for h in range(2):
        for j in y:
        z = numpy.array_split(j, 2)
                a.append(z[h][i])
    print(a)

выход:

[0, 4, 2, 6, 1, 5, 3, 7]

выходное значение = 16 должно быть [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11 ,7 15].

3 ответов


вот Нумпитонный способ использования np.transpose и reshaping -

def seq_pow2(N):
    shp = 2*np.ones(np.log2(N),dtype=int)
    return np.arange(N).reshape(shp).transpose(np.arange(len(shp))[::-1]).ravel()

обратите внимание:.transpose(np.arange(len(shp))[::-1] упростил бы до .T, поэтому у нас будет упрощенная версия -

def seq_pow2(N):
    shp = 2*np.ones(np.log2(N),dtype=int)
    return np.arange(N).reshape(shp).T.ravel()

вы можете еще больше упростить и заменить транспонирование в целом, выполнив ravel/flattening в колонке-основной порядок, как в fortran С .ravel('F') чтобы привести нас, наконец -

def seq_pow2(N):
    shp = 2*np.ones(np.log2(N),dtype=int)
    return np.arange(N).reshape(shp).ravel('F')

пример работает -

In [43]: seq_pow2(4)
Out[43]: array([0, 2, 1, 3])

In [44]: seq_pow2(8)
Out[44]: array([0, 4, 2, 6, 1, 5, 3, 7])

In [45]: seq_pow2(16)
Out[45]: array([ 0,  8,  4, 12,  2, 10,  6, 14,  1,  9,  5, 13,  3, 11,  7, 15])

рекурсивная версия python для ясности:

def rec(n):
    if n==1 : return [0]
    l=[0]*n
    l[::2]=rec(n//2)
    for i in range (0,n,2) : l[i+1]=l[i]+n//2
    return l

на

In [6]: rec(16)
Out[6]: [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]

или, наблюдая двоичное представление результата, решение numpy:

def rearange(N):
    u=2**arange(N.bit_length()-1)
    v=arange(N)
    bits= u[None,:] & v[:,None]
    return sum(bits*u[::-1],1)

самый простой способ сделать это -не используйте для циклов, но для некоторых манипуляций с массивом с numpy.

N = 8
pow2 = np.log2(N)
out = np.arange(N).reshape([2]*pow2).transpose(np.arange(pow2)[::-1]).flatten()   

   array([0, 4, 2, 6, 1, 5, 3, 7])

это перекраивает x на n-мерный массив, где n сила 2, что соответствует длине x. После этого переформования длина каждого измерения равна 2. Затем мы переворачиваем все размеры и выравниваем, чтобы получить нужный массив.

редактировать

Это аналогичный подход к Divakar это, и он закончил это гораздо более кратко, но я просто оставлю это здесь для потомков.