ضرب المصفوفات المتسلسلة
تُعدّ عملية ضرب المصفوفات المتسلسلة (أو مسألة ترتيب المصفوفات المتسلسلة [ 1 ] ) مسألة تحسين تتعلق بأكثر الطرق كفاءة لضرب سلسلة معينة من المصفوفات . لا تكمن المشكلة في إجراء عمليات الضرب نفسها، بل في تحديد ترتيب عمليات ضرب المصفوفات المطلوبة. ويمكن حل هذه المسألة باستخدام البرمجة الديناميكية .
توجد خيارات عديدة لأن ضرب المصفوفات عملية تجميعية . بمعنى آخر، بغض النظر عن كيفية وضع الأقواس في الناتج ، ستبقى النتيجة كما هي. على سبيل المثال، بالنسبة لأربع مصفوفات A و B و C و D ، توجد خمسة خيارات ممكنة:
- (( AB ) C ) D = ( A ( BC ) ) D = ( AB ) ( CD ) = A (( BC ) D ) = A ( B ( CD ) ).
على الرغم من أن ترتيب الحدود بين الأقواس لا يؤثر على الناتج، إلا أنه يؤثر على عدد العمليات الحسابية البسيطة اللازمة لحساب الناتج، أي على التعقيد الحسابي . يتطلب الضرب المباشر لمصفوفة من الرتبة X × Y في مصفوفة من الرتبة Y × Z عدد XYZ من عمليات الضرب العادية وعدد X ( Y - 1) Z من عمليات الجمع العادية. في هذا السياق، يُستخدم عادةً عدد عمليات الضرب العادية كمقياس لتعقيد وقت التشغيل.
إذا كانت A مصفوفة 10 × 30، و B مصفوفة 30 × 5، و C مصفوفة 5 × 60، فإن
- يتطلب حساب ( AB ) C ما يلي: (10×30×5) + (10×5×60) = 1500 + 3000 = 4500 عملية حسابية، بينما
- حساب A ( BC ) يتطلب (30×5×60) + (10×30×60) = 9000 + 18000 = 27000 عملية.
من الواضح أن الطريقة الأولى أكثر كفاءة. بناءً على هذه المعلومات، يمكن تحسين صياغة المسألة لتصبح: "كيفية تحديد أفضل طريقة لوضع الأقواس في حاصل ضرب n مصفوفة؟". يُعطى عدد طرق وضع الأقواس الممكنة بالعدد ( n -1) من أعداد كاتالان ، وهو Θ (4n / n³ / 2 ) . لذا، فإن فحص كل طريقة ممكنة ( باستخدام البحث الشامل ) يتطلب وقتًا تشغيليًا يتناسب أُسّيًا مع عدد المصفوفات، وهو أمر بطيء جدًا وغير عملي لقيم n الكبيرة . يمكن التوصل إلى حل أسرع لهذه المسألة بتقسيمها إلى مجموعة من المسائل الفرعية المترابطة.
خوارزمية البرمجة الديناميكية
لنفترض مبدئيًا أن كل ما نريد معرفته هو أقل تكلفة، أو أقل عدد من العمليات الحسابية اللازمة لضرب المصفوفات. إذا كنا نضرب مصفوفتين فقط، فلا توجد إلا طريقة واحدة لضربهما، وبالتالي فإن أقل تكلفة هي تكلفة هذه العملية. بشكل عام، يمكننا إيجاد أقل تكلفة باستخدام الخوارزمية التكرارية التالية :
- خذ سلسلة المصفوفات وافصلها إلى سلسلتين فرعيتين.
- أوجد أقل تكلفة لضرب كل متتالية جزئية .
- اجمع هذه التكاليف معًا، وأضف إليها تكلفة ضرب مصفوفتي النتائج.
- قم بذلك لكل موضع ممكن يمكن عنده تقسيم سلسلة المصفوفات، واحصل على الحد الأدنى على جميعها.
على سبيل المثال، إذا كان لدينا أربع مصفوفات ABCD ، فإننا نحسب التكلفة اللازمة لإيجاد كل من ( A ) ( BCD ) ، و( AB ) ( CD ) ، و( ABC ) ( D ) ، وذلك بإجراء عمليات متكررة لإيجاد أقل تكلفة لحساب ABC ، و AB ، وCD ، و BCD . ثم نختار الأفضل. والأفضل من ذلك، أن هذا لا يُعطي أقل تكلفة فحسب، بل يُظهر أيضًا أفضل طريقة لإجراء عملية الضرب: تجميع المصفوفات بالطريقة التي تُعطي أقل تكلفة إجمالية، وتكرار العملية نفسها لكل عامل.
مع ذلك، يتميز هذا الخوارزمية بتعقيد زمني أُسّي، مما يجعلها غير فعّالة تمامًا كالطريقة البسيطة التي تُجرّب جميع التباديل. والسبب هو أن الخوارزمية تُجري الكثير من العمليات المُكرّرة. على سبيل المثال، استخدمنا أعلاه استدعاءً تكراريًا لإيجاد أفضل تكلفة لحساب كل من ABC و AB . لكن إيجاد أفضل تكلفة لحساب ABC يتطلب أيضًا إيجاد أفضل تكلفة لحساب AB . ومع ازدياد عمق التكرار، يتكرر هذا النوع من العمليات غير الضرورية بشكل متزايد.
يُطلق على أحد الحلول البسيطة اسم التخزين المؤقت : في كل مرة نحسب فيها أقل تكلفة لازمة لضرب متتالية فرعية محددة، نحفظها. إذا طُلب منا حسابها مرة أخرى، فإننا ببساطة نُعطي الإجابة المحفوظة، ولا نعيد حسابها. بما أن هناك حوالي n² /² متتالية فرعية مختلفة، حيث n هو عدد المصفوفات، فإن المساحة المطلوبة للقيام بذلك معقولة. يمكن إثبات أن هذه الحيلة البسيطة تُقلل وقت التشغيل من أسي إلى O( n³ )، وهو أكثر من كافٍ للتطبيقات العملية. هذا ما يُعرف بالبرمجة الديناميكية من أعلى إلى أسفل .
تعتمد الطريقة التصاعدية التالية [ 2 ] على حساب الحد الأدنى لتكاليف جميع المتتاليات الفرعية ذات الطول k لكل قيمة 2 ≤ k ≤ n، وذلك باستخدام تكاليف المتتاليات الفرعية الأصغر التي تم حسابها مسبقًا. وتتميز هذه الطريقة بنفس زمن التشغيل التقاربي ولا تتطلب أي تكرار.
// المصفوفة A[i] لها أبعاد dims[i-1] × dims[i] لـ i = 1..n MatrixChainOrder ( int dims [] ) { // length[dims] = n + 1 n = dims . length - 1 ; // m[i,j] = الحد الأدنى لعدد عمليات الضرب القياسي (أي التكلفة) // اللازمة لحساب المصفوفة A[i]A[i+1]...A[j] = A[i..j] // التكلفة تساوي صفرًا عند ضرب مصفوفة واحدة لـ ( i = 1 ; i <= n ; i ++ ) m [ i , i ] = 0 ;for ( len = 2 ; len <= n ; len ++ ) { // أطوال التسلسلات الفرعية for ( i = 1 ; i <= n - len + 1 ; i ++ ) { j = i + len - 1 ; m [ i , j ] = MAXINT ; for ( k = i ; k <= j - 1 ; k ++ ) { cost = m [ i , k ] + m [ k + 1 , j ] + dims [ i - 1 ] * dims [ k ] * dims [ j ] ; if ( cost < m [ i , j ] ) { m [ i , j ] = cost ; s [ i , j ] = k ; // فهرس تقسيم التسلسل الفرعي الذي حقق أقل تكلفة } } } } }- ملاحظة: الفهرس الأول للأبعاد هو 0 والفهرس الأول لـ m و s هو 1.
تطبيق بايثون باستخدام مُزخرف التخزين المؤقت من المكتبة القياسية :
from functools import cacheدالة ` matrix_chain_order` تأخذ ثلاثة عناصر من قائمة ` int` كمعاملات ، وتعيد عددًا صحيحًا من عناصر القائمة . تأخذ الدالة ` a` عنصرين من القائمة ` i` و` j` كمعاملات ، وتعيد أصغر عنصر من القائمة ` i` و` k` . يتم حساب قيمة ` a` لكل عنصر من عناصر القائمة ` i` و` j` ، مع مراعاة أن ` k` تساوي صفرًا .return a ( 0 , len ( dims ) - 1 )خوارزميات أكثر كفاءة
هناك خوارزميات أكثر كفاءة من خوارزمية البرمجة الديناميكية O ( n 3 )، على الرغم من أنها أكثر تعقيدًا.
هو وشينغ
حققت خوارزمية نشرها تي سي هو وم. تي شينغ تعقيدًا حسابيًا من رتبة O ( n log n ) . [ 3 ] [ 4 ] [ 5 ] وقد أوضحا كيف يمكن تحويل (أو اختزال ) مسألة ضرب سلسلة المصفوفات إلى مسألة تثليث مضلع منتظم . يُوجَّه المضلع بحيث يكون له ضلع سفلي أفقي، يُسمى القاعدة، ويمثل النتيجة النهائية. أما الأضلاع n الأخرى للمضلع، في اتجاه عقارب الساعة، فتمثل المصفوفات. وتمثل الرؤوس على طرفي كل ضلع أبعاد المصفوفة التي يمثلها ذلك الضلع. مع وجود n مصفوفة في سلسلة الضرب، توجد n -1 عملية ثنائية و Cn - 1 طريقة لوضع الأقواس، حيث Cn - 1 هو العدد ( n -1) من أعداد كاتالان . تستغل الخوارزمية حقيقة وجود Cn - 1 تثليثًا ممكنًا لمضلع ذي n +1 ضلعًا.
توضح هذه الصورة أشكال التثليث الممكنة لسداسي منتظم . وتتوافق هذه الأشكال مع الطرق المختلفة التي يمكن بها وضع الأقواس لترتيب عمليات الضرب لضرب 5 مصفوفات.

