مقدمة في علوم الحاسوب النظرية › تعريف الاحتساب 5 / 26 السابق التالي

تعريف الحوسبة {#compchap }

تعريف الحوسبة

  • أن ترى كيف يمكن نمذجة الحوسبة بدقّة. \
  • أن تتعلّم النموذج الحوسب للدوائر المنطقية (Boolean circuits) / البرامج المستقيمة.
  • تكافؤ الدوائر مع البرامج المستقيمة.
  • تكافؤ AND/OR/NOT مع NAND.
  • أمثلة على الحساب في العالم الفيزيائي. \

"لا يوجد سبب يمنع من الاقتصاد في العمل العقلي كما في العمل البدني بمعاونة الآلات"، تشارلز بابيج، 1852

"إن لم ينبّهه مثالُي، وشرع أي رجل في بناء مُحرّك يجسّد في ذاته كامل القسم التنفيذي من التحليل الرياضي على مبادئ مختلفة أو بوسائل ميكانيكية أبسط، فلا أخشى أن أترك سمعتي في عهده، إذ هو وحده القادر تمامًا على تقدير طبيعة جهدي وقيمة نتائجه."، تشارلز بابيج، 1864

"لفهم برنامج لا بدّ أن تصير في آن واحد الماكينة والبرنامج"، آلان بيرليس، 1982

/images/introtcs/lec_03_computation-1.webp

مقال صادر عام 1944 في مجلة Popular Mechanics عن حاسوب هارفارد Mark I.

لقد كان الناس يحسبون لآلاف السنين، بمعونات لا تقتصر على القلم والورق، بل تشمل العدّاد (abacus) والمسطرةَ الحسابية (slide rules) والأجهزةَ الميكانيكيةَ المتنوعة، والحواسيبَ الإلكترونيةَ الحديثة. من حيث المبدأ، تبدو فكرة الحوسبة مرتبطة بالأداة التي تستخدمها بعينها. قد تظنّ أن الخوارزمية «الأفضل» لضرب الأعداد ستكون مختلفة إن نفّذتها في Python على حاسوب محمول حديث عمّا لو استخدمت القلم والورق. غير أننا، كما رأينا في المقدمة (مقدمة)، فإن الخوارزمية الأفضل من حيث السلوك التصاعدي (asymptotically better) كانت لتتفوّق في النهاية على خوارزمية أضعف مهما كانت التقنية الكامنة. وهذا يمنحنا أملًا في طريقة مستقلة عن التقنية (technology independent) لتعريف الحوسبة. وهذا هو ما نفعله في هذا الفصل. سنعرّف فكرة حساب مُخرَج من مُدخَل عبر تطبيق تسلسل من العمليات الأساسية (انظر الشكل التوضيحي). وباستخدام هذا، سنتمكن من تعريف عبارات مثل «الدالة ff يمكن حسابها بالنموذج XX» أو «الدالة ff يمكن حسابها بالنموذج XX باستخدام ss عمليات» بدقّة.

دالة تُرسِل النصوص إلى النصوص تحدِّد مهمةً حسابية، أي أنها تصف ما هي العلاقة المرغوبة بين المُدخل والمُخرج. وفي هذا الفصل نعرّف نماذجَ لـ_تنفيذ_ العمليات الحسابية التي تحقّق تلك العلاقة، أي أنها تصف كيف يُحسب المُخرج من المُدخل. وسنرى عدّة أمثلة لمثل هذه النماذج باستخدام الدوائر المنطقية (Boolean circuits) وبرامج البرمجة المستقيمة معًا.

أبرز ما نستفيده من هذا الفصل هو ما يلي:

  • يمكننا استخدام العمليات المنطقية (logical operations) مثل ANDAND وOROR وNOTNOT لحساب مُخرج من مُدخل (انظر الحساب باستخدام AND وOR وNOT.).

  • الدائرة المنطقية (Boolean circuit) هي طريقة لتركيب العمليات المنطقية الأساسية معًا لتحسب دالةً أعقد (انظر الدوائر المنطقية). ويمكننا أن نرى الدوائر المنطقية بوصفها نموذجًا رياضيًا (قائمًا على الرسوم البيانية الموجَّهة غير الدورية) فضلًا عن كونها أجهزة فيزيائية يمكننا بناؤها في العالم الحقيقي بطرق متنوّعة، لا تقتصر على أشباه الموصلات السيليكونية فحسب بل تشمل أيضًا الآليات الميكانيكية وحتى الحيوية (انظر التنفيذات الفيزيائية لأجهزة الحوسبة (استطراد)).

  • يمكننا أيضًا وصف الدوائر المنطقية بوصفها برامج مستقيمة (straight-line programs)، أي برامج لا تحتوي على أي بِنَى تكرارية (أي لا while ولا for ولا do .. until إلخ)، انظر البرامج المستقيمة.

  • من الممكن تنفيذ عمليات ANDAND وOROR وNOTNOT باستخدام العملية NANDNAND (وكذلك العكس). وهذا يعني أن الدوائر ذات البوابات ANDAND/OROR/NOTNOT تستطيع حساب الدوال نفسها (أي أنها متكافئة في القدرة) التي تحسبها الدوائر ذات بوابات NANDNAND، ويمكننا استخدام أيّ النموذجين لوصف الحوسبة بحسب ما يريحنا، انظر دالة NAND. ولكي نكشف جانبًا من الجواب مبكرًا، سنرى في السكر النحوي، وحساب كل دالة أن مثل هذه الدوائر تستطيع حساب كل الدوال المتناهية.

إحدى «الفكرة الكبرى» في هذا الفصل هي فكرة التكافؤ (equivalence) بين النماذج (equivalencemodels). ويكون نموذجان حوسبان متكافئين إذا استطاعا حساب المجموعة نفسها من الدوال. والدوائر المنطقية ذات البوابات ANDAND/OROR/NOTNOT تكافئ الدوائر ذات بوابات NANDNAND، لكن هذا مجرد مثال على ظاهرة أعمّ سنلتقي بها مرارًا في هذا الكتاب.

تعريف الحوسبة

اسم «الخوارزمية» مشتق من النقل اللاتيني لاسم محمد بن موسى الخوارزمي. كان الخوارزمي عالمًا فارسيًا في القرن التاسع الميلادي، عرّض كتبه العالمَ الغربي على نظام الأرقام العشري الموضعي، فضلًا عن حلول المعادلات من الدرجة الأولى والثانية (انظر الشكل التوضيحي). غير أن أوصاف الخوارزميات عند الخوارزمي كانت غير رسمية إلى حدٍّ كبير بمقاييس اليوم. فهو لم يستخدم «متغيّرات» مثل x,yx,y، بل استخدم أعدادًا بعينها مثل 10 و39، ووثق في القارئ أن يكون قادرًا على التعميم انطلاقًا من هذه الأمثلة، تمامًا كما لا يزال تُعلَّم الخوارزميات للأطفال اليوم.

إليك كيف وصف الخوارزمي خوارزمية حل معادلة من الشكل x2+bx=cx^2 +bx = c:

[كيف تحلّ معادلة من الشكل ] «الجذور والمربعات تساوي أعدادًا»: على سبيل المثال «مربع واحد، وعشرة جذور منه، مجموعها تسعة وثلاثون درهمًا»، أي ما هو المربع الذي إذا زيد إليه عشرة من جذره بلغ تسعة وثلاثين؟ الحل هو هذا: أنصِف عدد الجذور، فيصير في هذه الحالة خمسة. اضرب هذا في نفسه، فيكون الناتج خمسة وعشرين. أضِف هذا إلى تسعة وثلاثين، فيكون المجموع أربعة وستين. خُذ الآن جذر هذا المجموع، وهو ثمانية، واطرح منه نصف عدد الجذور، وهو خمسة، فيبقى ثلاثة. هذا هو جذر المربع الذي كنت تبحث عنه؛ وأما المربع نفسه فتسعة.

/images/introtcs/lec_03_computation-4.webp

/images/introtcs/lec_03_computation-5.webp

ولأغراض هذا الكتاب، سنحتاج إلى طريقة أدقّ بكثير لوصف الخوارزميات. ولحسن الحظ (أو لسوء الحظ؟)، على الأقل في الوقت الحالي، تتأخر الحواسيب كثيرًا في التعلّم من الأمثلة مقارنةً بأطفال سنّ المدرسة. ولهذا، في القرن العشرين، ابتكر الناس صيغًا رسمية دقيقة لوصف الخوارزميات، وهي لغات البرمجة. إليك خوارزمية الخوارزمي لحل المعادلات من الدرجة الثانية مصوغةً بلغة البرمجة Python:

from math import sqrt
#Pythonspeak to enable use of the sqrt function to compute square roots.

def solve_eq(b,c):
    # return solution of x^2 + bx = c following Al Khwarizmi's instructions
    # Al Kwarizmi demonstrates this for the case b=10 and c= 39

    val1 = b / 2.0 # "halve the number of the roots"
    val2 = val1 * val1 # "this you multiply by itself"
    val3 = val2 + c # "Add this to thirty-nine"
    val4 = sqrt(val3) # "take the root of this"
    val5 = val4 - val1 # "subtract from it half the number of roots"
    return val5  # "This is the root of the square which you sought for"

# Test: solve x^2 + 10*x = 39
print(solve_eq(10,39))
# 3.0

يمكننا تعريف الخوارزميات إجرائيًا على النحو التالي:

اقتباس

تعريف إجرائي للخوارزمية: الخوارزمية (algorithm) مجموعة تعليمات تصف كيفية حساب مُخرج من مُدخل عبر اتباع تسلسل من «الخطوات الأولية».

تقول الخوارزمية AA إنها تحسب الدالة FF إذا كان لكل مُدخل xx، حين نتبع تعليمات AA على المُدخل xx، ينتج عن ذلك المُخرج F(x)F(x).

في هذا الفصل سنجعل هذا التعريف الإجرائي دقيقًا باستخدام نموذج الدوائر المنطقية (Boolean Circuits). وسنبيّن أن الدوائر المنطقية متكافئة في القدرة مع البرامج المستقيمة (straight line programs) المكتوبة بلغات برمجة «فائقة البساطة» لا تحوي حتى حلقات تكرار. وسنرى أيضًا أن الاختيار بعينه للعمليات الأولية (elementary operations) لا أهمية له، وأن اختيارات كثيرة مختلفة تُنتج نماذج متكافئة القدرة (انظر الشكل التوضيحي). غير أننا سنحتاج إلى بعض الوقت حتى نصل إلى ذلك. وسنبدأ بمناقشة ما هي «العمليات الأولية»، وكيف نحوّل وصفًا لخوارزمية إلى عملية فيزيائية فعلية تُنتج مُخرجًا من مُدخل في العالم الحقيقي.

/images/introtcs/lec_03_computation-6.webp

الحساب باستخدام AND وOR وNOT.

تفكّك الخوارزمية الحسابَ المعقّد إلى سلسلة من خطوات أبسط. وتنفيذ هذه الخطوات ممكن بطرق متنوّعة مختلفة، منها:

  • كتابة الرموز على ورقة.

  • تعديل التيار الكهربائي الذي يجري في الأسلاك.

  • ربط بروتينٍ بخيط من الحمض النووي.

  • الاستجابة لمنبّه من جانب عنصر في مجموعة (مثل نحلة في مستعمرة، أو تاجر في سوق).

ولكي نعرّف الخوارزميات تعريفًا رسميًا، فلنحاول أن «نميل إلى جهة البساطة» وأن نمذجة «خطواتنا الأساسية» بوصفها بالغة الدقّة. فمثلًا، إليك بعض الدوال البسيطة جدًا:

  • OR:{0,1}2→{0,1}OR:\{0,1\}^2 \rightarrow \{0,1\} المعرَّفة بما يلي:
OR(a,b)={0a=b=01otherwiseOR(a,b) = \begin{cases} 0 & a=b=0 \\ 1 & \text{otherwise} \end{cases}
  • AND:{0,1}2→{0,1}AND:\{0,1\}^2 \rightarrow \{0,1\} المعرَّفة بما يلي:
AND(a,b)={1a=b=10otherwiseAND(a,b) = \begin{cases} 1 & a=b=1 \\ 0 & \text{otherwise} \end{cases}
  • NOT:{0,1}→{0,1}NOT:\{0,1\} \rightarrow \{0,1\} المعرَّفة بما يلي:
NOT(a)={0a=11a=0NOT(a) = \begin{cases} 0 & a = 1 \\ 1 & a = 0 \end{cases}

الدوال ANDAND وOROR وNOTNOT هي المشغّلات المنطقية الأساسية المستخدمة في المنطق وفي أنظمة حواسوب كثيرة. وفي سياق المنطق، من الشائع استعمال التدوين a∧ba \wedge b للدالة AND(a,b)AND(a,b)، وa∨ba \vee b للدالة OR(a,b)OR(a,b)، وa‾\overline{a} و¬a\neg a للدالة NOT(a)NOT(a)، وسنستعمل هذا التدوين أيضًا.

كل واحدة من الدوال AND,OR,NOTAND,OR,NOT تأخذ إمّا بتًّا واحدًا أو اثنين كمدخل، وتُنتج بتًّا واحدًا كمخرج. ومن البديهي أنها لا يمكن أن تكون أكثر أولية من ذلك. غير أن القدرة الحوسبية مصدرها تركيب هذه القوالب الأولية البسيطة معًا.

مثال — الأغلبية من ANDAND وOROR وNOTNOT

لتُعتبر الدالة MAJ:{0,1}3→{0,1}MAJ:\{0,1\}^3 \rightarrow \{0,1\} المعرَّفة بما يلي:

MAJ(x)={1x0+x1+x2≥20otherwise  .MAJ(x) = \begin{cases}1 & x_0 + x_1 + x_2 \geq 2 \\ 0 & \text{otherwise}\end{cases} \;.

بمعنى آخر، لكل x∈{0,1}3x\in \{0,1\}^3، لدينا MAJ(x)=1MAJ(x)=1 إذا ونحو فقط إذا كانت أكثرية عناصر xx (أي اثنين على الأقل من الثلاثة) مساوية لـ 11. هل تستطيع أن تجد صيغة تتضمّن ANDAND وOROR وNOTNOT لحساب MAJMAJ؟ (من المفيد لك أن تتوقف هنا وتستنتج الصيغة بنفسك. وكإرشاد، فرغم أن المشغّل NOTNOT لازم لحساب بعض الدوال، فإنك لن تحتاج إليه لحساب MAJMAJ.)

لنحاول أولًا أن نعيد صياغة MAJ(x)MAJ(x) بالكلمات: «MAJ(x)=1MAJ(x)=1 إذا ونحو فقط إذا وُجد زوجٌ ما من العناصر المتمايزة i,ji,j بحيث يكون xix_i وxjx_j كلاهما مساويًا لـ 11». وبعبارة أخرى، يعني ذلك أن MAJ(x)=1MAJ(x)=1 إذا ونحو فقط إذا كان إمّا x0=1x_0=1 وx1=1x_1=1 كلاهما، أو x1=1x_1=1 وx2=1x_2=1 كلاهما، أو x0=1x_0=1 وx2=1x_2=1 كلاهما. ولأن OROR لثلاثة شروط c0,c1,c2c_0,c_1,c_2 يمكن كتابتها OR(c0,OR(c1,c2))OR(c_0,OR(c_1,c_2))، يمكننا الآن ترجمة ذلك إلى صيغة كالتالي:

MAJ(x0,x1,x2)=OR( AND(x0,x1)  ,  OR(AND(x1,x2)  ,  AND(x0,x2)) )  . MAJ(x_0,x_1,x_2) = OR\left(\, AND(x_0,x_1)\;,\; OR \bigl( AND(x_1,x_2) \;,\; AND(x_0,x_2) \bigr) \, \right) \;.

تذكّر أننا يمكننا أيضًا كتابة a∨ba \vee b للدالة OR(a,b)OR(a,b) وa∧ba \wedge b للدالة AND(a,b)AND(a,b). وبهذا التدوين، يمكن كتابة المعادلة أيضًا على الصورة

MAJ(x0,x1,x2)=((x0∧x1)∨(x1∧x2))∨(x0∧x2)  .MAJ(x_0,x_1,x_2) = ((x_0 \wedge x_1) \vee (x_1 \wedge x_2)) \vee (x_0 \wedge x_2)\;.

كما يمكننا كتابة المعادلة بصورة «لغة برمجة»، أي التعبير عنها كمجموعة تعليمات لحساب MAJMAJ انطلاقًا من العمليات الأساسية AND,OR,NOTAND,OR,NOT:

def MAJ(X[0],X[1],X[2]):
    firstpair  = AND(X[0],X[1])
    secondpair = AND(X[1],X[2])
    thirdpair  = AND(X[0],X[2])
    temp       = OR(secondpair,thirdpair)
    return OR(firstpair,temp)

بعض خواص AND وOR

كما في الجمع والضرب المعياريَّين، فإن الدالتين ANDAND وOROR تستوفيان خاصيتي التبديل (commutativity): a∨b=b∨aa \vee b = b \vee a وa∧b=b∧aa \wedge b = b \wedge a، وخاصية التجميع (associativity): (a∨b)∨c=a∨(b∨c)(a \vee b) \vee c = a \vee (b \vee c) و(a∧b)∧c=a∧(b∧c)(a \wedge b) \wedge c = a \wedge (b \wedge c). وكما في حالة الجمع والضرب، فإننا نُسقط الأقواس غالبًا ونكتب a∨b∨c∨da \vee b \vee c \vee d بدلًا من ((a∨b)∨c)∨d((a \vee b) \vee c) \vee d، وكذلك في حالات OR وAND لأكثر من حدّ. كما أنهما تستوفيان صيغةً من صيغ قانون التوزيع:

تمرين محلول — قانون التوزيع لـ AND و OR

أثبِت أن لكل a,b,c∈{0,1}a,b,c \in \{0,1\}، لدينا a∧(b∨c)=(a∧b)∨(a∧c)a \wedge (b \vee c) = (a \wedge b) \vee (a \wedge c).

الحل

يمكننا إثبات ذلك بحصر جميع القيم الثمانية الممكنة لـ a,b,c∈{0,1}a,b,c \in \{0,1\}، لكنه أيضًا يتبع قانون التوزيع المعياري. لنفترض أن نُطابق أي عدد صحيح موجب مع «صحيح» والقيمة صفر مع «خطأ». عندئذٍ لكل عددين u,v∈Nu,v \in \N، يكون u+vu+v موجبًا إذا ونحو فقط إذا كانت u∨vu \vee v صحيحة، ويكون u⋅vu \cdot v موجبًا إذا ونحو فقط إذا كانت u∧vu \wedge v صحيحة. وهذا يعني أن لكل a,b,c∈{0,1}a,b,c \in \{0,1\}، يكون التعبير a∧(b∨c)a \wedge (b \vee c) صحيحًا إذا ونحو فقط إذا كان a⋅(b+c)a \cdot(b+c) موجبًا، ويكون التعبير (a∧b)∨(a∧c)(a \wedge b) \vee (a \wedge c) صحيحًا إذا ونحو فقط إذا كان a⋅b+a⋅ca \cdot b + a \cdot c موجبًا، ولكن بقانون التوزيع المعياري فإن a⋅(b+c)=a⋅b+a⋅ca\cdot (b+c) = a\cdot b + a \cdot c، وبالتالي فالتعبير الأول صحيح إذا ونحو فقط إذا كان التعبير الثاني كذلك.

مثال موسّع: حساب XORXOR من ANDAND وOROR وNOTNOT

لِنَرَ كيف يمكننا الحصول على دالة مختلفة من القوالب البناءية نفسها. لنُعرِّف XOR:{0,1}2→{0,1}XOR:\{0,1\}^2 \rightarrow \{0,1\} لتكون الدالة XOR(a,b)=a+bmod  2XOR(a,b)= a + b \mod 2. أي أن XOR(0,0)=XOR(1,1)=0XOR(0,0)=XOR(1,1)=0 وXOR(1,0)=XOR(0,1)=1XOR(1,0)=XOR(0,1)=1. ونحن ندّعي أنه يمكننا بناء XORXOR باستخدام ANDAND وOROR وNOTNOT لا غير.

توقّف وتأمّل

وكالمعتاد، من التمارين المفيدة أن تحاول استنتاج خوارزمية XORXOR باستخدام ANDAND وOROR وNOTNOT بنفسك قبل أن تقرأ ما يلي.

تحسب الخوارزمية التالية XORXOR باستخدام ANDAND وOROR وNOTNOT:

خوارزمية — XORXOR من ANDAND/OROR/NOTNOT

Input: $a,b \in \{0,1\}$.
Output: $XOR(a,b)$

$w1 \leftarrow AND(a,b)$

$w2 \leftarrow NOT(w1)$

$w3 \leftarrow OR(a,b)$

return $AND(w2,w3)$

لمّة

لكل a,b∈{0,1}a,b\in \{0,1\}، عند المدخلين a,ba,b، تُنتج الخوارزمية خوارزمية — XOR من AND/OR/NOT القيمة a+bmod  2a+b \mod 2.

برهان

لكل a,ba,b، لدينا XOR(a,b)=1XOR(a,b)=1 إذا ونحو فقط إذا كان aa مختلفًا عن bb. وعند المدخلين a,b∈{0,1}a,b\in \{0,1\}، تُنتج الخوارزمية خوارزمية — XOR من AND/OR/NOT القيمة AND(w2,w3)AND(w2,w3) حيث w2=NOT(AND(a,b))w2=NOT(AND(a,b)) وw3=OR(a,b)w3=OR(a,b).

  • إذا كان a=b=0a=b=0 فإن w3=OR(a,b)=0w3=OR(a,b)=0، وبالتالي سيكون المُخرج 00.

  • إذا كان a=b=1a=b=1 فإن AND(a,b)=1AND(a,b)=1، وبالتالي يكون w2=NOT(AND(a,b))=0w2=NOT(AND(a,b))=0، وسيكون المُخرج 00.

  • إذا كان a=1a=1 وb=0b=0 (أو العكس) فإن كلًّا من w3=OR(a,b)=1w3=OR(a,b)=1 وw1=AND(a,b)=0w1=AND(a,b)=0، وفي هذه الحالة ستُنتج الخوارزمية OR(NOT(w1),w3)=1OR(NOT(w1),w3)=1.

كما يمكننا التعبير عن الخوارزمية خوارزمية — XOR من AND/OR/NOT بلغة برمجة. وعلى وجه التحديد، إليك برنامج Python يحسب الدالة XORXOR:

def AND(a,b): return a*b
def OR(a,b):  return 1-(1-a)*(1-b)
def NOT(a):   return 1-a

def XOR(a,b):
    w1 = AND(a,b)
    w2 = NOT(w1)
    w3 = OR(a,b)
    return AND(w2,w3)

# Test out the code
print([f"XOR({a},{b})={XOR(a,b)}" for a in [0,1] for b in [0,1]])
# ['XOR(0,0)=0', 'XOR(0,1)=1', 'XOR(1,0)=1', 'XOR(1,1)=0']

تمرين محلول — حساب XORXOR على ثلاثة بتات مُدخلة

لتكن XOR3:{0,1}3→{0,1}XOR_3:\{0,1\}^3 \rightarrow \{0,1\} الدالة المعرَّفة بـ XOR3(a,b,c)=a+b+cmod  2XOR_3(a,b,c) = a + b +c \mod 2. أي أن XOR3(a,b,c)=1XOR_3(a,b,c)=1 إذا كان a+b+ca+b+c فرديًا، وXOR3(a,b,c)=0XOR_3(a,b,c)=0 في غير ذلك. أثبِت أنه بإمكانك حساب XOR3XOR_3 باستخدام AND وOR وNOT. يمكنك التعبير عنها بصيغة، أو استخدام لغة برمجة مثل Python، أو استخدام دائرة منطقية.

الحل

الجمع على مقياس اثنين يستوفي خصيتي التجميع ((a+b)+c=a+(b+c)(a+b)+c=a+(b+c)) و_التبديل_ (a+b=b+aa+b=b+a) نفسها التي يستوفيها الجمع المعياري. وهذا يعني أنه إذا عرّفنا a⊕ba \oplus b بأنه يساوي a+bmod  2a + b \mod 2، فإن XOR3(a,b,c)=(a⊕b)⊕c XOR_3(a,b,c) = (a \oplus b) \oplus c أو بمعنى آخر XOR3(a,b,c)=XOR(XOR(a,b),c)  . XOR_3(a,b,c) = XOR(XOR(a,b),c) \;.

ولأننا نعرف كيف نحسب XORXOR باستخدام AND وOR وNOT، يمكننا تركيب ذلك لحساب XOR3XOR_3 بالقوالب البناءية نفسها. وفي Python يقابل ذلك البرنامج التالي:

def XOR3(a,b,c):
    w1 = AND(a,b)
    w2 = NOT(w1)
    w3 = OR(a,b)
    w4 = AND(w2,w3)
    w5 = AND(w4,c)
    w6 = NOT(w5)
    w7 = OR(w4,c)
    return AND(w6,w7)

# Let's test this out
print([f"XOR3({a},{b},{c})={XOR3(a,b,c)}" for a in [0,1] for b in [0,1] for c in [0,1]])
# ['XOR3(0,0,0)=0', 'XOR3(0,0,1)=1', 'XOR3(0,1,0)=1', 'XOR3(0,1,1)=0', 'XOR3(1,0,0)=1', 'XOR3(1,0,1)=0', 'XOR3(1,1,0)=0', 'XOR3(1,1,1)=1']

توقّف وتأمّل

حاول تعميم الأمثلة أعلاه للحصول على طريقة لحساب XORn:{0,1}n→{0,1}XOR_n:\{0,1\}^n \rightarrow \{0,1\} لكل nn باستخدام 4n4n خطوة أساسية على الأكثر، وتكون تطبيقات لدالة من {AND,OR,NOT}\{ AND, OR , NOT \} على مُخرجات أو على قيم حُسبت سابقًا.

تعريف «العمليات الأساسية» و«الخوارزميات» إجرائيًا

لقد رأينا أننا يمكننا الحصول على بعض الأمثلة على الأقل من الدوال المهمة، عبر تركيب ANDAND وOROR وNOTNOT معًا. وهذا يوحي إلينا باستعمال ANDAND وOROR وNOTNOT بوصفها «عملياتنا الأساسية»، فنحصل على التعريف التالي لـ«الخوارزمية»:

اقتباس

تعريف شبه رسمي للخوارزمية: تتألف الخوارزمية (algorithm) من تسلسل خطوات من الشكل «احسب قيمة جديدة بتطبيق ANDAND أو OROR أو NOTNOT على قيم حُسبت سابقًا (مع الافتراض بأن المُدخل قد حُسب بدوره سابقًا)».

تقول الخوارزمية AA إنها تحسب الدالة FF إذا كان لكل مُدخل xx للدالة FF، حين نُدخل xx كمدخل إلى الخوارزمية، كانت القيمة المحسوبة في خطوتها الأخيرة هي F(x)F(x).

يثير هذا التعريف عدة مخاوف:

  1. أولًا وقبل كل شيء، هذا التعريف غير رسمي فعلًا. فنحن لا نحدّد بدقّة ما الذي تفعله كل خطوة، ولا ما الذي يعنيه «إدخال xx كمدخل».

  2. ثانيًا، يبدو اختيار ANDAND أو OROR أو NOTNOT عشوائيًا إلى حدٍّ كبير. ولماذا لا XORXOR وMAJMAJ؟ ولماذا لا نسمح بعمليات مثل الجمع والضرب؟ وماذا عن أي بِنَى منطقية أخرى مثل if/then أو while؟

  3. ثالثًا، هل نعرف أصلًا أن لهذا التعريف علاقة بأي حساب فعلي؟ فلو أعطى أحدهم لنا وصفًا لخوارزمية كهذه، فهل يمكننا استعمالها فعلًا لحساب الدالة في العالم الحقيقي؟

توقّف وتأمّل

ستوجّه هذه المخاوف مسيرتنا إلى حدٍّ كبير في الفصول القادمة. ومن ثمّ فإن من الحكمة أن تعيد قراءة التعريف الإجرائي أعلاه وتنظر فيما تعبّر عنه من رأي في هذه المسائل.

سيُخصَّص جزء كبير من هذا الكتاب لمعالجة المسائل أعلاه. وسنرى أن:

  1. يمكننا أن نجعل تعريف الخوارزمية رسميًا تمامًا، وبالتالي منح معنى رياضيًا دقيقًا لعبارات مثل «الخوارزمية AA تحسب الدالة ff».

  2. ورغم أن اختيار ANDAND/OROR/NOTNOT عشوائي، وأن كان بإمكاننا أن نختار دوال أخرى بالقدر نفسه، فسنرى أيضًا أن هذا الاختيار لا أهمية كبيرة له. وسنرى أننا لو استخدمنا بدلًا منه الجمع والضرب، بل وكل عملية أخرى يمكن اعتبارها بصورة معقولة خطوةً أساسية، لَحَصّلنا على القدرة الحوسبية نفسها.

  3. ويبيّن الأمر في الواقع أننا يمكننا أن نحسب هذه «الخوارزميات المعتمدة على ANDAND/OROR/NOTNOT» في العالم الحقيقي، بل إننا نفعل ذلك فعلًا. أولًا، مثل هذه الخوارزمية محدَّدة بدقّة واضح، ولذلك يمكن تنفيذها من إنسان بقلم وورقة. وثانيًا، هناك طرائق متنوّعة لآلية (mechanize) هذا الحساب. لقد رأينا بالفعل أننا نستطيع كتابة شيفرة Python تقابل اتباع قائمة التعليمات هذه. لكننا في الواقع نستطيع تنفيذ عمليات مثل ANDAND وOROR وNOTNOT مباشرةً عبر إشارات إلكترونية باستخدام مكوّنات تُعرف بالـ_ترانزستورات_ (transistors). هكذا تعمل الحواسيب الإلكترونية الحديثة.

وفي بقية هذا الفصل، وفي بقية هذا الكتاب، سنبدأ في الإجابة عن بعض هذه الأسئلة. وسنرى أمثلة أكثر على قوة العمليات البسيطة في حساب عمليات أعقد، منها الجمع والضرب والترتيب وغيرها. وسنناقش أيضًا كيف نُنفّذ فيزيائيًا عمليات بسيطة مثل ANDAND وOROR وNOTNOT باستخدام تقنيات متنوّعة.

الدوائر المنطقية

الرموز المعيارية للعمليات المنطقية أو «البوابات»  و و، فضلًا عن العملية  التي نتناولها في nandsec.

/images/introtcs/lec_03_computation-7.webp

توفّر الدوائر المنطقية (Boolean circuits) مفهومًا دقيقًا لـ«تركيب العمليات الأساسية معًا». تتألف الدائرة المنطقية (انظر الشكل التوضيحي) من بوابات و_مُدخلات_ متّصلة بواسطة أسلاك. تحمل الأسلاك إشارة تمثّل إمّا القيمة 00 وإمّا القيمة 11. تقابل كل بوابة إمّا عملية OR أو AND أو NOT. وللبوابة OR سلكان داخليان، وسلك خارج واحد أو أكثر. إذا حمل هذان السلكان الداخليان الإشارتين aa وbb (من أجل a,b∈{0,1}a,b \in \{0,1\})، فإن الإشارة على الأسلاك الخارجية ستكون OR(a,b)OR(a,b). وتُعرَّف بوابتا AND و_NOT_ بالمثل. أما المُدخلات فليس لها إلا أسلاك خارجة. إذا عيّننا مُدخلًا معيّنًا على القيمة a∈{0,1}a\in \{0,1\}، فإن هذه القيمة تنتشر على كل الأسلاك الخارجة منه. كما نُعيّن بعض البوابات لتكون بوابات مُخرَج، وتُقابل قيمتها نتيجة تقييم الدائرة. على سبيل المثال، يقدّم الشكل التوضيحي دائرة كهذه للدالة XORXOR، وفق مثال موسّع: حساب XOR من AND وOR وNOT. نُقيّم دائرة منطقية ذات nn مُدخلًا CC على مُدخل x∈{0,1}nx\in \{0,1\}^n بوضع بتات xx على المُدخلات، ثم نشر القيم على الأسلاك حتى نصل إلى مُخرج، انظر الشكل التوضيحي.

ملاحظة — التجسيد الفيزيائي للدوائر المنطقية

الدوائر المنطقية هي نموذج رياضي لا يقابل بالضرورة جسماً ماديًا، لكنها قابلة للتنفيذ فيزيائيًا. في التنفيذات الفيزيائية للدوائر، تُنفَّذ الإشارة غالبًا بواسطة فرق الجهد الكهربائي، أو الجهد الكهربائي (voltage)، على سلك، حيث يُفسَّر، مثلًا، الجهد الأعلى من مستوى معيّن على أنه قيمة منطقية 11، والأدنى من مستوى معيّن على أنه قيمة منطقية 00. يناقش التنفيذات الفيزيائية لأجهزة الحوسبة (استطراد) التنفيذات الفيزيائية للدوائر المنطقية (مع أمثلة تشمل استعمال الإشارات الكهربائية كما في الدوائر السيليكونية، وكذلك التنفيذات الحيوية والميكانيكية).

/images/introtcs/lec_03_computation-8.webp

تمرين محلول — دالة التساوي التام

عرِّف ALLEQ:{0,1}4→{0,1}ALLEQ:\{0,1\}^4 \rightarrow \{0,1\} لتكون الدالة التي على المُدخل x∈{0,1}4x\in \{0,1\}^4 تُنتج 11 إذا ونحو فقط إذا كان x0=x1=x2=x3x_0=x_1=x_2=x_3. أعطِ دائرة منطقية لحساب ALLEQALLEQ.

الحل

هناك طريقة أخرى لوصف الدالة ALLEQALLEQ: أنها تُنتج 11 على مُدخل x∈{0,1}4x\in \{0,1\}^4 إذا ونحو فقط إذا كان x=04x = 0^4 أو x=14x=1^4. ويمكننا صياغة الشرط x=14x=1^4 على صورة x0∧x1∧x2∧x3x_0 \wedge x_1 \wedge x_2 \wedge x_3، ويمكن حسابه باستعمال ثلاث بوابات AND. وبالمثل يمكننا صياغة الشرط x=04x=0^4 على صورة x‾0∧x‾1∧x‾2∧x‾3\overline{x}_0 \wedge \overline{x}_1 \wedge \overline{x}_2 \wedge \overline{x}_3، ويمكن حسابه باستعمال أربع بوابات NOT وثلاث بوابات AND. ومُخرج ALLEQALLEQ هو OR لهذين الشرطين، أي الدائرة ذات 4 بوابات NOT و6 بوابات AND وبوابة OR واحدة المعروضة في الشكل التوضيحي.

/images/introtcs/lec_03_computation-9.webp

الدوائر المنطقية: تعريف رسمي

عرّفنا الدوائر المنطقية إجرائيًا بأنها ما يُحصل عليه بوصل بوابات AND و_OR_ و_NOT_ بواسطة أسلاك لإنتاج مُخرج من مُدخل. غير أنه، لكي نتمكّن من إثبات مبرهنات حول وجود دوائر منطقية تحسب دوالّ متنوّعة أو عدم وجودها، نحتاج إلى:

  1. أن نعرّف الدائرة المنطقية رسميًا بوصفها كائنًا رياضيًا.

  2. أن نعرّف رسميًا معنى قول الدائرة CC إنها تحسب الدالة ff.

وسنقوم الآن بذلك. سنعرّف الدائرة المنطقية بأنها رسم بياني موجَّه غير دوري (Directed Acyclic Graph) مُوسَم. وتُقابل رؤوس (vertices) الرسم البياني بوابات الدائرة ومُدخلاتها، بينما تُقابل حواف (edges) الرسم البياني الأسلاك. السلك الذي ينتقل من مُدخل أو بوابة uu إلى بوابة vv في الدائرة يقابل حافة موجَّهة بين الرأسين المقابلين. أما المُدخلات فهي رؤوس لا حواف داخلية لها، بينما لكل بوابة العدد المناسب من الحواف الداخلية بحسب الدالة التي تحسبها. (أي أن بوابتَي AND و_OR_ لهما جيران داخليان اثنان، بينما لبوابة NOT جار داخلي واحد.) والتعريف الرسمي هو ما يلي (انظر أيضًا الشكل التوضيحي):

الدائرة المنطقية (Boolean Circuit) هي رسم بياني موجَّه غير دوري (DAG) مُوسَم. ولديها  رأس مُدخل، تُعلَّم بـ ,,  ولا حواف داخلية لها، وبقية الرؤوس هي بوابات. ولبوابات AND و_OR_ و_NOT_ على التوالي حافتان داخليتان، وحافتان داخليتان، وحافة داخلية واحدة. وإذا كان للدائرة  مُخرجات، فإن  من البوابات تُعرف بـ_المُخرجات_ وتُعلَّم بـ ,,. وحين نُقيّم دائرة  على مُدخل ، نبدأ بتعيين قيمة رؤوس المُدخلات إلى  ثم ننشر القيم، فنُسند إلى كل بوابة  نتيجة تطبيق عملية  على قيم جيران  الداخلة. ومُخرج الدائرة هو القيمة المُسندة إلى بوابات الخرج.

تعريف — الدوائر المنطقية

لتكن n,m,sn,m,s أعدادًا صحيحة موجبة بحيث s≥ms \geq m. الدائرة المنطقية (Boolean circuit) ذات nn مُدخلًا وmm مُخرجًا وss بوابة هي رسم بياني موجَّه غير دوري (DAG) مُوسَم G=(V,E)G=(V,E) له s+ns+n رأسًا، يستوفي الخصائص التالية:

  • بالضبط nn من الرؤوس ليس لها جيران داخلية. وتُعرف هذه الرؤوس بـ_المُدخلات_ وتُوسَم بـnn من الوسوم X[00], …\ldots, X[n−1n-1]. ولكل مُدخل جار خارج واحد على الأقل.

  • الرؤوس الـss الأخرى تُعرف بـ_البوابات_. وتُوسَم كل بوابة بـ∧\wedge أو ∨\vee أو ¬\neg. والبوابات الموسومة بـ∧\wedge (AND) أو ∨\vee (OR) لها جيران داخليان اثنان. والبوابات الموسومة بـ¬\neg (NOT) لها جار داخلي واحد. وسنسمح بالحواف الموازية.[1]

  • بالضبط mm من البوابات تُوسَم أيضًا بـmm من الوسوم Y[00], …\ldots, Y[m−1m-1] (بالإضافة إلى وسمها ∧\wedge/∨\vee/¬\neg). وتُعرف هذه بـ_المُخرجات_.

و_حجم_ الدائرة المنطقية هو العدد ss من البوابات التي تحتوي عليها.

توقّف وتأمّل

هذا تعريف رياضي غير تافه، ومن ثمّ من المفيد أن تقرأه ببطء وتدقيق. فكما في كل التعريفات الرياضية، فإننا نستعمل كائنًا رياضيًا معروفًا --- رسمًا بيانيًا موجَّهًا غير دوري (DAG) --- لتعريف كائن جديد، هو الدائرة المنطقية. قد يكون هذا وقتًا مناسبًا لمراجعة بعض الخصائص الأساسية للرسوم البيانية غير الدورية، وخصوصية أنها يمكن أن تُرتَّب طوبولوجيًّا، انظر مثال موسّع: الفرز الطوبولوجي.

إذا كانت CC دائرة ذات nn مُدخلًا وmm مُخرجًا، وx∈{0,1}nx\in \{0,1\}^n، فيمكننا حساب مُخرج CC على المُدخل xx بالطريقة الطبيعية: نُسند إلى رؤوس المُدخلات X[00], …\ldots, X[n−1n-1] القيم x0,…,xn−1x_0,\ldots,x_{n-1}، ونطبّق كل بوابة على قيم جيرانها الداخلة، ثم نُخرج القيم المقابلة لرؤوس الخرج. ويُعرَّف ذلك رسميًا على النحو التالي:

تعريف — حساب دالة بواسطة دائرة منطقية

لتكن CC دائرة منطقية ذات nn مُدخلًا وmm مُخرجًا. لكل x∈{0,1}nx\in \{0,1\}^n، يُعرَّف مُخرج CC على المُدخل xx، ويُرمز له بـC(x)C(x)، بأنه نتيجة العملية التالية:

لتكن h:V→Nh:V \rightarrow \N هي التوزيع الطبقي الأدنى (minimal layering) لـCC (أي الفرز الطوبولوجي، انظر مبرهنة — الطبقيّة الأصغرية متفرّدة). ولتكن LL أكبر طبقة في hh، ولكل ℓ=0,1,…,L\ell=0,1,\ldots,L نفعل ما يلي:

  • لكل vv في الطبقة ℓ\ell (أي vv بحيث h(v)=ℓh(v)=\ell) نفعل:

    • إذا كان vv رأس مُدخل موسومًا بـX[ii] لمُدخل ما i∈[n]i\in [n]، فإننا نُسند إلى vv القيمة xix_i.

    • إذا كان vv رأس بوابة موسومًا بـ∧\wedge وله جيران داخليان u,wu,w، فإننا نُسند إلى vv ناتج AND للقيمتين المُسندتين إلى uu وww. (وبما أن uu وww جيران داخليان لـvv، فإنهما في طبقة أدنى من vv، وبذلك تكون قيمتُهما قد سُندت بالفعل.)

    • إذا كان vv رأس بوابة موسومًا بـ∨\vee وله جيران داخليان u,wu,w، فإننا نُسند إلى vv ناتج OR للقيمتين المُسندتين إلى uu وww.

    • إذا كان vv رأس بوابة موسومًا بـ¬\neg وله جار داخلي واحد uu، فإننا نُسند إلى vv نفي القيمة المُسندة إلى uu.

  • نتيجة هذه العملية هي القيمة y∈{0,1}my\in \{0,1\}^m بحيث إن كان j∈[m]j\in [m] فإن yjy_j هي القيمة المُسندة إلى الرأس الموسوم بـY[jj].

ولتكن f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m. نقول إن الدائرة CC تحسب ff إذا كان لكل x∈{0,1}nx\in \{0,1\}^n، لدينا C(x)=f(x)C(x)=f(x).

ملاحظة — تفاصيل دقيقة في الدوائر المنطقية (اختياري)

في صياغة تعريف — الدوائر المنطقية، اتخذنا بعض الخيارات التقنية التي ليست بالغة الأهمية، لكنها ستكون مريحة لنا لاحقًا. معنى وجود حواف موازية أن بوابة AND أو OR المعنونة uu يمكن أن يكون كلا جيريها الداخلي هو البوابة vv نفسها. وبما أن AND(a,a)=OR(a,a)=aAND(a,a)=OR(a,a)=a لكل a∈{0,1}a\in \{0,1\}، فإن هذه الحواف الموازية لا تساعد في حساب قيم جديدة في الدوائر ذات بوابات AND/OR/NOT. غير أننا سنرى لاحقًا دوائر بمجموعات بوابات أكثر عمومية. وشرط أن يكون لكل رأس مُدخل جار خارج واحد على الأقل ليس بالغة الأهمية أيضًا، لأن يمكننا دائمًا إضافة «بوابات وهمية» تلامس هذه المُدخلات. لكنه مريح لأنها تضمن أن عدد المُدخلات في الدائرة (بما أن لكل بوابة جيران داخليان على الأكثر) لا يتجاوز أبدًا ضعف حجمها.

البرامج المستقيمة

لقد رأينا طريقتين لوصف كيفية حساب دالة ff باستخدام AND و_OR_ و_NOT_:

  • الدائرة المنطقية (Boolean circuit)، المعرَّفة في تعريف — الدوائر المنطقية، تحسب ff بوصل بوابات AND و_OR_ و_NOT_ بالمُدخلات بواسطة الأسلاك.

  • ويمكننا أيضًا وصف حساب كهذا باستخدام برنامج مستقيم (straight-line program) تتكون أسطره من الشكل foo = AND(bar,blah) وfoo = OR(bar,blah) وfoo = NOT(bar)، حيث foo وbar وblah أسماء متغيّرات. (ونسمّيه برنامجًا مستقيمًا لأنه لا يحتوي على حلقات تكرار ولا على تعليمات تفرّع (مثل if/then).)

ولجعل التعريف الثاني أدقّ، سنعرّف الآن لغة برمجة تكافئ الدوائر المنطقية. ونسميّ لغة البرمجة هذه لغة البرمجة AON-CIRC (يرمز "AON" إلى AND/OR/NOT، ويرمز "CIRC" إلى الدائرة).

على سبيل المثال، إليك برنامج AON-CIRC يُخرج، على مُدخل x∈{0,1}2x \in \{0,1\}^2، القيمة x0∧x1‾\overline{x_0 \wedge x_1} (أي عملية NOTNOT المطبَّقة على AND(x0,x1)AND(x_0,x_1):

temp = AND(X[0],X[1])
Y[0] = NOT(temp)

لغة AON-CIRC ليست لغة برمجة عملية: فقد صُمِّمت لأغراض تعليمية فحسب، بوصفها وسيلة لنمذجة الحوسبة بوصفها تركيبًا لدوال ANDAND وOROR وNOTNOT. غير أنها مع ذلك يمكن تنفيذها بسهولة على الحاسوب.

وبناءً على هذا المثال، لعلك تستطيع بالفعل أن تخمّن كيف تكتب برنامجًا لحساب (مثلًا) x0∧x1∨x2‾x_0 \wedge \overline{x_1 \vee x_2}، وعمومًا كيف تترجم دائرة منطقية إلى برنامج AON-CIRC. غير أننا، لأننا نريد إثبات عبارات رياضية عن برامج AON-CIRC، سنحتاج إلى تعريف دقيق للغة البرمجة AON-CIRC. وقد تكون المواصفات الدقيقة للّغات البرمجة طويلة ومملّة في أحيانٍ كثيرة،[2] لكنها بالغة الأهمية للتنفيذات الآمنة والموثوقة. ولحسن الحظ، فإن لغة البرمجة AON-CIRC بسيطة بما يكفي لنعرّفها رسميًا بألمٍ قليل.

مواصفة لغة البرمجة AON-CIRC

برنامج AON-CIRC هو تسلسل من النصوص، نسمّيها «أسطرًا»، يستوفي الشروط التالية:

  • لكل سطر إحدى الصور التالية: foo = AND(bar,baz) أو foo = OR(bar,baz) أو foo = NOT(bar)، حيث foo وbar وbaz معرِّفات متغيّرات. (ونتبع عرف لغات البرمجة الشائع في استعمال أسماء مثل foo وbar وbaz بديلًا عن معرِّفات عامة.) ويقابل السطر foo = AND(bar,baz) عملية إسناد القيمة المنطقية لـ AND إلى المتغيّر foo من قيم المتغيّرين bar وbaz. ومثلًا، يقابل foo = OR(bar,baz) وfoo = NOT(bar) عمليتي OR المنطقية وNOT المنطقية.

  • يمكن أن يكون معرِّف المتغيّر في لغة البرمجة AON-CIRC أي تركيبة من الحروف والأرقام والشرطات السفلية والأقواس. وهناك نوعان خاصان من المتغيّرات:

    • المتغيّرات من الشكل X[ii], مع i∈{0,1,…,n−1}i \in \{0,1,\ldots, n-1\} تُعرف بـ_متغيّرات المُدخل_.
    • المتغيّرات من الشكل Y[jj] تُعرف بـ_متغيّرات الخرج_.
  • برنامج AON-CIRC الصحيح PP يتضمّن متغيّرات مُدخل من الشكل X[00],…\ldots,X[n−1n-1] ومتغيّرات خرج من الشكل Y[00],…\ldots, Y[m−1m-1] حيث n,mn,m عددان طبيعيان. ونقول إن nn هو عدد مُدخلات البرنامج PP وmm هو عدد مُخرجاته.

  • في برنامج AON-CIRC الصحيح، يجب أن تكون المتغيّرات الموجودة في الطرف الأيمن من معامل الإسناد في كل سطر إمّا متغيّرات مُدخل وإمّا متغيّرات فُسندت لها قيمة في سطر سابق.

  • إذا كان PP برنامج AON-CIRC صحيحًا ذو nn مُدخلًا وmm مُخرجًا، فإن مُخرج PP على المُدخل xx لكل x∈{0,1}nx\in \{0,1\}^n هو النص y∈{0,1}my\in \{0,1\}^m المعرَّف على النحو التالي:

    • نهيّئ متغيّرات المُدخل X[00],…\ldots,X[n−1n-1] على القيم x0,…,xn−1x_0,\ldots,x_{n-1}
    • ننفّذ أسطر العمليات في PP واحدًا تلو الآخر بالترتيب، وفي كل سطر نُسند إلى المتغيّر في الطرف الأيسر من معاملات الإسناد قيمة العملية الموجودة في الطرف الأيمن.
    • لتكن y∈{0,1}my\in \{0,1\}^m هي قيم متغيّرات الخرج Y[00],…\ldots, Y[m−1m-1] في نهاية التنفيذ.
  • نرمز لمُخرج PP على المُدخل xx بـP(x)P(x).

  • حجم برنامج AON circ PP هو عدد الأسطر التي يحتوي عليها. (ولعلّ القارئ يلاحظ أن هذا يوافق تعريفنا لحجم الدائرة بعدد البوابات التي تحتوي عليها.)

والآن وقد وصفنا برامج AON-CIRC رسميًا، يمكننا أن نعرّف معنى قول برنامج AON-CIRC PP إنه يحسب دالة ff:

تعريف — حساب دالة بواسطة برامج AON-CIRC

لتكن f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow\{0,1\}^m، وليكن PP برنامج AON-CIRC صحيحًا ذو nn مُدخلًا وmm مُخرجًا. نقول إن PP يحسب ff إذا كان P(x)=f(x)P(x)=f(x) لكل x∈{0,1}nx\in \{0,1\}^n.

يعطي التمرين المحلول التالي مثالًا على برنامج AON-CIRC.

تمرين محلول

لتُعتبر الدالة التالية CMP:{0,1}4→{0,1}CMP:\{0,1\}^4 \rightarrow \{0,1\} التي، على أربعة بتات مُدخلة a,b,c,d∈{0,1}a,b,c,d\in \{0,1\}، تُنتج 11 إذا ونحو فقط إذا كان العدد الذي تمثّله (a,b)(a,b) أكبر من العدد الذي تمثّله (c,d)(c,d). أي أن CMP(a,b,c,d)=1CMP(a,b,c,d)=1 إذا ونحو فقط إذا كان 2a+b>2c+d2a+b>2c+d.

اكتب برنامج AON-CIRC يحسب CMPCMP.

الحل

كتابة برنامج كهذا مملّة لكنها ليست صعبة حقًا. لمقارنة عددين نقارن أولًا أكثر خانتين أهمية فيهما، ثم ننتقل إلى الخانة التالية وهكذا دواليك. وفي حالتنا هنا حيث للأرقام خانتان ثنائيتان فقط، تكون هذه المقارنات بسيطة إلى حدٍّ خاص. يكون العدد الذي تمثّله (a,b)(a,b) أكبر من العدد الذي تمثّله (c,d)(c,d) إذا ونحو فقط إذا وقع أحد الشرطين التاليين:

  1. البت الأكثر أهمية aa في (a,b)(a,b) أكبر من البت الأكثر أهمية cc في (c,d)(c,d).

أو

  1. البتان الأكثر أهمية aa وcc متساويتان، لكن b>db>d.

وهناك طريقة أخرى للتعبير عن الشرط نفسه وهي التالية: العدد (a,b)(a,b) أكبر من (c,d)(c,d) إذا ونحو فقط إذا كان a>ca>c OR (a≥ca\ge c AND b>db>d).

بالنسبة للخانات الثنائية α,β\alpha,\beta، فإن الشرط α>β\alpha>\beta هو ببساطة α=1\alpha=1 وβ=0\beta=0، أي AND(α,NOT(β))=1AND(\alpha,NOT(\beta))=1، والشرط α≥β\alpha\ge\beta هو ببساطة OR(α,NOT(β))=1OR(\alpha, NOT(\beta))=1. ويمكن استعمال هذه الملاحظات مجتمعةً للحصول على برنامج AON-CIRC التالي الذي يحسب CMPCMP:

# Compute CMP:{0,1}^4-->{0,1}
# CMP(X)=1 iff 2X[0]+X[1] > 2X[2] + X[3]
temp_1 = NOT(X[2])
temp_2 = AND(X[0],temp_1)
temp_3 = OR(X[0],temp_1)
temp_4 = NOT(X[3])
temp_5 = AND(X[1],temp_4)
temp_6 = AND(temp_5,temp_3)
Y[0] = OR(temp_2,temp_6)

ويمكننا أيضًا تقديم هذا البرنامج المكوَّن من 8 أسطر بوصفه دائرة ذات 8 بوابات، انظر الشكل التوضيحي.

/images/introtcs/lec_03_computation-10.webp

إثبات تكافؤ برامج AON-CIRC والدوائر المنطقية

وسنثبت الآن رسميًا أن برامج AON-CIRC والدوائر المنطقية لهما القدرة نفسها تمامًا:

مبرهنة — تكافؤ الدوائر والبرامج المستقيمة

لتكن f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m وs≥ms \geq m عددًا ما. حينئذٍ يكون ff قابلًا للحساب بواسطة دائرة منطقية ذات ss بوابة إذا ونحو فقط كان ff قابلًا للحساب بواسطة برنامج AON-CIRC من ss سطرًا.

الفكرة بسيطة — برامج AON-CIRC والدوائر المنطقية ليست سوى طريقتين مختلفتين لوصف العملية الحسابية نفسها تمامًا. على سبيل المثال، تقابل بوابة AND في دائرة منطقية حساب AND لقيمتين حُسبتا سابقًا. وفي برنامج AON-CIRC سيقابل ذلك السطر الذي يخزّن في متغيّر ناتج AND لمتغيّرين حُسبا سابقًا.

توقّف وتأمّل

هذا البرهان لـمبرهنة — تكافؤ الدوائر والبرامج المستقيمة بسيط في جوهره، لكن كل التفاصيل التي يحتويها تجعل قراءته أثقل قليلًا. غالبًا ما يكون من الأفضل لك أن تحاول استنتاجه بنفسك قبل قراءته. ويحتوي مستودعنا على GitHub على «برهان بـ Python» لـمبرهنة — تكافؤ الدوائر والبرامج المستقيمة: تنفيذ الدالتين circuit2prog وprog2circuits اللتين تربطان الدوائر المنطقية ببرامج AON-CIRC وبالعكس.

برهان

لتكن f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m. وبما أن المبرهنة عبارة عن «إذا وفقط إذا»، فإثباتها يتطلب بيان الاتجاهين: ترجمة برنامج AON-CIRC يحسب ff إلى دائرة تحسب ff، وترجمة دائرة تحسب ff إلى برنامج AON-CIRC يفعل ذلك.

نبدأ بالاتجاه الأول. وليكن PP برنامج AON-CIRC يحسب ff. نعرّف دائرة CC على النحو التالي: يكون للدائرة nn مُدخلًا وss بوابة. ولكل i∈[s]i \in [s]، إذا كان سطر العمليات رقم ii على الصورة foo = AND(bar,blah)، فإن البوابة رقم ii في الدائرة ستكون بوابة AND موصولة بالبوابتين jj وkk حيث تقابل jj وkk آخر سطرين قبل ii كُتب فيهما المتغيّران bar وblah (على التوالي). (فمثلًا، إذا كان i=57i=57 وكان آخر سطر كُتب فيه bar هو 3535 وآخر سطر كُتب فيه blah هو 1717، فإن الجارين الداخليين للبوابة 5757 هما البوابتان 3535 و1717.) وإذا كان أحد bar أو blah متغيّر مُدخل، فإننا نوصل البوابة برأس المُدخل المقابل بدلًا من ذلك. وإذا كان foo متغيّر خرج من الشكل Y[jj], فإننا نضيف الوسم نفسه إلى البوابة المقابل لنُعلّمها بوابة خرج. ونجري العمليات المماثلة إذا تضمّن السطر رقم ii عملية OR أو عملية NOT (مع أننا نستعمل في الحالة الثانية بوابة NOT المقابل، ولها عندها جار داخلي واحد بدل اثنين). ولكل مُدخل x∈{0,1}nx\in \{0,1\}^n، إذا نفّذنا البرنامج PP على xx، فإن القيمة المحسوبة في السطر رقم ii هي بالضبط القيمة التي ستُسنَد إلى البوابة رقم ii إذا قيّمنا الدائرة CC على xx. وبذلك يكون C(x)=P(x)C(x)=P(x) لكل x∈{0,1}nx\in \{0,1\}^n.

وفي الاتجاه الآخر، لتكن CC دائرة ذات ss بوابة وnn مُدخلًا تحسب الدالة ff. نرتّب البوابات وفق ترتيب طوبولوجي ونكتبها v0,…,vs−1v_0,\ldots,v_{s-1}. ويمكننا الآن إنشاء برنامج PP من ss سطر عمليات كالتالي. لكل i∈[s]i\in [s]، إذا كانت viv_i بوابة AND بجارين داخليين vj,vkv_j,v_k، فإننا نضيف إلى PP سطرًا على الصورة temp_ii = AND(temp_jj,temp_kk), إلا إذا كان أحد الرأسين رأس مُدخل أو بوابة خرج، وعندئذٍ نغيّر ذلك إلى الصورة X[.] أو Y[.] بحسب ما يلائم. ولأننا نعمل بترتيب طوبولوجي، فإننا نضمن أن الجيران الداخليين vjv_j وvkv_k يقابلان متغيّرات فُسندت لها قيمة بالفعل. ونفعل الشيء نفسه مع بوابتَي OR وNOT. ومرة أخرى، يمكن التحقق أن القيمة P(x)P(x) ستساوي C(x)C(x) لكل مُدخل xx، وبذلك يحسب البرنامج الدالة نفسها التي تحسبها الدائرة. (ولاحظ أن CC دائرة صحيحة، وفقًا لـتعريف — الدوائر المنطقية، فإن لكل رأس مُدخل في CC جارًا خارجًا واحدًا على الأقل، وهناك بالضبط mm بوابة خرج موسومة بـ0,…,m−10,\ldots,m-1؛ وبالتالي فإن جميع المتغيّرات X[0] و…\ldots وX[n−1n-1] وY[0] و…\ldots وY[m−1m-1] ستظهر في البرنامج PP.)

/images/introtcs/lec_03_computation-11.webp

التنفيذات الفيزيائية لأجهزة الحوسبة (استطراد)

الحوسبة مفهوم مجرد يختلف عن تنفيذاته الفيزيائية. ورغم أن معظم أجهزة الحوسبة الحديثة تُحصل بوَصْل البوابات المنطقية على ترانزستورات قائمة على أشباه الموصلات، إلا أن الناس على مدى التاريخ قد حسبوا باستخدام تشكيلة ضخمة من الآليات، منها الأنظمة الميكانيكية، والغازات والسوائل (المعروفة بـ_التحكّم الموائع_ (fluidics))، والعمليات الحيوية والكيميائية، بل وحتى الكائنات الحيّة (مثلًا، انظر الشكل التوضيحي أو هذا الفيديو لتعرف كيف يمكن استعمال السرطان أو العفن المخاطي في إجراء حسابات).

في هذا القسم سنستعرض بعض هذه التنفيذات، وذلك أولًا لتقدير كيف يمكن ترجمة الدوائر المنطقية مباشرةً إلى العالم الفيزيائي دون المرور بمنظومة العمارة وأنظمة التشغيل والمترجِمات كاملةً، وثانيًا للتأكيد على أن المعالجات القائمة على السيليكون ليست بأي حال الطريقة الوحيدة لإجراء الحوسبة. وفعلًا، كما سنرى في الحساب الكمّي، فإن خطًّا بحثيًا ممتعًا جدًا في الآونة الأخيرة يتطلّب استعمال وسائل مختلفة للحوسبة تتيح لنا الاستفادة من الآثار الميكانيكية الكمّية (quantum mechanical effects) لتفعيل أنواع مختلفة من الخوارزميات.

/images/introtcs/lec_03_computation-12.webp

الترانزستورات

يمكن تصوّر الـ_ترانزستور_ (transistor) على أنه دائرة كهربائية لها مُدخلان يُعرفان بـ_المصدر_ (source) و_البوابة_ (gate)، ومُخرج يُعرف بـ_المصبّ_ (sink). تتحكّم البوابة في ما إذا كان التيار يفيض من المصدر إلى المصبّ. وفي الـ_ترانزستور القياسي_، إذا كانت البوابة «مُفعَّلة» (ON) أمكن للتيار أن يفيض من المصدر إلى المصبّ، وإذا كانت «معطَّلة» (OFF) لم يستطع. وفي الـ_ترانزستور التكاملي_ (complementary transistor) يكون هذا معكوسًا: إذا كانت البوابة «معطَّلة» أمكن للتيار أن يفيض من المصدر إلى المصبّ، وإذا كانت «مُفعَّلة» لم يستطع.

/images/introtcs/lec_03_computation-13.webp

هناك عدة طرق لتنفيذ منطق الترانزستور. فمثلًا، يمكننا استعمال الصنابير لتنفيذه بواسطة ضغط الماء (انظر الشكل التوضيحي). وقد يبدو هذا مجرد فضول، لكن هناك مجال يُعرف بـالتحكّم الموائع (fluidics) يعنى بتنفيذ العمليات المنطقية باستعمال السوائل أو الغازات. ومن بين دوافعه العمل في ظروف بيئية قاسية مثل الفضاء أو أرض المعركة، حيث لن تصمد المعدات الإلكترونية القياسية.

تستخدم التنفيذات القياسية للترانزستورات تيارًا كهربائيًا. ومن أقدم التنفيذات ما استخدم أنابيب التفريغ (vacuum tubes). وكما يدل اسمها، فإن أنبوب التفريغ أنبوب لا يحتوي على شيء (أي فراغ)، حيث يمكن للإلكترونات مبدئيًا أن تفيض بحرية من المصدر (سلك) إلى المصبّ (لوح). غير أنه توجد بين الاثنين بوابة (شبكة)، وتعديل جهدها يمكن أن يعوق فيض الإلكترونات.

كانت أنابيب التفريغ المبكّرة بحجم لمبات المصابيح تقريبًا، وكانت تتشابه معها إلى حدٍّ كبير. وفي الخمسينيات حلّت محلّها الترانزستورات، التي تنفّذ المنطق نفسه باستعمال أشباه الموصلات (semiconductors)، وهي مواد لا توصّل الكهرباء عادةً ولكن يمكن تعديل ناقليتها والتحكم بها بإدخال شوائب (وتُسمّى «التخليق» (doping)) وتطبيق مجال كهربائي خارجي (ويُعرف ذلك بـ_أثر المجال_ (field effect)). وفي الستينيات بدأت تُنفَّذ الحواسيب باستعمال الدوائر المتكاملة (integrated circuits) التي سمحت بكثافة أعلى بكثير. وفي عام 1965 تنبّأ جوردون مور بأن عدد الترانزستورات في كل دائرة متكاملة سيتضاعف كل عام (انظر الشكل التوضيحي)، وأن هذا سيؤدي إلى «عجائب مثل الحواسيب المنزلية —أو على الأقلّ أطراف طرفية موصولة بحاسوب مركزي— وضوابط آلية للسيارات، ومعدّات اتصالات شخصية محمولة». ومنذ ذلك الحين، ظلّت هذه «قانون مور» المزعوم (في نسخ معدَّلة منه) يسير بقوة، وإن كان النمو الأُسّي لا يمكن أن يستمر إلى الأبد، فبعض القيود الفيزيائية بدأت بالفعل تظهر للعلن.

/images/introtcs/lec_03_computation-14.webp

/images/introtcs/lec_03_computation-15.webp

/images/introtcs/lec_03_computation-16.webp

البوابات المنطقية من الترانزستورات

يمكننا استعمال الترانزستورات لتنفيذ دوال منطقية متنوّعة مثل ANDAND وOROR وNOTNOT. ولكل بوابة ذات مُدخلين G:{0,1}2→{0,1}G:\{0,1\}^2 \rightarrow \{0,1\}، يكون هذا التنفيذ نظامًا فيه سلكا مُدخل x,yx,y وسلك مُخرج واحد zz، بحيث إذا طابقنا الجهد العالي بـ«11» والجهد المنخفض بـ«00»، فإن السلك zz يساوي «11» إذا ونحو فقط كان تطبيق GG على قيمتي السلكين xx وyy يساوي 11 (انظر الشكل التوضيحي والشكل التوضيحي). وهذا يعني أن ما دام هناك دائرة من نوع AND/OR/NOT تحسب الدالة g:{0,1}n→{0,1}mg:\{0,1\}^n \rightarrow \{0,1\}^m، فيمكننا حساب gg في العالم الفيزيائي باستعمال الترانزستورات أيضًا.

تنفيذ البوابات المنطقية باستعمال الترانزستورات. الشكل مأخوذ من موقع روري مانغلز.

تنفيذ بوابة NAND (انظر nandsec) باستعمال الترانزستورات.

الحوسبة البيولوجية

يمكن أن تستند الحوسبة إلى الأنظمة البيولوجية أو الكيميائية. فمثلًا، ينتج lac operon_ الأنزيمات اللازمة لهضم اللاكتوز فقط إذا تحقّقت الشروط x∧(¬y)x \wedge (\neg y)، حيث xx تعني «اللاكتوز موجود» وyy تعني «الغلوكوز موجود». تمكن الباحثون من إنشاء ترانزستورات، ومنها بوابات منطقية، قائمة على جزيئات الحمض النووي (انظر أيضًا الشكل التوضيحي). وتتيح مشاريع مثل لغة برمجة Cello تحويل الدوائر المنطقية إلى تسلسلات حمض نووي ترمّز عمليات يمكن تنفيذها في الخلايا البكتيرية، انظر هذا الفيديو. وأحد دوافع حوسبة الحمض النووي هو تحقيق توازٍ أو كثافة تخزين أعلى؛ وآخر هو إنشاء «وكلاء بيولوجيين أذكياء» يمكن حقنهم في الأجساد فيتكاثرون ويصلحون الخلايا التالفة أو يقتلونها، والتي أضرّها مرضٌ مثل السرطان. وهي بالطبع لا تقتصر الحوسبة في الأنظمة البيولوجية على الحمض النووي: فحتى الأنظمة الأكبر مثل أسراب الطيور يمكن اعتبارها عمليات حسابية.

أداء البوابات المنطقية القائمة على الحمض النووي. الشكل مأخوذ من ورقة Bonnet et al، Science، 2013.

الأوتوماتا الخلوية ولعبة الحياة

الأوتوماتا الخلوية (cellular automata) نموذج لنظام مؤلَّف من تسلسل من الخلايا، لكل منها حالة متناهية محتملة. وفي كل خطوة تحدّث الخلية حالتها بناءً على حالات خلاياها المجاورة وعلى قواعد بسيطة. وكما سنناقش لاحقًا في هذا الكتاب (انظر الآليات الخلوية)، فإن الأوتوماتا الخلوية مثل «لعبة الحياة» لكونيواي يمكن استعمالها لمحاكاة البوابات الحسابية.

بوابة AND باستعمال إعداد من «لعبة الحياة». الشكل مأخوذ من ورقة جان-فيليب رينار.

الشبكات العصبية

أحد أجهزة الحوسبة التي نحملها جميعًا هو دماغنا نفسه. وقد خدمت الأدمغة البشرية البشرية على مدى التاريخ، فأنجزت حسابات تمتدّ من التمييز بين الفرائس والمفترسات، مرورًا بالاكتشافات العلمية والتحفّرات الفنية، وصولًا إلى تأليف رسائل ذكية من 280 حرفًا. ولا يزال العمل الدقيق للدماغ غير مفهوم تمامًا، لكن أحد النماذج الرياضية الشائعة له هو شبكة عصبية (neural network) كبيرة جدًا.

يمكن تصوّر الشبكة العصبية على أنها دائرة منطقية تستعمل بوابات أخرى غير ANDAND/OROR/NOTNOT بوصفها الأساس. فمثلًا، أحد الأسس التي يمكننا استعمالها هي بوابات العتبة (threshold gates). ولكل متجه w=(w0,…,wk−1)w= (w_0,\ldots,w_{k-1}) من الأعداد الصحيحة ولكل عدد صحيح tt (قد يكون بعضها أو كلها سالبة)، تكون دالة العتبة المقابلة لـw,tw,t هي الدالة Tw,t:{0,1}k→{0,1}T_{w,t}:\{0,1\}^k \rightarrow \{0,1\} التي تُسقط x∈{0,1}kx\in \{0,1\}^k على 11 إذا ونحو فقط إذا كان ∑i=0k−1wixi≥t\sum_{i=0}^{k-1} w_i x_i \geq t. فمثلًا، دالة العتبة Tw,tT_{w,t} المقابلة لـw=(1,1,1,1,1)w=(1,1,1,1,1) وt=3t=3 هي ببساطة دالة الأغلبية MAJ5MAJ_5 على {0,1}5\{0,1\}^5. ويمكن تصوّر بوابات العتبة على أنها تقريب لـ_خلايا العصبونات_ التي تشكّل جوهر أدمغة البشر والحيوانات. وفي التقريب الأول، للعصبون kk مُدخلات ومُخرج واحد، وينطلق العصبون «ناريًا» أي يُشغّل مُخرجه حين تتجاوز تلك الإشارات عتبةً ما.

تستعمل كثير من خوارزميات التعلّم الآلي الشبكات العصبية الاصطناعية التي لا يقصد منها محاكاة الأحياء بل تنفيذ مهمة حسابية ما، ولذلك فهي غير مقيّدة ببوابة عتبة أو ببوابات أخرى مستوحاة من الأحياء. وبوجه عام، تُوصف الشبكة العصبية بأنها تعمل على إشارات هي أعداد حقيقية لا قيم 0/10/1، حيث يُحصل مُخرج بوابة على مُدخلات x0,…,xk−1x_0,\ldots,x_{k-1} بتطبيق f(∑iwixi)f(\sum_i w_i x_i)، حيث f:R→Rf:\R \rightarrow \R هي دالة تنشيط (activation function) كوحدة الخطية المصحَّحة (ReLU) أو السيغمويد (Sigmoid) أو غيرها كثير (انظر الشكل التوضيحي). غير أنه، لأغراض مناقشتنا، كل ما سبق متكافئ (انظر أيضًا تمرين — NAND من دوال التنشيط). ويسرّنا أن نلاحظ أننا نستطيع اختصار حالة المُدخلات الحقيقية إلى مُدخلات ثنائية بتمثيل العدد الحقيقي في الأساس الثنائي، ثم ضرب وزن البت المقابل لرتبة الخانة ithi^{th} في 2i2^i.

دوال التنشيط الشائعة المستعملة في الشبكات العصبية، ومنها الوحدات الخطية المصحَّحة (ReLU) ودوال السيغمويد والظلّية. ويمكن اعتبار جميعها تقريبًا مستمرًا يبسّط دالة الخطوة. وكل هذه يمكن استعمالها لحساب بوابة NAND (انظر NANDsfromActivationfunctionex). وهذه الخاصية تمكّن الشبكات العصبية من حساب (تقريبًا) أي دالة يمكن حسابها بدائرة منطقية.

حاسوب مصنوع من الكرات والأنابيب

يمكننا تنفيذ الحوسبة بوسائط فيزيائية كثيرة أخرى، دون أي مكوّنات إلكترونية أو حيوية أو كيميائية. وقد تقدُّمت مقترحات كثيرة لحاسوبات ميكانيكية، يعود أقدمها على الأقل إلى آلات الحوسبة لغوتفريد لايبنتس في سبعينيات القرن السابع عشر، وإلى خطة تشارلز بابيج عام 1837 المحرك التحليلي الميكانيكي. ومن الأمثلة، يوضّح الشكل التوضيحي تنفيذًا مبسّطًا لبوابة NAND (نفي AND، انظر دالة NAND) باستعمال كرات تمرّ في أنابيب. ونمثّل قيمة منطقية في {0,1}\{0,1\} بزوج من الأنابيب بحيث يمرّ في أحدهما كرة بالضبط. ونسمّي أحد الأنابيب «أنبوب 00» والآخر «أنبوب 11»، وبذلك تحدّد هوية الأنبوب الذي توجد فيه الكرة القيمة المنطقية. وتقابل بوابة NAND جسمًا ميكانيكيًا له زوجان من الأنابيب الداخلة وزوج من الأنابيب الخارجة، بحيث لكل a,b∈{0,1}a,b \in \{0,1\}، إذا كانت كرتان تتدحرجان نحو الجسم في أنبوب aa من الزوج الأول وأنبوب bb من الزوج الثاني، فإن كرة ستتدحرج خارج الجسم في أنبوب NAND(a,b)NAND(a,b) من الزوج الخرج. وفعلًا، هناك حتى لعبة تعليمية متوفّرة تجاريًا تستعمل الكرات أساسًا للحوسبة، انظر الشكل التوضيحي.

/images/introtcs/lec_03_computation-20.webp

/images/introtcs/lec_03_computation-21.webp

لعبة Turing Tumble تحتوي تنفيذًا للبوابات المنطقية باستعمال الكرات.

دالة NAND

دالة NANDNAND هي دالة أخرى بسيطة بالغة النفع لتعريف الحوسبة. وهي الدالة التي تُسقط {0,1}2\{0,1\}^2 على {0,1}\{0,1\} والمعرَّفة بما يلي:

NAND(a,b)={0a=b=11otherwise  .NAND(a,b) = \begin{cases} 0 & a=b=1 \\ 1 & \text{otherwise} \end{cases}\;.

وكما يدل اسمها، فإن NANDNAND هي نفي AND (أي NAND(a,b)=NOT(AND(a,b))NAND(a,b)= NOT(AND(a,b)))، ولذلك يمكننا بوضوح حساب NANDNAND باستعمال ANDAND وNOTNOT. ومن اللافت أن الاتجاه المعاكس يصمد أيضًا:

مبرهنة — NAND تحسب AND وOR وNOT

يمكننا حساب ANDAND وOROR وNOTNOT بتركيب دالة NANDNAND وحدها.

برهان

نبدأ بالملاحظة التالية. لكل a∈{0,1}a\in \{0,1\} لدينا AND(a,a)=aAND(a,a)=a. وبذلك NAND(a,a)=NOT(AND(a,a))=NOT(a)NAND(a,a)=NOT(AND(a,a))=NOT(a). وهذا يعني أن NANDNAND تستطيع حساب NOTNOT. وبمبدأ «النفي المزدوج»، لدينا AND(a,b)=NOT(NOT(AND(a,b)))AND(a,b)=NOT(NOT(AND(a,b)))، ولذلك يمكننا استعمال NANDNAND لحساب ANDAND أيضًا. ومتى استطعنا حساب ANDAND وNOTNOT، استطعنا حساب OROR باستعمال «قانون دو مورغان»: OR(a,b)=NOT(AND(NOT(a),NOT(b)))OR(a,b)=NOT(AND(NOT(a),NOT(b))) (ويمكن أيضًا كتابتها a∨b=a‾∧b‾‾a \vee b = \overline{\overline{a} \wedge \overline{b}}) لكل a,b∈{0,1}a,b \in \{0,1\}.

توقّف وتأمّل

برهان مبرهنة — NAND تحسب AND وOR وNOT بسيط جدًا، غير أنك ينبغي أن تتأكد أنك (i) تفهم نص المبرهنة، و__(ii)__ تتابع برهانها. وعلى وجه الخصوص، ينبغي أن تتأكد أنك تفهم لماذا يكون قانون دو مورغان صحيحًا.

ويمكننا استعمال NANDNAND لحساب دوال أخرى كثيرة، كما يوضّح التمرين التالي.

تمرين محلول — حساب الأغلبية بـNAND

لتكن MAJ:{0,1}3→{0,1}MAJ: \{0,1\}^3 \rightarrow \{0,1\} الدالة التي على المُدخل a,b,ca,b,c تُنتج 11 إذا ونحو فقط إذا كان a+b+c≥2a+b+c \geq 2. بيّن كيف تحسب MAJMAJ بتركيب من عمليات NANDNAND.

الحل

تذكّر أن المعادلة تنص على أن

MAJ(x0,x1,x2)=OR( AND(x0,x1)  ,  OR(AND(x1,x2)  ,  AND(x0,x2)) )  . MAJ(x_0,x_1,x_2) = OR\left(\, AND(x_0,x_1)\;,\; OR \bigl( AND(x_1,x_2) \;,\; AND(x_0,x_2) \bigr) \, \right) \;.

ويمكننا استعمال مبرهنة — NAND تحسب AND وOR وNOT لاستبدال كل مواضع ANDAND وOROR بـNANDNAND. على وجه التحديد، يمكننا استعمال التكافؤات AND(a,b)=NOT(NAND(a,b))AND(a,b)=NOT(NAND(a,b)) وOR(a,b)=NAND(NOT(a),NOT(b))OR(a,b)=NAND(NOT(a),NOT(b)) وNOT(a)=NAND(a,a)NOT(a)=NAND(a,a) لاستبدال الطرف الأيمن من المعادلة بتعبير يتضمّن NANDNAND وحدها، مما ينتج أن MAJ(a,b,c)MAJ(a,b,c) تكافئ التعبير (الذي هو بعض الشيء مُعقّد)

NAND( NAND( NAND(NAND(a,b),NAND(a,c)),NAND(NAND(a,b),NAND(a,c)) ),NAND(b,c) ) \begin{gathered} NAND \biggl(\, NAND\Bigl(\, NAND\bigl(NAND(a,b),NAND(a,c)\bigr), \\ NAND\bigl(NAND(a,b),NAND(a,c)\bigr)\, \Bigr),\\ NAND(b,c) \, \biggr) \end{gathered}

ويمكن التعبير عن الصيغة نفسها أيضًا بدائرة ببوابات NAND، انظر الشكل التوضيحي.

/images/introtcs/lec_03_computation-23.webp

دوائر NAND

نعرّف دوائر NAND بأنها الدوائر التي كل بوابات فيها عمليات NAND. وتقابل هذه الدائرة أيضًا رسمًا بيانيًا موجَّهًا غير دوري (DAG)، إذ إن كل البوابات تقابل الدالة نفسها (أي NAND)، ولذلك لا نحتاج حتى إلى وسمها، وكل البوابات درجتها الداخلة اثنان بالضبط. ورغم بساطتها، فإن دوائر NAND قد تكون بالغة القوة.

مثال — دائرة NANDNAND للدالة XORXOR

تذكّر الدالة XORXOR التي تُسقط x0,x1∈{0,1}x_0,x_1 \in \{0,1\} على x0+x1mod  2x_0 + x_1 \mod 2. لقد رأينا في مثال موسّع: حساب XOR من AND وOR وNOT أننا نستطيع حساب XORXOR باستعمال ANDAND وOROR وNOTNOT، وبذلك وبموجب مبرهنة — NAND تحسب AND وOR وNOT نستطيع حسابها بعمليات NANDNAND وحدها. غير أن التالي بناء مباشر لحساب XORXOR عبر تسلسل من عمليات NAND:

  1. ليكن u=NAND(x0,x1)u = NAND(x_0,x_1).
  2. ليكن v=NAND(x0,u)v = NAND(x_0,u)
  3. ليكن w=NAND(x1,u)w = NAND(x_1,u).
  4. تكون XORXOR لـx0x_0 وx1x_1 هي y0=NAND(v,w)y_0 = NAND(v,w).

ويمكن التحقق من أن هذه الخوارزمية تحسب XORXOR فعلًا بحصر الاحتمالات الأربعة الممكنة لـx0,x1∈{0,1}x_0,x_1 \in \{0,1\}. ويمكننا أيضًا تمثيل هذه الخوارزمية بيانيًا بدائرة، انظر الشكل التوضيحي.

/images/introtcs/lec_03_computation-24.webp

وفعلًا، يمكننا إثبات المبرهنة التالية:

مبرهنة — NAND عملية شاملة

لكل دائرة منطقية CC ذات ss بوابة، توجد دائرة NAND C′C' ذات 3s3s بوابة على الأكثر تحسب الدالة نفسها التي تحسبها CC.

فكرة البرهان هي مجرد استبدال كل بوابة ANDAND وOROR وNOTNOT بتنفيذها المعتمد على NAND، على النحو المبيَّن في برهان مبرهنة — NAND تحسب AND وOR وNOT.

برهان

إذا كانت CC دائرة منطقية، فإن بما أننا، كما رأينا في برهان مبرهنة — NAND تحسب AND وOR وNOT، لدينا لكل a,b∈{0,1}a,b \in \{0,1\}

  • NOT(a)=NAND(a,a)NOT(a) = NAND(a,a)

  • AND(a,b)=NAND(NAND(a,b),NAND(a,b))AND(a,b) = NAND(NAND(a,b),NAND(a,b))

  • OR(a,b)=NAND(NAND(a,a),NAND(b,b))OR(a,b) = NAND(NAND(a,a),NAND(b,b))

نستطيع استبدال كل بوابة في CC بثلاث بوابات NANDNAND على الأكثر للحصول على دائرة مكافئة C′C'. وستكون الدائرة الناتجة ذات 3s3s بوابة على الأكثر.

النموذجان متكافئان في القدرة إذا أمكِن استعمالهما لحساب المجموعة نفسها من الدوال.

أمثلة أخرى على دوائر NAND (اختياري)

إليك بعض الأمثلة الأكثر تعقيدًا على دوائر NAND:

_زيادة الأعداد الصحيحة. لنُعتبر مهمة حساب، انطلاقًا من مُدخل نصي x∈{0,1}nx\in \{0,1\}^n يمثّل عددًا طبيعيًا X∈NX\in \N، التمثيلَ الخاص بـX+1X+1. أي أننا نريد حساب الدالة INCn:{0,1}n→{0,1}n+1INC_n:\{0,1\}^n \rightarrow \{0,1\}^{n+1} بحيث لكل x0,…,xn−1x_0,\ldots,x_{n-1}، يكون INCn(x)=yINC_n(x)=y الذي يستوفي ∑i=0nyi2i=(∑i=0n−1xi2i)+1\sum_{i=0}^n y_i 2^i = \left( \sum_{i=0}^{n-1} x_i 2^i \right)+1. (لتبسيط التدوين، نستخدم في هذا المثال التمثيل الذي تكون فيه الخانة الأقل أهمية أولًا لا آخرًا.)

يمكن وصف عملية الزيادة بإيجاز شديد على النحو التالي: «أضِف 11 إلى البت الأقل أهمية وانقل الحمل (carry)». وبشكل أدق قليلًا، في حالة التمثيل الثنائي، للحصول على زيادة xx، نفحص xx بدءًا من البت الأقل أهمية، ونقلب كل البتات 11 إلى 00 حتى نلاقي بتًّا مساويًا لـ00، وعندئذٍ نقلبه إلى 11 ونتوقف.

ويمكننا بذلك حساب زيادة x0,…,xn−1x_0,\ldots,x_{n-1} بفعل ما يلي:

خوارزمية — حساب دالة الزيادة

INPUT: $x_0,x_1,\ldots,x_{n-1}$ representing the number $\sum_{i=0}^{n-1} x_i\cdot 2^i$ # we use LSB-first representation
OUTPUT:$y \in \{0,1\}^{n+1}$ such that $\sum_{i=0}^n y_i \cdot 2^i =  \sum_{i=0}^{n-1} x_i\cdot 2^i + 1$

Let $c_0 \leftarrow 1$ # we pretend we have a "carry" of $1$ initially
For{$i=0,\ldots, n-1$}
Let $y_i \leftarrow XOR(x_i,c_i)$.
If{$c_i=x_i=1$}
$c_{i+1}=1$
else
$c_{i+1}=0$
endif
Endfor
Let $y_n \leftarrow c_n$.

تصف خوارزمية — حساب دالة الزيادة بدقة كيفية حساب عملية الزيادة، ويمكن بسهولة تحويلها إلى شيفرة Python تؤدّي الحساب نفسه، لكنها لا تبدو وكأنها تعطي مباشرةً دائرة NAND لحسابها. غير أننا نستطيع تحويل هذه الخوارزمية سطرًا بسطر إلى دائرة NAND. فمثلًا، بما أن NAND(a,NOT(a))=1NAND(a,NOT(a))=1 لكل aa، يمكننا استبدال التصريح الأول c0=1c_0=1 بـc0=NAND(x0,NAND(x0,x0))c_0 = NAND(x_0,NAND(x_0,x_0)). ونحن نعرف بالفعل كيف نحسب XORXOR باستعمال NAND، وبذلك يمكننا استعمال ذلك لتنفيذ العملية yi←XOR(xi,ci)y_i \leftarrow XOR(x_i,c_i). وبالمثل، يمكننا كتابة تعليمات «if» على صورة ci+1←AND(ci,xi)c_{i+1} \leftarrow AND(c_i,x_i)، أو بمعنى آخر ci+1←NAND(NAND(ci,xi),NAND(ci,xi))c_{i+1} \leftarrow NAND(NAND(c_i,x_i),NAND(c_i,x_i)). وأخيرًا، يمكن كتابة الإسناد yn=cny_n = c_n على صورة yn=NAND(NAND(cn,cn),NAND(cn,cn))y_n = NAND(NAND(c_n,c_n),NAND(c_n,c_n)). ويجمع هذه الملاحظات معًا لتُعطي لكل n∈Nn\in \N دائرة NANDNAND تحسب INCnINC_n. فعلى سبيل المثال، يوضّح الشكل التوضيحي كيف تبدو هذه الدائرة عند n=4n=4.

/images/introtcs/lec_03_computation-25.webp

من الزيادة إلى الجمع. ومتى توفرت لدينا عملية الزيادة، يمكننا بالتأكيد حساب الجمع بالتكرار المتواصل للزيادة (أي حساب x+yx+y بتنفيذ INC(x)INC(x) yy مرة). غير أن ذلك سيكون غير فعّال تمامًا وغير ضروري. وبفكرة تتبّع الحمل نفسها، يمكننا تنفيذ خوارزمية الجمع «المدرسية» وحساب الدالة ADDn:{0,1}2n→{0,1}n+1ADD_n:\{0,1\}^{2n} \rightarrow \{0,1\}^{n+1} التي على مُدخل x∈{0,1}2nx\in \{0,1\}^{2n} تُخرج التمثيل الثنائي لمجموع العددين الممثَّلين بـx0,…,xn−1x_0,\ldots,x_{n-1} وxn,…,x2n−1x_{n},\ldots,x_{2n-1}:

خوارزمية — الجمع باستعمال NAND

INPUT: $u \in \{0,1\}^n$, $v\in \{0,1\}^n$ representing numbers in LSB-first binary representation.
OUTPUT: LSB-first binary representation of $x+y$.

Let $c_0 \leftarrow 0$
For{$i=0,\ldots,n-1$}
    Let $y_i \leftarrow u_i + v_i \mod 2$
    If{$u_i + v_i + c_i \geq 2$}
    $c_{i+1}\leftarrow 1$
    else
    $c_{i+1} \leftarrow 0$
    endif
Endfor
Let $y_n \leftarrow c_n$

ومرة أخرى، يمكن ترجمة خوارزمية — الجمع باستعمال NAND إلى دائرة NAND. والملاحظة الجوهرية هي أن تعليمات «if/then» تقابل ببساطة ci+1←MAJ3(ui,vi,vi)c_{i+1} \leftarrow MAJ_3(u_i,v_i,v_i)، وقد رأينا في تمرين محلول — حساب الأغلبية بـNAND أن الدالة MAJ3:{0,1}3→{0,1}MAJ_3:\{0,1\}^3 \rightarrow \{0,1\} يمكن حسابها باستعمال NANDNAND.

لغة البرمجة NAND-CIRC

تمامًا كما فعلنا مع الدوائر المنطقية، يمكننا تعريف نسخة بلغة برمجة تكافئ دوائر NAND. وهي أبسط حتى من لغة AON-CIRC لأن لدينا عملية واحدة فقط. ونعرّف لغة برمجة NAND-CIRC بأنها لغة برمجة كل سطر فيها (باستثناء تصريح المُدخل/المُخرج) على الصورة التالية:

foo = NAND(bar,blah)

حيث foo وbar وblah معرِّفات متغيّرات.

مثال — أول برنامج NAND-CIRC لنا

إليك مثالًا على برنامج NAND-CIRC:

u = NAND(X[0],X[1])
v = NAND(X[0],u)
w = NAND(X[1],u)
Y[0] = NAND(v,w)

توقّف وتأمّل

هل تعرف الدالة التي يحسبها هذا البرنامج؟ إرشاد: لقد رأيتها من قبل.

ورسميًا، تمامًا كما فعلنا في تعريف — حساب دالة بواسطة برامج AON-CIRC بالنسبة إلى AON-CIRC، يمكننا تعريف فكرة الحساب ببرنامج NAND-CIRC بالطريقة الطبيعية:

تعريف — الحساب ببرنامج NAND-CIRC

لتكن f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m دالة ما، وليكن PP برنامج NAND-CIRC. نقول إن PP تحسب الدالة ff إذا:

  1. كان للبرنامج PP nn متغيّر مُدخل X[00],…,,\ldots,X[n−1n-1] وmm متغيّر خرج Y[00],…\ldots,Y[m−1m-1].

  2. لكل x∈{0,1}nx\in \{0,1\}^n، إذا نفّذنا PP مع إسناد القيم x0,…,xn−1x_0,\ldots,x_{n-1} إلى X[00],…,,\ldots,X[n−1n-1]، فإن متغيّرات الخرج Y[00],…\ldots,Y[m−1m-1] تكون في نهاية التنفيذ لها القيم y0,…,ym−1y_0,\ldots,y_{m-1} حيث y=f(x)y=f(x).

وكما من قبل، يمكننا أن نبيّن أن دوائر NAND تكافئ برامج NAND-CIRC (انظر الشكل التوضيحي):

مبرهنة — تكافؤ دوائر NAND والبرامج المستقيمة

لكل f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m وs≥ms \geq m، يكون ff قابلًا للحساب ببرنامج NAND-CIRC من ss سطرًا إذا ونحو فقط كان قابلًا للحساب بدائرة NAND ذات ss بوابة.

/images/introtcs/lec_03_computation-26.webp

نُغفل برهان مبرهنة — تكافؤ دوائر NAND والبرامج المستقيمة لأنه يسير على الخطوط نفسها تمامًا كتكافؤ الدوائر المنطقية مع برنامج AON-CIRC (مبرهنة — تكافؤ الدوائر والبرامج المستقيمة). وبناءً على مبرهنة — تكافؤ دوائر NAND والبرامج المستقيمة ومبرهنة — NAND عملية شاملة، نعرف أننا نستطيع ترجمة كل برنامج AON-CIRC من ss سطرًا PP إلى برنامج NAND-CIRC مكافئ من 3s3s سطرًا على الأكثر. وفعلًا، يمكن إجراء هذه الترجمة بسهولة باستبدال كل سطر على الصورة foo = AND(bar,blah) أو foo = OR(bar,blah) أو foo = NOT(bar) بالأسطر المكافئة من 1 إلى 3 التي تستعمل العملية NAND. ويحتوي مستودعنا على GitHub على «برهان بالشيفرة»: برنامج Python بسيط اسمه AON2NAND يحوّل برنامج AON-CIRC إلى برنامج NAND-CIRC مكافئ.

ملاحظة — هل لغة البرمجة NAND-CIRC تامّة تورينج؟ (ملاحظة اختيارية)

ربما سمعت مصطلحًا يُعرف بـ«تامّة تورينج» (Turing Complete) يُستعمل أحيانًا لوصف لغات البرمجة. (وإن لم تكن سمعت به، فلا بأس من تجاهل بقية هذه الملاحظة: فنحن نعرّف هذا المصطلح بدقّة في نماذج حوسبة مكافئة.) وإن كنت سمعت به، فقد تتساءل إن كانت لغة البرمجة NAND-CIRC تملك هذه الخاصية. والجواب لا، أو على نحو أدقّ أن مصطلح «اكتمال تورينج» لا ينطبق أصلًا على لغة البرمجة NAND-CIRC. والسبب أن لغة البرمجة NAND-CIRC، بحسب تصميمها، تستطيع حساب الدوال المتناهية F:{0,1}n→{0,1}mF:\{0,1\}^n \rightarrow \{0,1\}^m فقط، وهي دوال تأخذ عددًا ثابتًا من بتات المُدخل وتُنتج عددًا ثابتًا من بتات الخرج. أما مصطلح «تامّة تورينج» فلا ينطبق إلا على لغات البرمجة الخاصة بالدوال اللامتناهية التي تقبل مُدخلات بطول اعتباطي. وسنعود إلى هذا التمييز لاحقًا في هذا الكتاب.

تكافؤ كل هذه النماذج

إذا جمعنا بين مبرهنة — تكافؤ الدوائر والبرامج المستقيمة ومبرهنة — NAND عملية شاملة ومبرهنة — تكافؤ دوائر NAND والبرامج المستقيمة، نحصل على النتيجة التالية:

مبرهنة — التكافؤ بين نماذج الحوسبة المتناهية

لكل s,n,ms,n,m كبيرة بما يكفي ولكل f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m، الشروط التالية كلها متكافئة فيما بينها:

  • ff يمكن حسابها بدائرة منطقية (ذات بوابات ∧,∨,¬\wedge,\vee,\neg) عدد بواباتها O(s)O(s) على الأكثر.

  • ff يمكن حسابها ببرنامج AON-CIRC مستقيم عدد أسطره O(s)O(s) على الأكثر.

  • ff يمكن حسابها بدائرة NAND عدد بواباتها O(s)O(s) على الأكثر.

  • ff يمكن حسابها ببرنامج NAND-CIRC مستقيم عدد أسطره O(s)O(s) على الأكثر.

بـ«O(s)O(s)» نعني أن الحد هو c⋅sc\cdot s على الأكثر حيث cc ثابت مستقل عن nn. فمثلًا، إذا كانت ff قابلة للحساب بدائرة منطقية ذات ss بوابة، فإنها قابلة للحساب ببرنامج NAND-CIRC من 3s3s سطرًا على الأكثر، وإذا كانت ff قابلة للحساب بدائرة NAND ذات ss بوابة، فإنها قابلة للحساب ببرنامج AON-CIRC من 2s2s سطرًا على الأكثر.

نُغفل البرهان الرسمي، إذ يُحصل عليه بدمج مبرهنة — تكافؤ الدوائر والبرامج المستقيمة ومبرهنة — NAND عملية شاملة ومبرهنة — تكافؤ دوائر NAND والبرامج المستقيمة. والملاحظة الأساسية هي أن النتائج التي رأيناها تتيح لنا ترجمة برنامج/دائرة يحسب ff في أحد النماذج أعلاه إلى برنامج/دائرة يحسب ff في نموذج آخر، بزيادة عدد الأسطر/البوابات بمعامل ثابت على الأكثر (وهذا المعامل الثابت هو 33 على الأكثر في الواقع).

ويعد مبرهنة — تكافؤ الدوائر والبرامج المستقيمة حالة خاصة من نتيجة أكثر عمومية. فقد نُفكّر في نماذج حوسبة أكثر عمومية، حيث نستعمل بدلًا من AND/OR/NOT أو NAND عمليات أخرى (انظر الدوائر بمجموعات بوابات أخرى أدناه). ويبيّن الأمر أن الدوائر المنطقية تكافئ هذه النماذج أيضًا في القدرة. وأن كون كل هذه الطرق المختلفة لتعريف الحوسبة يقود إلى نماذج متكافئة يدلّ على أننا «على الطريق الصحيح». وهذا يبرّر الخيارات التي تبدو عشوائية والمتمثلة في استعمال AND/OR/NOT أو NAND بوصفهما عملياتنا الأساسية، لأن هذه الخيارات لا تؤثر في قدرة نموذجنا الحوسب. وتعني نتائج التكافؤ مثل مبرهنة — التكافؤ بين نماذج الحوسبة المتناهية أننا نستطيع بسهولة الترجمة بين الدوائر المنطقية ودوائر NAND وبرامج NAND-CIRC وما شابه. وسنستعمل هذه القدرة لاحقًا في هذا الكتاب، منتقلين كثيرًا إلى الصياغة الأكثر ملاءمة دون أن نُوليها عناية بالغة. ولذلك لن نقلق كثيرًا من التمييز بين، مثلًا، الدوائر المنطقية وبرامج NAND-CIRC.

وفي المقابل، سنواصلة الحرص على التمييز بين الدوائر/البرامج و_الدوال_ (تذكّر functionprogramidea). فتقابل الدالةُ مواصفةَ مهمة حسابية، وهي كائن مختلف جوهريًا عن البرنامج أو الدائرة التي تقابل تنفيذَ تلك المهمة.

الدوائر بمجموعات بوابات أخرى

لا شيء خاص بـAND/OR/NOT أو NAND. فلكل مجموعة دوال G={G0,…,Gk−1}\mathcal{G} = \{ G_0,\ldots,G_{k-1} \} يمكننا تعريف فكرة الدوائر التي تستعمل عناصر G\mathcal{G} بوصفها بوابات، وفكرة «لغة برمجة G\mathcal{G}» التي يكون فيها كل سطر إسنادًا إلى متغيّر foo نتيجة تطبيق Gi∈GG_i \in \mathcal{G} ما على متغيّرات مُعرَّفة سابقًا أو على مُدخلات. وبالتحديد، يمكننا تقديم التعريف التالي:

تعريف — البرامج المستقيمة العامة

لتكن F={f0,…,ft−1}\mathcal{F} = \{ f_0,\ldots, f_{t-1} \} مجموعة متناهية من الدوال المنطقية، بحيث fi:{0,1}ki→{0,1}f_i:\{0,1\}^{k_i} \rightarrow \{0,1\} لمُدخل ما ki∈Nk_i \in \N. برنامج F\mathcal{F} هو تسلسل من الأسطر، يُسند كلٌّ منها إلى متغيّر ما نتيجة تطبيق fi∈Ff_i \in \mathcal{F} ما على kik_i متغيّرات أخرى. وكما سبق، نستعمل X[ii] وY[jj] للدلالة على متغيّرات المُدخل والمُخرج.

ونقول إن F\mathcal{F} هي مجموعة عمليات شاملة (وتُعرف أيضًا بمجموعة بوابات شاملة) إذا وُجد برنامج F\mathcal{F} يحسب الدالة NANDNAND.

تقابل برامج AON-CIRC برامج {AND,OR,NOT}\{AND,OR,NOT\}، وتقابل برامج NAND-CIRC برامج F\mathcal{F} للمجموعة F\mathcal{F} التي لا تحتوي إلّا على الدالة NANDNAND، ولكن يمكننا أيضًا تعريف برامج {IF,ZERO,ONE}\{ IF, ZERO, ONE\} (انظر أدناه)، أو استعمال أي مجموعة أخرى.

ويمكننا أيضًا تعريف دوائر F\mathcal{F}، وهي رسوم بيانية موجَّهة تكون فيها كل بوابة مقابلة لتطبيق دالة fi∈Ff_i \in \mathcal{F}، ولكل بوابة kik_i سلكًا داخليًا وسلكًا خارجيًا واحدًا. (وإذا كانت الدالة fif_i غير متماثلة، بمعنى أن ترتيب مُدخلاتها مهم، فيلزم أن نوسم كل سلك يدخل البوابة بمُعامل الدالة الذي يقابله.) وكما في مبرهنة — تكافؤ الدوائر والبرامج المستقيمة، يمكننا أن نبيّن أن دوائر F\mathcal{F} وبرامج F\mathcal{F} متكافئة. لقد رأينا أن الدوائر/البرامج الناتجة عن F={AND,OR,NOT}\mathcal{F} = \{ AND,OR, NOT\} تكافئ في القدرة لغة البرمجة NAND-CIRC، لأننا نستطيع حساب NANDNAND باستعمال ANDAND/OROR/NOTNOT وبالعكس. ويبيّن الأمر أن هذه حالة خاصة من ظاهرة أعمّ — عمومية (universality) NANDNAND ومجموعات البوابات الأخرى — سنستكشفها بمزيد من التفصيل لاحقًا في هذا الكتاب.

مثال — دوائر IF وZERO وONE

لتكن F={IF,ZERO,ONE}\mathcal{F} = \{ IF , ZERO, ONE \} حيث ZERO:{0,1}→{0}ZERO:\{0,1\} \rightarrow \{0\} وONE:{0,1}→{1}ONE:\{0,1\} \rightarrow \{1\} هما دالتا الصفر والواحد الثابتتان،[3] وIF:{0,1}3→{0,1}IF:\{0,1\}^3 \rightarrow \{0,1\} هي الدالة التي على المُدخل (a,b,c)(a,b,c) تُخرج bb إذا كان a=1a=1 وcc في غير ذلك. عندئذٍ تكون F\mathcal{F} شاملة.

وفعلًا، يمكننا أن نبيّن أن {IF,ZERO,ONE}\{ IF, ZERO, ONE \} شاملة باستعمال الصيغة التالية لـNANDNAND:

NAND(a,b)=IF(a,IF(b,ZERO,ONE),ONE)  . NAND(a,b) = IF(a,IF(b,ZERO,ONE),ONE) \;.

وهناك أيضًا بعض المجموعات F\mathcal{F} المقيّدة أكثر في القدرة. فمثلًا يمكن إثبات أن استعملنا بوابات AND أو OR وحدها (من دون NOT) فإننا لا نحصل على نموذج حوسبة مكافئ. وتغطي التمارين عدة أمثلة على مجموعات بوابات شاملة وغير شاملة.

المواصفة مقابل التنفيذ (مرة أخرى)

/images/introtcs/lec_03_computation-27.webp

كما ناقشنا في ميّزوا الدوال عن البرامج!، فإن أحد أهم التمييزات في هذا الكتاب هو تمييز المواصفة عن التنفيذ، أي فصل «ماذا» عن «كيف» (انظر الشكل التوضيحي). فتقابل الدالةُ مواصفةَ مهمة حسابية، أي ما الذي ينبغي أن يُنتج كمُخرج لكل مُدخل بعينه. وتقابل البرنامج (أو الدائرة، أو أي طريقة أخرى لتحديد الخوارزميات) تنفيذَ كيفية حساب المُخرج المطلوب من المُدخل. أي أن البرنامج مجموعة تعليمات حول كيفية حساب المُخرج من المُدخل. وحتى في النموذج الحوسب نفسه، قد توجد طرق كثيرة مختلفة لحساب الدالة نفسها. فمثلًا، يوجد أكثر من برنامج NAND-CIRC واحد يحسب دالة الأغلبية، وأكثر من دائرة منطقية واحدة تحسب دالة الجمع، وهكذا دواليك.

والخلط بين المواصفة والتنفيذ (أو بما يكافئهما من الدوال و_البرامج_) خطأ شائع، وللأسف تشجّعه المصطلحات الشائعة في لغات البرمجة التي تسمّي أجزاء من البرنامج «دوالًا». غير أنه، في نظرية علم الحاسوب وممارسته معًا، من المهم الإبقاء على هذا التمييز، وهو مهم بشكل خاص لنا في هذا الكتاب.

خلاصة

  • الخوارزمية وصفةٌ لتنفيذ حسابٍ، وهي تسلسل من عمليات «أولية» أو «بسيطة».
  • أحد التعريفات المرشَّحة للعمليات «الأولية» هو المجموعة ANDAND وOROR وNOTNOT.
  • تعريف مرشَّح آخر لعملية «أولية» هو عملية NANDNAND. وهي عملية يسهل تنفيذها في العالم الفيزيائي بطرق متنوّعة، منها الترانزستورات الإلكترونية.
  • يمكننا استعمال NANDNAND لحساب دوال أخرى كثيرة، منها الأغلبية والزيادة وغيرها.
  • وهناك اختيارات أخرى متكافئة، منها المجموعتان {AND,OR,NOT}\{AND,OR,NOT\} و{IF,ZERO,ONE}\{ IF, ZERO, ONE \}.
  • يمكننا تعريف رسميًا فكرة أن الدالة F:{0,1}n→{0,1}mF:\{0,1\}^n \rightarrow \{0,1\}^m قابلة للحساب باستعمال لغة البرمجة NAND-CIRC.
  • ولكل مجموعة عمليات أساسية، تتكافؤ فكرة القابلية للحساب بدائرة وفكرة القابلية للحساب ببرنامج مستقيم.

التمارين

تمرين — مقارنة أعداد من 44 بتات

أعطِ دائرة منطقية (ببوابات AND/OR/NOT) تحسب الدالة CMP8:{0,1}8→{0,1}CMP_8:\{0,1\}^8 \rightarrow \{0,1\} بحيث CMP8(a0,a1,a2,a3,b0,b1,b2,b3)=1CMP_8(a_0,a_1,a_2,a_3,b_0,b_1,b_2,b_3)=1 إذا ونحو فقط إذا كان العدد الذي تمثّله a0a1a2a3a_0a_1a_2a_3 أكبر من العدد الذي تمثّله b0b1b2b3b_0b_1b_2b_3.

تمرين — مقارنة أعداد من nn بتًا

أثبِت أن هناك ثابتًا cc بحيث إن لكل nn توجد دائرة منطقية (ببوابات AND/OR/NOT) CC عدد بواباتها c⋅nc\cdot n على الأكثر تحسب الدالة CMP2n:{0,1}2n→{0,1}CMP_{2n}:\{0,1\}^{2n} \rightarrow \{0,1\} بحيث CMP2n(a0⋯an−1b0⋯bn−1)=1CMP_{2n}(a_0\cdots a_{n-1} b_0 \cdots b_{n-1})=1 إذا ونحو فقط إذا كان العدد الذي تمثّله a0⋯an−1a_0 \cdots a_{n-1} أكبر من العدد الذي تمثّله b0⋯bn−1b_0 \cdots b_{n-1}.

تمرين — OR وNOT شاملتان

أثبِت أن المجموعة {OR,NOT}\{ OR, NOT \} شاملة، بمعنى أنه يمكن حساب NAND باستعمال هاتين البوابتين.

تمرين — AND وOR ليستا شاملتين

أثبِت أن لكل دائرة CC ذات مُدخل من nn بتات تحتوي بوابات AND وOR وحدها، وكذلك بوابات تحسب الدالتين الثابتتين 00 و11، تكون CC مُحكَمة الترتيب (monotone)، بمعنى أنه إذا كان x,x′∈{0,1}nx,x' \in \{0,1\}^n وxi≤xi′x_i \leq x'_i لكل i∈[n]i\in [n]، فإن C(x)≤C(x′)C(x) \leq C(x').

استنتج أن المجموعة {AND,OR,0,1}\{ AND, OR, 0, 1\} ليست شاملة.

تمرين — XOR ليست شاملة

أثبِت أن لكل دائرة CC ذات مُدخل من nn بتات تحتوي بوابات XOR وحدها، وكذلك بوابات تحسب الدالتين الثابتتين 00 و11، تكون CC تآلفية أو خطّية على مقياس اثنين، بمعنى أنه يوجد a∈{0,1}na\in \{0,1\}^n وb∈{0,1}b\in \{0,1\} بحيث لكل x∈{0,1}nx\in \{0,1\}^n، لدينا C(x)=∑i=0n−1aixi+bmod  2C(x) = \sum_{i=0}^{n-1}a_ix_i + b \mod 2.

استنتج أن المجموعة {XOR,0,1}\{ XOR , 0 , 1\} ليست شاملة.

تمرين — MAJ وNOT و1 شاملة

لتكن MAJ:{0,1}3→{0,1}MAJ:\{0,1\}^3 \rightarrow \{0,1\} دالة الأغلبية. أثبِت أن {MAJ,NOT,1}\{ MAJ,NOT, 1 \} مجموعة بوابات شاملة.

تمرين — MAJ وNOT ليستا شاملتين

أثبِت أن {MAJ,NOT}\{ MAJ,NOT \} ليست مجموعة شاملة. انظر الحاشية للإرشاد.[4]

تمرين — NOR شاملة

لتكن NOR:{0,1}2→{0,1}NOR:\{0,1\}^2 \rightarrow \{0,1\} المعرَّفة بـNOR(a,b)=NOT(OR(a,b))NOR(a,b) = NOT(OR(a,b)). أثبِت أن {NOR}\{ NOR \} مجموعة بوابات شاملة.

تمرين — LOOKUP شاملة

أثبِت أن {LOOKUP1,0,1}\{ LOOKUP_1,0,1 \} مجموعة بوابات شاملة، حيث 00 و11 دالتان ثابتتان وLOOKUP1:{0,1}3→{0,1}LOOKUP_1:\{0,1\}^3 \rightarrow \{0,1\} تحقق أن LOOKUP1(a,b,c)LOOKUP_1(a,b,c) تساوي aa إذا كان c=0c=0 وتساوي bb إذا كان c=1c=1.

تمرين — حدّ على حجم الأساس الشامل (تحدٍّ)

أثبِت أن لكل مجموعة جزئية BB من الدوال من {0,1}k\{0,1\}^k إلى {0,1}\{0,1\}، إذا كانت BB شاملة، فهناك دائرة BB عدد بواباتها O(1)O(1) على الأكثر تحسب الدالة NANDNAND (يمكنك البدء بإثبات وجود دائرة BB عدد بواباتها O(k16)O(k^{16}) على الأكثر).[5]

تمرين — الحجم والمُدخلات / المُخرجات

أثبِت أن لكل دائرة NAND ذات حجم ss وnn مُدخلًا وmm مُخرجًا، لدينا s≥min⁡{n/2,m}s \geq \min \{ n/2 , m \}. انظر الحاشية للإرشاد.[6]

تمرين — العتبة باستعمال NAND

أثبِت أن هناك ثابتًا cc بحيث إن لكل n>1n>1، ولكل أعداد صحيحة a0,…,an−1,b∈{−2n,−2n+1,…,−1,0,+1,…,2n}a_0,\ldots,a_{n-1},b \in \{-2^n,-2^n+1,\ldots,-1,0,+1,\ldots,2^n\}، توجد دائرة NAND عدد بواباتها cn4c n^4 على الأكثر تحسب دالة العتبة fa0,…,an−1,b:{0,1}n→{0,1}f_{a_0,\ldots,a_{n-1},b}:\{0,1\}^n \rightarrow \{0,1\} التي على مُدخل x∈{0,1}nx\in \{0,1\}^n تُخرج 11 إذا ونحو فقط إذا كان ∑i=0n−1aixi>b\sum_{i=0}^{n-1} a_i x_i > b.

تمرين — NAND من دوال التنشيط

نقول إن الدالة f:R2→Rf:\mathbb{R}^2 \rightarrow \mathbb{R} هي مُقارِب NAND (NAND approximator) إذا كانت لها الخاصية التالية: لكل a,b∈Ra,b \in \mathbb{R}، إذا كان min⁡{∣a∣,∣1−a∣}≤1/3\min\{|a|,|1-a|\}\leq 1/3 وmin⁡{∣b∣,∣1−b∣}≤0.1\min \{ |b|,|1-b| \}\leq 0.1، حينئذٍ ∣f(a,b)−NAND(⌊a⌉,⌊b⌉)∣≤0.1|f(a,b) - NAND(\lfloor a \rceil, \lfloor b \rceil)| \leq 0.1، حيث نرمز بـ⌊x⌉\lfloor x \rceil إلى أقرب عدد صحيح إلى xx. أي أنه إذا كان a,ba,b على مسافة 1/31/3 من {0,1}\{0,1\}، فنريد أن يساوي f(a,b)f(a,b) قيمة NANDNAND للقيم في {0,1}\{0,1\} الأقرب إلى aa وbb على التوالي. وفي غير ذلك، لا يهمّنا ما يكونه مُخرج ff عند aa وbb.

في هذا التمرين ستبيّن أنه يمكن بناء مُقارِب NAND من دوال تنشيط شائعة كثيرة المستعملة في الشبكات العصبية العميقة. ونتيجةً لذلك ستحصل على أن الشبكات العصبية العميقة تستطيع محاكاة دوائر NAND. وبما أن دوائر NAND تستطيع أيضًا محاكاة الشبكات العصبية العميقة، فإن هذين النموذجين الحوسبين متكافئان فيما بينهما.

  1. بيّن أن هناك مُقارِب NAND ff المعرَّف بـf(a,b)=L(DReLU(L′(a,b)))f(a,b) = L(DReLU(L'(a,b))) حيث L′:R2→RL':\mathbb{R}^2 \rightarrow \mathbb{R} دالة تآلفية (affine) (من الشكل L′(a,b)=αa+βb+γL'(a,b)=\alpha a + \beta b + \gamma لمُعاملات ما α,β,γ∈R\alpha,\beta,\gamma \in \mathbb{R})، وLL دالة تآلفية (من الشكل L(y)=αy+βL(y) = \alpha y + \beta لمُعاملات α,β∈R\alpha,\beta \in \mathbb{R})، وDReLU:R→RDReLU:\mathbb{R} \rightarrow \mathbb{R} دالة معرَّفة بـDReLU(x)=min⁡(1,max⁡(0,x))DReLU(x) = \min(1,\max(0,x)). ولاحظ أن DReLU(x)=1−ReLU(1−ReLU(x))DReLU(x) = 1-ReLU(1-ReLU(x)) حيث ReLU(x)=max⁡(x,0)ReLU(x)=\max(x,0) هي دالة تنشيط الوحدة الخطية المصحَّحة.

  2. بيّن أن هناك مُقارِب NAND ff المعرَّف بـf(a,b)=L(sigmoid(L′(a,b)))f(a,b) = L(sigmoid(L'(a,b))) حيث L′,LL',L تآلفية كما سبق وsigmoid:R→Rsigmoid:\mathbb{R} \rightarrow \mathbb{R} دالة معرَّفة بـsigmoid(x)=ex/(ex+1)sigmoid(x) = e^x/(e^x+1).

  3. بيّن أن هناك مُقارِب NAND ff المعرَّف بـf(a,b)=L(tanh(L′(a,b)))f(a,b) = L(tanh(L'(a,b))) حيث L′,LL',L تآلفية كما سبق وtanh:R→Rtanh:\mathbb{R} \rightarrow \mathbb{R} دالة معرَّفة بـtanh(x)=(ex−e−x)/(ex+e−x)tanh(x) = (e^x-e^{-x})/(e^x+e^{-x}).

  4. أثبِت أن لكل دائرة NAND CC ذات nn مُدخلًا ومُخرج واحد تحسب دالة g:{0,1}n→{0,1}g:\{0,1\}^n \rightarrow \{0,1\}، إذا استبدلنا كل بوابة في CC بمُقارِب NAND ثم استدعينا الدائرة الناتجة على x∈{0,1}nx\in \{0,1\}^n ما، فإن المُخرج سيكون عددًا yy بحيث ∣y−g(x)∣≤1/3|y-g(x)|\leq 1/3.

تمرين — الأغلبية بكفاءة باستعمال NAND

أثبِت أن هناك ثابتًا cc بحيث إن لكل n>1n>1 توجد دائرة NAND عدد بواباتها c⋅nc\cdot n على الأكثر تحسب دالة الأغلبية على nn بت مُدخل MAJn:{0,1}n→{0,1}MAJ_n:\{0,1\}^n \rightarrow \{0,1\}. أي أن MAJn(x)=1MAJ_n(x)=1 إذا ونحو فقط إذا كان ∑i=0n−1xi>n/2\sum_{i=0}^{n-1}x_i > n/2. انظر الحاشية للإرشاد.[7]

تمرين — المُخرج في الطبقة الأخيرة

أثبِت أن لكل f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\}، إذا كانت هناك دائرة منطقية CC ذات ss بوابة تحسب ff، فهناك دائرة منطقية C′C' ذات ss بوابة على التوالي بحيث في التوزيع الطبقي الأدنى لـC′C'، تكون بوابة خرج C′C' موضوعة في الطبقة الأخيرة. انظر الحاشية للإرشاد.[8]

ملاحظات سردية

المقتطف من كتاب الخوارزمي مأخوذ من «جبر ابن موسى» لفريدريك روزن، 1831.

كان تشارلز بابيج (1791-1871) عالمًا ومخترعًا وباحثًا رؤيويًا (انظر [@swade2002the, @collier2000charles]). وقبل أكثر من قرن من اختراع الحواسيب الإلكترونية الحديثة، أدرك بابيج أن الحساب يمكن أن يُآلَف من حيث المبدأ. وكان تصميمه الأول لحاسوب ميكانيكي هو محرك الفروق المصمَّم لإجراء الاستيفاء متعددة الحدود. ثم صمّم بعد ذلك المحرك التحليلي، وهو آلة أكثر عمومية بكثير وأول نموذج أوّلي لحاسوب عام قابل للبرمجة. وللأسف لم يستطع بابيج يومًا إتمام تصميم نماذجه الأوّلية. ومن أوائل من أدركوا إمكانات المحرك وآثاره البعيدة آدا لوفلايس (انظر ملاحظات الحلقات وما لا نهاية).

جرى أول البحث في جبر البولياني على يد بولي ودي مورغان في الأربعينيات من القرن التاسع عشر [@Boole1847mathematical, @DeMorgan1847]. وأُعطيت تعريف الدوائر المنطقية وصلتها بدوائر المرحِّلات الكهربائية في رسالة الماجستير لشانون [@Shannon1938]. (وقد وصف هوارد غاردنر رسالة شانون بأنها «ربما أهم رسالة ماجستير، وأيضًا أشهرها، في القرن العشرين».) كتاب سافيج [@Savage1998models]، مثل هذا الكتاب، يقدّم نظرية الحوسبة بدءًا بالدوائر المنطقية بوصفها النموذج الأول. ويحتوي كتاب جوكنا [@Jukna12] على عرض حديث ومتعمّق للدوائر المنطقية، انظر أيضًا [@wegener1987complexity].

أُثبت أن دالة NAND شاملة على يد شيفر [@Sheffer1913]، مع أن هذا يظهر أيضًا في عمل بيرس الأسبق، انظر [@Burks1978charles]. واستعمل وايتهد وروسيل NAND أساسًا لمنطقهما في عملهما العظيم مبادئ الرياضيات (Principia Mathematica) [@WhiteheadRussell1912]. وتبحث أطروحة إرنست للدكتوراه [@Ernst2009phd] تجريبيًا في أصغر دوائر NAND لدوال متنوّعة. ويبني كتاب نيسان وشوكن [@NisanShocken2005] نظام حوسبة يبدأ من بوابات NAND وينتهي عند البرامج والألعاب عالية المستوى («من NAND إلى تيتريس»)؛ انظر أيضًا الموقع nandtotetris.org.

عرّفنا في تعريف — الدوائر المنطقية حجم الدائرة المنطقية بأنه عدد البوابات التي تحتوي عليها. وهذه إحدى عريفتين مستعملتين في الأدبيات. أما العريفة الأخرى فتُعرّف الحجم بأنه عدد الأسلاك (وهو مكافئ لعدد البوابات مضافًا إليه عدد المُدخلات). وهذا لا يصنع فارقًا يُذكر في كل السياقات تقريبًا، لكنه قد يؤثّر في تعقيد حجم الدائرة لبعض «الأمثلة المرضية» لدوال مثل دالة الصفر الثابت التي لا تعتمد على كثير من مُدخلاتها.


  1. معنى وجود حواف موازية أن بوابة AND أو OR المعنونة uu يمكن أن يكون كلا جيريها الداخلي هو البوابة vv نفسها. وبما أن AND(a,a)=OR(a,a)=aAND(a,a)=OR(a,a)=a لكل a∈{0,1}a\in \{0,1\}، فإن هذه الحواف الموازية لا تساعد في حساب قيم جديدة في الدوائر ذات بوابات AND/OR/NOT. غير أننا سنرى لاحقًا دوائر بمجموعات بوابات أكثر عمومية. ↩︎

  2. فمثلًا مواصفة لغة البرمجة C تتجاوز 500 صفحة. ↩︎

  3. يمكن أيضًا تعريف هاتين الدالتين بأنهما تأخذان مُدخلًا طوله صفر. وهذا لا يحدث أي فرق في قدرة النموذج الحوسبية. ↩︎

  4. إرشاد: استعمل الحقيقة MAJ(a‾,b‾,c‾)=MAJ(a,b,c)‾MAJ(\overline{a},\overline{b},\overline{c}) = \overline{MAJ(a,b,c)} لإثبات أن كل f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} يمكن حسابها بدائرة فيها بوابتا MAJMAJ وNOTNOT فقط تستوفي f(0,0,…,0)≠f(1,1,…,1)f(0,0,\ldots,0) \neq f(1,1,\ldots,1). شكرًا لناثان برونيل وديفيد إيفانز على اقتراح هذا التمرين. ↩︎

  5. شكرًا لأليك صن وسيمون فيشر على ملاحظاتهما على هذه المسألة. ↩︎

  6. إرشاد: استعمل شرطَي تعريف — الدوائر المنطقية اللذين ينصّان على أن لكل رأس مُدخل جارًا خارجًا واحدًا على الأقل، وأن هناك بالضبط mm بوابة خرج. وانظر أيضًا ملاحظة — تفاصيل دقيقة في الدوائر المنطقية (اختياري). ↩︎

  7. أحد أساليب حل هذه المسألة هو استعمال التعاود وتحليله باستعمال ما يُعرف بـ«مبرهنة سيجر» (Master Theorem). ↩︎

  8. إرشاد: يمكن إزالة الرؤوس الواقعة في طبقات بعد الخرج بأمان دون تغيير وظيفة الدائرة. ↩︎