جدول أرقام يعمل على المتجهات
المصفوفة جدول مستطيل من الأرقام. نخزّنها في بايثون كقائمة صفوف، فـ[[1, 2], [3, 4]] مصفوفة 2×2 وتقرأ m[الصف][العمود] مدخلة واحدة. وتصف المصفوفات صفوفًا كثيرة من النوع نفسه من الحساب دفعة واحدة. فإذا كان كل صف يحمل أوزان محفظة وكان المتجه عوائد الأصول في اليوم فإن حاصل ضرب المصفوفة في المتجه هو عائد كل محفظة في خطوة واحدة. وكل مدخلة في الناتج هي الجداء النقطي لصف مع المتجه، فيجب أن يساوي طول الصف طول المتجه.
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def mat_vec(matrix, vector):
return [dot(row, vector) for row in matrix]
print(mat_vec([[1, 2], [3, 4]], [5, 6])) # [17, 39]
# Two portfolios, three assets, one day of returns
weights = [[0.5, 0.3, 0.2],
[0.2, 0.5, 0.3]]
returns = [0.02, -0.01, 0.03]
print([round(r, 4) for r in mat_vec(weights, returns)]) # [0.013, 0.008]المنقول وضرب المصفوفات
المنقول يبدّل الصفوف والأعمدة: تصبح المدخلة (i, j) هي (j, i)، فتتحول مصفوفة 2×3 إلى 3×2. وفي بايثون تفعل ذلك [list(column) for column in zip(*m)]. وحاصل الضرب A @ B لمصفوفة n×k ومصفوفة k×m هو مصفوفة n×m مدخلتها (i, j) هي الجداء النقطي للصف i من A مع العمود j من B. وأعمدة B هي صفوف منقولها، مما يقصّر الكود. ويجب أن تتطابق الأبعاد الداخلية (k وk)، وعلى خلاف الأعداد يهم الترتيب: ففي العموم A @ B ليست B @ A.
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def transpose(m):
return [list(column) for column in zip(*m)]
def mat_mul(a, b):
columns = transpose(b)
return [[dot(row, column) for column in columns] for row in a]
print(transpose([[1, 2, 3], [4, 5, 6]])) # [[1, 4], [2, 5], [3, 6]]
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
print(mat_mul(A, B)) # [[19, 22], [43, 50]]
print(mat_mul(B, A)) # [[23, 34], [31, 46]] (a different answer: order matters)حل نظام خطي
كثير من الأسئلة لها الصيغة: أوجد x بحيث A @ x = b. كم يلزم من كل أداة من أداتي تحوّط لإلغاء تعرّض معيّن؟ وأي أحمال للعوامل تعيد إنتاج العوائد المرصودة؟ هذا نظام خطي. وتحلّه طريقة الحذف لغاوس بتحويل المعادلات إلى مثلث: لكل عمود نختار الصف ذا أكبر قيمة مطلقة في ذلك العمود محورًا (ونرفعه إلى أعلى مما يُبقي أخطاء التقريب صغيرة)، ونطرح مضاعفاته من الصفوف التي تحته لنصنع أصفارًا، ثم نحل صعودًا بـالتعويض العكسي. وإذا كان المحور (شبه) صفر فالصفوف معتمدة بعضها على بعض والمصفوفة منفردة (singular) فلا جواب وحيد: ارفع خطأً بدل القسمة على صفر.
def solve(a, b):
n = len(a)
m = [list(map(float, row)) + [float(b[i])] for i, row in enumerate(a)] # augmented matrix
for col in range(n):
pivot = max(range(col, n), key=lambda r: abs(m[r][col]))
if abs(m[pivot][col]) < 1e-12:
raise ValueError("the matrix is singular")
m[col], m[pivot] = m[pivot], m[col]
for r in range(col + 1, n):
factor = m[r][col] / m[col][col]
for c in range(col, n + 1):
m[r][c] -= factor * m[col][c]
x = [0.0] * n
for i in range(n - 1, -1, -1): # back-substitution
known = sum(m[i][j] * x[j] for j in range(i + 1, n))
x[i] = (m[i][n] - known) / m[i][i]
return x
print(solve([[2, 1], [1, 3]], [3, 5])) # [0.8, 1.4]
print(solve([[1, 1, 1], [0, 2, 5], [2, 5, -1]], [6, -4, 27])) # [5.0, 3.0, -2.0]
try:
solve([[1, 2], [2, 4]], [1, 2]) # the second row is twice the first
except ValueError as error:
print(error) # the matrix is singularimport math
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def mat_vec(matrix, vector):
return [dot(row, vector) for row in matrix]
# Check a solution by multiplying back: A x should give b again
A = [[2, 1], [1, 3]]
b = [3, 5]
x = [0.8, 1.4] # the answer from solve(A, b)
residual = [got - want for got, want in zip(mat_vec(A, x), b)]
print([abs(round(r, 12)) for r in residual]) # [0.0, 0.0] (tiny rounding errors vanish)
print(all(math.isclose(g, w) for g, w in zip(mat_vec(A, x), b))) # True
# The identity matrix leaves any vector unchanged
identity = [[1, 0], [0, 1]]
print(mat_vec(identity, [7, 9])) # [7, 9]x = A⁻¹ b، لكن في الكود لا تحسب المعكوس تقريبًا أبدًا: فالحذف المباشر أسرع وأدق. وتحقق أيضًا من الجواب بضربه من جديد (يجب أن تعيد A @ x القيمة b حتى حدود التقريب)، واعتبر المصفوفة شبه المنفردة، كأصلين شبه تامّي الارتباط، تحذيرًا من أن الحل سيكون غير مستقر. وستستخدم لاحقًا numpy.linalg.solve التي تؤدي العمل نفسه بكود مُترجَم.- المصفوفة قائمة صفوف؛ و
mat_vecتأخذ الجداء النقطي لكل صف مع المتجه. - المنقول بـ
zip(*m)؛ وA @ Bالمدخلة (i, j) = صف i من A نقطي عمود j من B؛ والترتيب مهم. - حُلّ
A x = bبالحذف مع المحور والتعويض العكسي؛ والمحور (شبه) الصفري يعني مصفوفة منفردة.
التالي: المتغيرات العشوائية والتوقّع، لغة عدم اليقين وراء كل نموذج مخاطر وتسعير.
المختبر: ما الذي يُقيَّم
- طبّق مصفوفة على متجه باستخدام
mat_vec25 نقطة - بدّل الصفوف والأعمدة باستخدام
transpose25 نقطة - اضرب مصفوفتين باستخدام
mat_mul25 نقطة - حُلّ A x = b بالحذف باستخدام
solve25 نقطة