في المثال أدناه، لدينا أربعة أضلاع: أ، ب، ج، والنتيجة النهائية هي أ ب ج. المصفوفة أ هي مصفوفة 10×30، والمصفوفة ب هي مصفوفة 30×5، والمصفوفة ج هي مصفوفة 5×60، والنتيجة النهائية هي مصفوفة 10×60. المضلع المنتظم في هذا المثال هو رباعي الأضلاع، أي مربع.

حاصل ضرب المصفوفات AB هو مصفوفة من الرتبة 10×5، وBC هي مصفوفة من الرتبة 30×60. التثليثان المحتملان في هذا المثال هما:
تمثيل مضلع لـ (AB)C
تمثيل مضلعي لـ A(BC)
تكلفة المثلث الواحد، من حيث عدد عمليات الضرب اللازمة، هي حاصل ضرب رؤوسه. أما التكلفة الإجمالية لتثليث مضلع معين فهي مجموع تكاليف جميع مثلثاته.
- ( AB ) C : (10 × 30 × 5) + (10 × 5 × 60) = 1500 + 3000 = 4500 عملية ضرب
- أ ( ب ج ) : (30 × 5 × 60) + (10 × 30 × 60) = 9000 + 18000 = 27000 عملية ضرب
طوّر هو وشينغ خوارزمية لإيجاد الحل الأمثل لمسألة تقسيم البيانات بأقل تكلفة في زمن قدره O ( n log n ) . يعتمد برهانهما على "اللمة 1" التي أُثبتت في تقرير فني عام 1981، ولكنها حُذفت من الورقة البحثية المنشورة. [ 6 ] [ 4 ] مع أن برهان اللمة في التقرير الفني غير صحيح، إلا أن شينغ قدّم برهانًا مُصحّحًا. [ 1 ]
خوارزميات أخرى من رتبة O ( n log n )
نشر وانغ، تشو، وتيان خوارزمية مبسطة من رتبة O ( n log m ) ، حيث n هو عدد المصفوفات في السلسلة و m هو عدد القيم الدنيا المحلية في تسلسل أبعاد سلسلة المصفوفات المعطاة. [ 7 ]
حل تشين-هو-شينغ التقريبي
تعمل خوارزميةٌ طُوِّرت بشكلٍ مستقلٍّ من قِبَل تشين [ 8 ] وهو وشينغ [ 9 ] في زمنٍ زمنيٍّ O( n ) ، وتُنتج صيغةً للأقواس أسوأ بنسبة 15.47% على الأكثر من الخيار الأمثل. في معظم الحالات، تُعطي الخوارزمية الحل الأمثل أو حلاً أسوأ منه بنسبة 1-2% فقط. [ 5 ]
تبدأ الخوارزمية بتحويل المشكلة إلى مشكلة تقسيم المضلع. يُخصص لكل رأس V من رؤوس المضلع وزن w . لنفترض أن لدينا ثلاثة رؤوس متتاليةوذلكهو الرأس ذو الوزن الأدنىننظر إلى الشكل الرباعي ذي الرؤوس(في اتجاه عقارب الساعة). يمكننا تحديد موقعها بطريقتين:
- و، مع التكلفة
- ومع التكلفة.
لذلك، إذا
أو ما يعادل ذلك
نقوم بإزالة الرأسمن المضلع وأضف الضلعإلى عملية التثليث. نكرر هذه العملية حتى لايحقق الشرط المذكور أعلاه. بالنسبة لجميع الرؤوس المتبقيةنضيف الجانبإلى عملية التثليث. وهذا يعطينا تثليثًا مثاليًا تقريبًا.
التعميمات
تُعمَّم مسألة ضرب سلاسل المصفوفات لحل مسألة أكثر تجريدًا: بالنظر إلى تسلسل خطي من العناصر، وعملية ثنائية ترابطية على تلك العناصر، وطريقة لحساب تكلفة إجراء تلك العملية على أي عنصرين مُعطى (بالإضافة إلى جميع النتائج الجزئية)، احسب الطريقة الأقل تكلفة لتجميع العناصر لتطبيق العملية على التسلسل. [ 10 ] مثال عملي على ذلك يأتي من ترتيب عمليات الربط في قواعد البيانات ؛ انظر تحسين الاستعلام § ترتيب الربط .
هناك حالة خاصة أخرى، وإن كانت مصطنعة بعض الشيء، وهي دمج سلاسل نصية من قائمة سلاسل. في لغة C ، على سبيل المثال، تبلغ تكلفة دمج سلسلتين نصيتين بطول m و n باستخدام دالة strcat من الرتبة O( m + n ) ، حيث نحتاج إلى زمن قدره O( m ) للعثور على نهاية السلسلة الأولى، وزمن قدره O( n ) لنسخ السلسلة الثانية إلى نهايتها. باستخدام دالة التكلفة هذه، يمكننا كتابة خوارزمية برمجة ديناميكية لإيجاد أسرع طريقة لدمج سلسلة من السلاسل النصية. مع ذلك، فإن هذا التحسين غير مجدٍ عمليًا، إذ يمكننا دمج السلاسل مباشرةً في زمن يتناسب مع مجموع أطوالها. توجد مشكلة مماثلة في القوائم المرتبطة أحادية الاتجاه .
ثمة تعميم آخر يتمثل في حل المشكلة عند توفر معالجات متوازية. في هذه الحالة، بدلاً من جمع تكاليف حساب كل عامل من عوامل ضرب المصفوفة، نختار القيمة القصوى لأننا نستطيع القيام بذلك في آن واحد. قد يؤثر هذا بشكل كبير على كل من الحد الأدنى للتكلفة والتجميع الأمثل النهائي؛ حيث تُفضّل التجميعات الأكثر "توازنًا" التي تُبقي جميع المعالجات مشغولة. وهناك مناهج أكثر تطورًا. [ 11 ]
انظر أيضاً
مراجع
- 1 2 شوارتز، عوديد؛ فايس، إيلاد (يناير 2019). "إعادة النظر في حساب نواتج سلاسل المصفوفات ". مجلة SIAM للحوسبة . 48 (5): 1481-1486 . doi : 10.1137/18m1195401 . S2CID 203009883 .
- ↑ كورمن، توماس هـ ؛ ليسرسون، تشارلز إي ؛ ريفست، رونالد إل ؛ شتاين، كليفورد (2001). "15.2: ضرب سلسلة المصفوفات". مقدمة في الخوارزميات . المجلد. الطبعة الثانية. مطبعة معهد ماساتشوستس للتكنولوجيا وماكجرو هيل. الصفحات 331-338 . ISBN 978-0-262-03293-3.
- ↑ هو، تي سي ؛ شينغ، إم-تي. (1982). "حساب جداءات سلاسل المصفوفات، الجزء الأول" (ملف PDF) . مجلة SIAM للحوسبة . 11 (2): 362-373 . CiteSeerX 10.1.1.695.2923 . doi : 10.1137/0211028 . ISSN 0097-5397 . مؤرشف من الأصل (ملف PDF) بتاريخ 2016-08-04 . تم الاطلاع عليه بتاريخ 2015-01-20 .
- 1 2 هو، تي سي ؛ شينغ، إم-تي. (1984). "حساب نواتج سلاسل المصفوفات، الجزء الثاني" (ملف PDF) . مجلة SIAM للحوسبة . 13 (2): 228-251 . CiteSeerX 10.1.1.695.4875 . doi : 10.1137/0213017 . ISSN 0097-5397 . مؤرشف من الأصل (ملف PDF) بتاريخ 2016-08-04 . تم الاطلاع عليه بتاريخ 2015-01-20 .
- 1 2 أرتور، تشوماي (1996). "تقريب سريع جدًا لمسألة ضرب سلسلة المصفوفات" (ملف PDF) . مجلة الخوارزميات . 21 : 71-79 . CiteSeerX 10.1.1.218.8168 . doi : 10.1006/jagm.1996.0037 . S2CID 2818053. مؤرشف من النسخة الأصلية (ملف PDF) بتاريخ 27-07-2018.
- ↑ هو، تي سي؛ شينغ، إم تي (1981). حساب نواتج سلسلة المصفوفات، الجزء الأول، الجزء الثاني (ملف PDF) (تقرير فني). جامعة ستانفورد، قسم علوم الحاسوب. الجزء الثاني، الصفحة 3. STAN-CS-TR-81-875.
- ↑ وانغ، شياودونغ؛ تشو، داكسين؛ تيان، جون (أبريل 2013). "الحساب الفعال لسلسلة المصفوفات". المؤتمر الدولي الثامن لعلوم الحاسوب والتعليم 2013. الصفحات 703-707 . doi : 10.1109/ICCSE.2013.6553999 . ISBN 978-1-4673-4463-0. S2CID 17303326 .
- ↑ تشين، فرانسيس واي. (يوليو 1978). "خوارزمية O(n) لتحديد ترتيب حسابي شبه مثالي لمنتجات سلسلة المصفوفات" . اتصالات ACM . 21 (7): 544-549 . doi : 10.1145/359545.359556 .
- ↑ هو، تي سي؛ شينغ، إم تي (يونيو 1981). "خوارزمية من رتبة O(n) لإيجاد تقسيم شبه مثالي لمضلع محدب". مجلة الخوارزميات . 2 (2): 122-138 . doi : 10.1016/0196-6774(81)90014-6 .
- ↑ ج. بومغارتنر، د. بيرنهولت، د. كوسيرفا، ر. هاريسون، م. نويجن، ج. رامانوجام، و ب. سادايابان. إطار عمل لتحسين أداء ترجمة تعابير انكماش الموتر إلى برامج متوازية. ورشة العمل الدولية السابعة حول نماذج البرمجة المتوازية عالية المستوى والبيئات الداعمة (HIPS '02). فورت لودرديل، فلوريدا. 2002. متاح على الرابط http://citeseer.ist.psu.edu/610463.html وعلى الرابط http://www.csc.lsu.edu/~gb/TCE/Publications/OptFramework-HIPS02.pdf
- ↑ هيجو لي، جونغ كيم، سونغجي هونغ، وسونغجو لي. تخصيص المعالج وجدولة المهام لمنتجات سلسلة المصفوفات على الأنظمة المتوازية. مؤرشف في 22 يوليو 2011 على موقع Wayback Machine . مجلة IEEE للمعاملات في الأنظمة المتوازية والموزعة، المجلد 14، العدد 4، الصفحات 394-407، أبريل 2003.
- خوارزميات وأساليب التحسين
- المصفوفات (الرياضيات)
- البرمجة الديناميكية
