Есть массивы с точками:
X[0.0, 0.0, 0.0, 0.0],Y[0.0, 0.3333, 0.6667, 1.0],
kX[0.0, 0.0, 0.0],kY[0.1667, 0.5, 0.8333]
Нужно сформировать двумерные массивы R2, U и V размера(3,4), элементы которых считаются по формулам:
r2 = math.sqrt(math.pow((kX[k]-X[i]), 2) + math.pow((kY[k]-Y[i] ) , 2))
u = 1 / (2 * math.pi) * ((kY[k]-Y[i]) / math.pow(r2[k][i], 2))
v = - 1 / (2 * math.pi) * ((kX[k]-X[i]) / math.pow(r2[k][i], 2))
Поскольку я новичек, меня особенно интересует подробное описание следующего кода
###псевдокод####
r2 = math.sqrt(math.pow((kX[k]-X[i]), 2) + math.pow((kY[k]-Y[i] ) , 2))
### реализация на питоне ####
r2 = [[(cp.y - p.y)**2 + (cp.x - p.x)**2 for p in points]
for cp in control_points]
где используется collections.namedtuple и вообще как обращаться к элементам массивов без использования циклов, желательно на этом примере.
Случай с точками двух массивов понятен:
u = [[1 / (2 * math.pi)*(kx - x) for x in X] for kx in kX]
v = [[1 / (2 * math.pi)*(ky - y) for y in Y] for ky in kY]
А как быть, если нужно:
u = [[1 / (2 * math.pi)*(kx - x) for x in X] for kx in kX] / r2[k][i]
v = [[1 / (2 * math.pi)*(ky - y) for y in Y] for ky in kY] / r2[k][i]
Ответ
Если вы хотите использовать X, Y, kX, kY как Python-списки напрямую:
import math
r2 = [[((kx - x)**2 + (ky - y)**2)**.5 for x, y in zip(X, Y)]
for kx, ky in zip(kX, kY)]
u = [[(kx - x) / (2 * math.pi * r2ki) for x, r2ki in zip(X, r2k)]
for kx, r2k in zip(kX, r2)]
v = [[(ky - y) / (2 * math.pi * r2ki) for y, r2ki in zip(Y, r2k)]
for ky, r2k in zip(kY, r2)]
zip() это встроенная в Питон функция:
>>> list(zip(range(3), "abc"))
[(0, 'a'), (1, 'b'), (2, 'c')]
Происхождение названия от zipper—застёжка-молния: zip() "сшивает" переданные аргументы.
Point2D (collections.namedtuple)
Если:
points = list(map(Point2D, X, Y))
control_points = list(map(Point2D, kX, kY))
тогда код для r2 идентичен уже приведённому решению (c точностью до вызова sqrt):
r2 = [[((cp.y - p.y)**2 + (cp.x - p.x)**2)**.5 for p in points]
for cp in control_points]
Пример:
>>> import collections
>>> Point2D = collections.namedtuple('Point2D', 'x y')
>>> X, Y = [0.0, 0.0, 0.0, 0.0], [0.0, 0.3333, 0.6667, 1.0]
>>> points = list(map(Point2D, X, Y))
>>> points == [Point2D(x, y) for x, y in zip(X, Y)]
True
>>> points
[Point2D(x=0.0, y=0.0), Point2D(x=0.0, y=0.3333),
Point2D(x=0.0, y=0.6667), Point2D(x=0.0, y=1.0)]
>>> [p.y for p in points]
[0.0, 0.3333, 0.6667, 1.0]
numpy
Eсли работать с X, Y, kX, kY как с numpy-массивами:
>>> import numpy as np
>>> X, Y, kX, kY = map(np.array, [X, Y, kX, kY])
>>> r2 = ((kX[:,None] - X[None,:])**2 + (kY[:,None] - Y[None,:])**2)**.5
>>> r2
array([[ 0.1667, 0.1666, 0.5 , 0.8333],
[ 0.5 , 0.1667, 0.1667, 0.5 ],
[ 0.8333, 0.5 , 0.1666, 0.1667]])
>>> u = (kX[:,None] - X[None,:]) / (2 * math.pi * r2)
>>> v = (kY[:,None] - Y[None,:]) / (2 * math.pi * r2)
>>> v
array([[ 0.15915494, -0.15915494, -0.15915494, -0.15915494],
[ 0.15915494, 0.15915494, -0.15915494, -0.15915494],
[ 0.15915494, 0.15915494, 0.15915494, -0.15915494]])
Здесь индексация с None добавляет новую размерность (из одномерного массива создаёт двумерный):
>>> kY
array([ 0.1667, 0.5 , 0.8333])
>>> kY[:,None]
array([[ 0.1667],
[ 0.5 ],
[ 0.8333]])