Cython avec des tableaux de longueur variable
Les baies à croissance dynamique sont un type de baie. Ils sont très utiles lorsque vous ne connaissez pas la taille exacte du tableau au moment de la conception. Vous devez d'abord définir un nombre initial d'éléments. ( Wikipédia )
J'ai écrit une solution Python et l'ai convertie en Cython . Cython peut être utilisé pour améliorer la vitesse des boucles for imbriquées en Python. Où mon code Cython est légèrement plus rapide. Ma solution Cython n'est évidemment pas la plus rapide. J'essaie d'effectuer une boucle for imbriquée similaire à celle de mon code Python aussi vite que possible en Cython.
Il serait utile d'avoir une certaine expérience en C, ce que je n'ai pas. Le principal problème que j'ai rencontré est que Cython a des règles de portée différentes de Python. Puisque C et Python ont des règles de portée différentes. En d'autres termes, nous ne pouvons pas créer un nouveau vecteur dans la boucle et lui attribuer le même nom.
Ma solution fonctionne mais est trop lente. Quelqu'un peut-il améliorer le code Cython ci-dessus en utilisant une approche plus semblable au C.
Python
import numpy as np
my_list = [1,2,3]
n = 10
a = 0.5
Estimate_1_list = []
Estimate_2_list = []
for l in my_list:
# Resizable matrices
a_mat = np.zeros((l,n+1),float)
b_mat = np.zeros((l,n+1),float)
for i in range(n):
t = i*a
for j in range(l):
# Fill matrices
a_mat[j,i+1] = a_mat[j,i+1] + np.random.random()
b_mat[j,i+1] = a_mat[j,i+1]/(2*t+3)
# Append values of interest to use at different values of matrix size
Estimate_1_list.append(np.mean(a_mat[:,n]))
Estimate_2_list.append(np.std(a_mat[:,n]))
results = [Estimate_1_list,Estimate_2_list]
Cython
import cython
# Load cython extension
%load_ext Cython
%%cython
import numpy as np
def my_function(list my_list, int n, int a ):
cdef list Estimate_1_list = []
cdef list Estimate_2_list = []
cdef int l,i,t,j
for l in my_list:
# Resizable matrices (could I use memory view?)
a_mat = np.zeros((l,n+1),float)
b_mat = np.zeros((l,n+1),float)
for i in range(n):
t = i*a
for j in range(l):
# Fill matrices
a_mat[j,i+1] = a_mat[j,i+1] + np.random.random()
b_mat[j,i+1] = a_mat[j,i+1]/(2*t+3)
# Append values of interest to use at different values of matrix size
Estimate_1_list.append(np.mean(a_mat[:,n]))
Estimate_2_list.append(np.std(a_mat[:,n]))
# Return results
results = [Estimate_1_list,Estimate_2_list]
return results
Des tests
# Test cython to show that the function is running
my_list = [1,2,3]
n = 10
a = 0.5
my_function(my_list, n, a)
[[0.13545224609230933, 0.6603542545719762, 0.6632002117071227],
[0.0, 0.19967544614685195, 0.22125180486616808]]
Réponses
Zéros
Cette:
a_mat = np.zeros
n'est pas le bon appel pour votre objectif. Vous voulez à la np.emptyplace, car vous ne vous souciez pas réellement des valeurs initiales puisque vous effectuez une boucle d'initialisation complète juste après.
De plus: puisque vous ajoutez random()à chaque élément de a_mat, initialisez simplement a_matà un seul appel de random()avec la forme correcte, plutôt que d'avoir à faire une addition par élément.
Vectorisation
Vous avez une dimension extérieure (à ltravers my_list), une deuxième dimension ( l) et une troisième dimension ( n + 1). La deuxième dimension est variable; le premier et le troisième sont constants. Cela signifie que vous pouvez représenter cela plus efficacement si vous réorganisez vos dimensions de sorte que les dimensions fixes soient à l'intérieur. En d'autres termes, si vous aviez
len(my_list) = 3
l = 1, 2, 3
n = 10
alors vous pouvez réellement représenter cela comme une seule matrice tridimensionnelle de dimensions 6 * 3 * 10, où 6 == len(my_list) * (len(my_list) - 1). Je pense qu'il est possible de faire tout cela sans une seule forboucle, ce qui est l'idéal pour des performances vectorisées.