AlgoVerse

ضرب زنجیره‌ای ماتریس‌ها

بهترین پرانتزگذاری برای ضرب چند ماتریس را با برنامه‌ریزی پویا پیدا می‌کند؛ ترتیب ضرب‌ها تعداد ضرب‌های عددی را تا چند برابر عوض می‌کند.

ضرب ماتریس p×q در q×r به p·q·r ضرب عددی نیاز دارد. ضرب ماتریسی شرکت‌پذیر است، پس A1·A2·…·An را با هر پرانتزگذاری می‌شود حساب کرد؛ ولی هزینه‌ها خیلی فرق می‌کنند.

ادامه‌ی توضیح ↓
ورودی:
n=7خروجی PDF

شش ماتریس با ابعاد ۳۰، ۳۵، ۱۵، ۵، ۱۰، ۲۰، ۲۵؛ کمترین هزینه ۱۵۱۲۵ با ((A1(A2A3))((A4A5)A6)).

  1. INF = float("inf")
  2. def matrix_chain(p):
  3. # Matrix k (1-based) is p[k-1] × p[k]. "-" marks a cell not computed yet.
  4. n = len(p) - 1
  5. if n < 1:
  6. return None
  7. m = [["-"] * n for _ in range(n)]
  8. s = [["-"] * n for _ in range(n)]
  9. for i in range(n):
  10. m[i][i] = 0
  11. for length in range(2, n + 1):
  12. for i in range(n - length + 1):
  13. j = i + length - 1
  14. best = INF
  15. for k in range(i, j):
  16. cost = m[i][k] + m[k + 1][j] + p[i] * p[k + 1] * p[j + 1]
  17. if cost < best:
  18. best = cost
  19. s[i][j] = k + 1
  20. m[i][j] = best
  21. order = parens(s, 0, n - 1)
  22. return m[0][n - 1], order
  23. def parens(s, i, j):
  24. if i == j:
  25. return "A" + str(i + 1)
  26. k = s[i][j]
  27. return "(" + parens(s, i, k - 1) + parens(s, k, j) + ")"

ناوردا: وقتی m[i][j] حساب می‌شود، همه‌ی زنجیره‌های کوتاه‌تر از قبل حل شده‌اند؛ هر زیرزنجیره‌ی یک جواب بهینه، خودش بهینه است.

شروع اجرا

best—

m

جدول هنوز خالی است.

s

جدول هنوز خالی است.

درخت فراخوانی‌های بازگشتی

نارنجی پر = فراخوانی در حال اجرا · حاشیه‌ی نارنجی = هنوز روی پشته · خاکستری = تمام شده

متغیرهای محلی

نارنجی = در همین گام عوض شد

p list

3035155102025

Call stack

  1. matrix_chain()خط 4
گام ۱ از ۲۹۷
خط سیاه = cell · خط نارنجی = گام کلیدیtry_split: 0/35 · cell: 0/15 · order: 0/1
    توضیح

    ایده

    ضرب ماتریس p×q در q×r به p·q·r ضرب عددی نیاز دارد. ضرب ماتریسی شرکت‌پذیر است، پس A1·A2·…·An را با هر پرانتزگذاری می‌شود حساب کرد؛ ولی هزینه‌ها خیلی فرق می‌کنند.

    برنامه‌ریزی پویا

    m[i][j] کمترین هزینه‌ی ضرب Ai … Aj است. آخرین ضرب در جایی مثل k زنجیره را دو تکه می‌کند:

    m[i][j] = min ( m[i][k] + m[k+1][j] + p[i−1]·p[k]·p[j] )   برای i ≤ k < j
    

    زنجیره‌ها را به ترتیب طول پر می‌کنیم تا تکه‌های کوتاه‌تر آماده باشند. s[i][j] بهترین k را نگه می‌دارد و از روی آن پرانتزگذاری ساخته می‌شود.

    هزینه

    O(n²) خانه، هر کدام O(n) انتخاب: O(n³)؛ در برابر تعداد نمایی پرانتزگذاری‌ها (عدد کاتالان).

    در trace به چه چیزی نگاه کنی

    • جدول m قطر به قطر پر می‌شود و s جای بهترین شکست را نشان می‌دهد. شماره‌ها در جدول از ۰ شروع می‌شوند و در برچسب‌ها از ۱.