Построение массива numpy как функция предыдущего элемента - PullRequest
0 голосов
/ 25 апреля 2020

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

import numpy as np

def build_array_recursively(length, V_0, function):
    returnList = np.empty(length)
    returnList[0] = V_0
    for i in range(1,length):
        returnList[i] = function(returnList[i-1])
    return returnList

d_t = 0.05
print(build_array_recursively(20, 0.3, lambda x: x-x*d_t+x*x/2*d_t*d_t-x*x*x/6*d_t*d_t*d_t))

Метод печати выше выводит

[0.3 0.28511194 0.27095747 0.25750095 0.24470843 0.23254756 0.22098752
 0.20999896 0.19955394 0.18962586 0.18018937 0.17122037 0.16269589
 0.15459409 0.14689418 0.13957638 0.13262186 0.1260127 0.11973187 0.11376316]

Есть ли быстрый способ сделать это в numpy без for для l oop? Если да, есть ли способ обработать два элемента перед текущим, например, может ли массив Фибоначчи быть построен аналогично? Я нашел подобный вопрос здесь

Возможно ли векторизовать рекурсивное вычисление массива NumPy, где каждый элемент зависит от предыдущего?

, но не был дан ответ В основном. В моем примере уравнение разностей трудно решить вручную.

Ответы [ 2 ]

0 голосов
/ 25 апреля 2020

Использование функции для уточнения кода (вместо встроенной лямбды):

def fn(x):
    return x-x*d_t+x*x/2*d_t*d_t-x*x*x/6*d_t*d_t*d_t

И функция, объединяющая элементы build_array_recursively и method2:

def foo1(length, V_0, function):
    returnList = np.empty(length)
    returnList[0] = x = V_0
    for i in range(1,length):
        returnList[i] = x = function(x)
    return returnList

In [887]: timeit build_array_recursively(20,0.3, fn);                                                                     
61.4 µs ± 63 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [888]: timeit method2(20,0.3, fn);                                                                  
16.9 µs ± 103 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [889]: timeit foo1(20,0.3, fn);                                                                     
13 µs ± 29.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Основная экономия времени в method2 и foo2 переносит x, последнее значение, с одной итерации на следующую, вместо индексации с помощью returnList[i-1].

Метод накопления, назначение предварительно выделенного массива или добавления в список менее важно. Производительность обычно одинакова.

Здесь вычисления достаточно просты, поэтому детали того, что вы делаете в l oop, сильно влияют на общее время.

Все это циклы. В некоторых ufunc есть метод reduceaccumulate), который может неоднократно применять функцию к элементам входного массива. np.sum, np.cumsum, et c используйте это. Но вы не можете сделать это с помощью общей функции Python.

Вы должны использовать какой-нибудь инструмент компиляции, например numba, чтобы выполнить l oop намного быстрее.

0 голосов
/ 25 апреля 2020

Это быстрее для того, что вы хотите сделать. Вам не нужно использовать рекурсию для функции.

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

def method2(length, V_0, d_t):
    k = [V_0]
    x = V_0
    for i in range(1, length):
        x = x - x * d_t + x * x / 2 * d_t * d_t - x * x * x / 6 * d_t * d_t * d_t
        k.append(x)
    return np.asarray(k)

print(method2(20,0.3, 0.05))

. Выполнение существующего метода 10000 раз занимает 0,438 секунды, а method2 - 0,097 секунды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...