Самый быстрый способ заполнить массив? - PullRequest
3 голосов
/ 23 мая 2019

Я хочу заполнить массив размером n следующим образом (очень большой массив):

1 for i = 1, (n/2)+1
2 for i = 2, 3, ... , (n/2)
0 for i = (n/2)+2, ... , n

Является ли самый быстрый способ итерации от 0 до n с использованием оператора if и% для каждого?

Как это:

array = []
for index in range(1,n):
    if index == 1 or (index % ((n/2)+1) == 0):
        array.append(1)
    if index == 2 or index == 3 or (index % (n/2) == 0):
        array.append(2)
    if (index % ((n/2)+2) == 0):
        array.append(0)

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

Ответы [ 4 ]

1 голос
/ 24 мая 2019

для четных n:

def generate_array2(n):
    a = np.empty((2,n//2))
    a[0]=2
    a[1]=0
    a[:,0]=1
    return a.ravel()

%timeit a= generate_array2(1_000_000)
491 ± 6.58 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1 голос
/ 23 мая 2019

Так как другие ответы шли с пустыми массивами, которые довольно быстрые, я хочу добавить некоторые другие ответы для полноты. (Очевидно, в первоначальном вопросе есть некоторая двусмысленность относительно того, что делать, если n нечетно, и ожидал ли автор индексации на основе нуля или на основе единицы.)

Вы можете создать список довольно просто, используя понимание списка

[1 if i in [0, n/2] else (2 if i <=(n/2) else 0) for i in range(0, n)]

Но гораздо быстрее расширение функциональности списка путем расширения класса:

class NewThing(list):
    def __init__(self, n):
        self.n = n
    def __getitem__(self,i):
        if i in {0,n/2}:
            return 1
        elif i <=n/2:
            return 2
        return 0
    def to_list(self):
        return [self[i] for i in range(0, n)]

>>>unique_list = NewThing(len(u))
>>>unique_list[4]
2
>>>unique_list.to_list()
[1, 2, 2, 2, 2, 1, 0, 0, 0, 0]

Вы можете вызвать весь список или только отдельные элементы. Создание всего списка все еще идет медленно.

1 голос
/ 23 мая 2019

Самый эффективный способ создать такую ​​коллекцию - использовать массив numpy.

Мы можем построить это с:

import numpy as np

def generate_array(n):
    a = np.hstack((np.full(n//2+1, 2), np.zeros(n//2-1)))
    a[[0, n//2]] = 1
    return a

Например:

>>> generate_array(6)
array([1., 2., 2., 1., 0., 0.])
>>> generate_array(10)
array([1., 2., 2., 2., 2., 1., 0., 0., 0., 0.])
>>> generate_array(16)
array([1., 2., 2., 2., 2., 2., 2., 2., 1., 0., 0., 0., 0., 0., 0., 0.])

Задержка

для n=100_000, получаем:

>>> timeit(partial(generate_array, 100_000), number=10_000)
1.0764452270013862

для n=1_000_000, получаем:

>>> timeit(partial(generate_array, 1_000_000), number=1_000)
6.448311180000019

Генерация массива из 100 тыс. Элементов, таким образом, занимает приблизительно (включая небольшие накладные расходы partial и т. Д.) .

1 голос
/ 23 мая 2019

Вы можете использовать numpy для этого. Вы можете использовать np.r_ для объединения нескольких срезов и использовать их для назначения массива:

import numpy as np

n = 10
a = np.zeros(n)
a[np.r_[0,n//2]] = 1
a[np.r_[1:n//2]] = 2
a[np.r_[n//2+1:n]] = 0

print(a)
array([1., 2., 2., 2., 2., 1., 0., 0., 0., 0.])
...