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

الشيفرة بيانات والبيانات شيفرة { #codeanddatachap }

الشيفرة بيانات والبيانات شيفرة

  • التعرّف على واحد من أهم المفاهيم في الحوسبة: ثنائية الشيفرة والبيانات. \
  • بناء الثقة في التنقّل بين تمثيلات مختلفة للبرامج. \
  • متابعة بناء "مُقيِّم دوائر كونية" (universal circuit evaluator) الذي يقيّم دوائر أخرى، إذ يتسلّم تمثيلها. \
  • رؤية النتيجة الكبرى التي تكمل نتيجة الفصل السابق: بعض الدوال تتطلّب عدداً أسّياً من البوابات (gates) لحسابها.
  • مناقشة أطروحة كيرش-تورينغ الفيزيائية الموسّعة (Physical extended Church-Turing thesis) التي تدّعي أن الدوائر المنطقية (Boolean circuits) تلتقط كلَّ حسابٍ ممكنٍ في العالم الفيزيائي، وآثارها الفيزيائية والفلسفية.

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

"لن يسمّي عالم الرياضيات تطابقاً بين مجموعة المئتين والستين ثلاثياً من أربع وحدات ومجموعةٍ من عشرين وحدةً أخرى "كونياً" (universal)، مع أنّ هذا التطابق هو على الأرجح أكثر خاصيةٍ عامة جوهرية في الحياة على الأرض"، ميشا غروموف، 2013

البرنامج (program) ليس سوى تسلسلٍ من الرموز، كلٌّ منها يمكن ترميزه كسلسلة من 00 و11 باستخدام معيار ASCII مثلاً. وعليه يمكننا تمثيل كل برنامج NAND-CIRC (وعليه أيضاً كل دائرة منطقية) كسلسلة ثنائية. تبدو هذه العبارة بديهية لكنها في الواقع عميقة إلى حدٍّ كبير. فهي تعني أننا يمكننا معاملة الدوائر أو برامج NAND-CIRC بوصفها تعليمات (instructions) لتنفيذ حساب، وكذلك بوصفها بيانات (data) يمكن أن تُستخدَم بوصفها مدخلات (inputs) لحسابات أخرى.

البرنامج (program) قطعةٌ من النص، ولذلك يمكن إطعامه كمدخل لبرامج أخرى.

هذا التطابق بين الشيفرة (code) و_البيانات_ (data) هو أحد أكثر جوانب الحوسبة جوهرية. إنه يقف وراء فكرة الحواسيب عامة الغرض (general purpose)، التي لا تكون موصولة سلفاً بحساب مهمةٍ واحدة فحسب، وهو أيضاً أساس أملنا في الحصول على ذكاء اصطناعي عام. يتّسع استعمال هذا المفهوم في جميع مجالات الحوسبة، من لغات البرمجة النصية إلى التعلّم الآلي، لكن من الإنصاف القول إننا لم نتقنه بعد إتقاناً كاملاً. كثير من عمليات الاستغلال الأمني تنطوي على حالات مثل "فيض المخزن المؤقت" (buffer overflows) حين ينجح المهاجمون في حقن شيفرة حيث توقّع النظام بياناتَ "خاملة" (passive) فحسب (انظر الشكل التوضيحي). العلاقة بين الشيفرة والبيانات تتجاوز حدود أجهزة الحاسوب الإلكترونية. فمثلاً يمكن التفكير في الـ DNA بوصفه برنامجاً وبيانات معاً (بعبارات شرودينغر، الذي كتب قبل اكتشاف بنية الـ DNA كتاباً ألهم واتسون وكريك، فإن الـ DNA هو في آنٍ واحد "مخطّط المهندس وحرفة البنّاء").

/images/introtcs/lec_04_code_and_data-1.webp

في هذا الفصل سنبدأ باستكشاف بعض التطبيقات العديدة للتطابق بين الشيفرة والبيانات. نبدأ باستعمال تمثيل البرامج/الدوائر كسلاسل من أجل عدِّ (count) عدد البرامج/الدوائر حتى حجمٍ معيّن، ونستعمل ذلك للحصول على نظير للنتيجة التي أثبتناها في السكر النحوي، وحساب كل دالة. هناك أثبتنا أن كلَّ دالة يمكن حسابها بدائرة، لكن تلك الدائرة قد تكون بحجم أسّي (انظر مبرهنة — شمولية الدوائر المنطقية، بحدّ محسّن للحدّ الدقيق). في هذا الفصل سنثبت أن هناك بعضَ الدوال التي لا يمكننا فعل أفضل منها: فالدائرة الصغرى التي تحسبها بحجم أسّي.

سنتكلّم أيضاً في هذا الفصل عن مخطّطي تمثيل البرامج/الدوائر كسلاسل لإثبات وجود "دائرة كونية" (universal circuit) — دائرة تستطيع تقييم دوائر أخرى. وفي لغات البرمجة يُعرف هذا بـ "المُقيِّم الدائري الذاتي" (meta circular evaluator) — برنامجٌ بلغة برمجة معيّنة يستطيع تنفيذ برامج أخرى في اللغة نفسها. هذه النتائج تخضع لقيدٍ مهم: يتعيّن أن تكون الدائرة الكونية أكبر حجماً من الدوائر التي تقيّمها. سنُري لاحقاً في الحلقات وما لا نهاية كيف نتخلّص من هذا القيد، حيث سنُدخل الحلقات (loops) و_آلات تورينغ_ (Turing machines).

انظر الشكل التوضيحي لمحةٍ عن نتائج هذا الفصل.

/images/introtcs/lec_04_code_and_data-2.webp

تمثيل البرامج كسلاسل

/images/introtcs/lec_04_code_and_data-3.webp

يمكننا تمثيل البرامج أو الدوائر كسلاسل بطرق لا حصر لها. فمثلاً، بما أن الدوائر المنطقية رسومٌ موجّهة مُعطّلة الحلقات (directed acyclic graphs)، يمكننا استعمال تمثيل مصفوفة التجاور (adjacency matrix) أو قائمة التجاور (adjacency list) لها. لكن بما أن شيفرة البرنامج هي في النهاية تسلسلٌ من الحروف والرموز فحسب، فإن تمثيل البرنامج بهذا التسلسل هو على الأرجح أبسط تمثيل من الناحية المفاهيمية. فمثلاً، برنامج NAND-CIRC التالي PP

temp_0 = NAND(X[0],X[1])
temp_1 = NAND(X[0],temp_0)
temp_2 = NAND(X[1],temp_0)
Y[0] = NAND(temp_1,temp_2)

ليس سوى سلسلة من 107 رمزاً تضم حروفاً صغيرة وكبيرة، وأرقاماً، وشَرطة سفلية _ وعلامة التساوي =، وعلامات ترقيم مثل "(" و")" و","، ومسافات، وعلامات "سطر جديد" (تُرمز عادةً بـ "\ n" أو "↵"). يمكن ترميز كل رمزٍ من هذه الرموز كسلسلة من 77 بتات باستخدام ترميز ASCII، وبالتالي يمكن ترميز البرنامج PP كسلسلة طولها 7⋅107=7497 \cdot 107 = 749 بت.

لم يكن في النقاش أعلاه شيء خاصٌ بالبرنامج PP، وبالتالي يمكننا استعمال نفس المنطق لإثبات أن كلَّ برنامج NAND-CIRC يمكن تمثيله كسلسلة في {0,1}∗\{0,1\}^*. وفعلاً يمكننا فعل أفضل قليلاً من ذلك. فبما أن أسماء متغيّرات العمل (working variables) في برنامج NAND-CIRC لا تؤثّر في وظيفته، يمكننا دائماً تحويل البرنامج إلى الصورة P′P' بحيث تكون كل المتغيّرات عدا المدخلات والمخرجات على الصورة temp_0 وtemp_1 وtemp_2 وهكذا.. علاوةً على ذلك، إذا كان البرنامج فيه ss سطراً، فلن نضطر أبداً إلى استعمال فهرس أكبر من 3s3s (لأن كل سطر ينطوي على ثلاث متغيّرات كحدٍّ أقصى)، وبالمثل فإن فهارس متغيّرات المدخلات والمخرجات ستكون جميعها 3s3s على الأكثر. وبما أن العدد بين 00 و3s3s يمكن التعبير عنه بأرقام لا تتجاوز ⌈log⁡10(3s+1)⌉=O(log⁡s)\lceil \log_{10}(3s+1) \rceil = O(\log s)، فإن كل سطر في البرنامج (الذي على الصورة foo = NAND(bar,blah)) يمكن تمثيله باستخدام O(1)+O(log⁡s)=O(log⁡s)O(1) + O(\log s) = O(\log s) رمزاً، كلٌّ منها يمكن تمثيله بـ 77 بتات. وعليه يمكن تمثيل برنامجٍ من ss سطراً كسلسلة من O(slog⁡s)O(s \log s) بت، ممّا ينتج عنه المبرهنة التالية:

مبرهنة — تمثيل البرامج كسلاسل

هناك ثابت cc بحيث إن كان f∈SIZE(s)f \in SIZE(s)، فهناك برنامج PP يحسب ff وتمثيله كسلسلة طوله cslog⁡sc s \log s على الأكثر.

توقّف وتأمّل

نُهمل البرهان الصوري لـمبرهنة — تمثيل البرامج كسلاسل لكنّرجاءً تأكّد أنك تفهم لماذا ينتج عن السبب أعلاه.

عدِّ البرامج، وحدودٌ دنيا لحجم برامج NAND-CIRC

إحدى نتائج تمثيل البرامج كسلاسل هي أن عدد البرامج ذات طولٍ معيّن محدودٌ بعدد السلاسل التي تمثّلها. ولهذا الأمر نتائج على المجموعات SIZEn,m(s)SIZE_{n,m}(s) التي عرّفناها في الصنف SIZEn,m​(s).

مبرهنة — عدِّ البرامج

لكل s,n,m∈Ns,n,m\in \N، ∣SIZEn,m(s)∣≤2O(slog⁡s).|SIZE_{n,m}(s)| \leq 2^{O(s \log s)}. أي إنّ ثَمّ ما لا يتجاوز 2O(slog⁡s)2^{O(s\log s)} دالةً يحسبها برامج NAND-CIRC التي لا تتجاوز ss سطراً.[1]

برهان

لكل n,m∈Nn,m \in \N، سنُظهر أنّ بالإسناد EE من SIZEn,m(s)SIZE_{n,m}(s) إلى مجموعة السلاسل ذات الطول cslog⁡sc s \log s لأي ثابت cc. وهذا سيُنهي البرهان، لأنّه يعني أنّ ∣SIZEn,m(s)∣أصغرمنحجممجموعةكلالسلاسلالتيطولها|SIZE_{n,m}(s)| أصغر من حجم مجموعة كل السلاسل التي طولها c s \log sعلىالأكثر على الأكثر \ell =c s \log s$. وحجم هذه المجموعة الأخيرة هو 1+2+4+⋯+2ℓ=2ℓ+1−11+2+4+\cdots + 2^\ell = 2^{\ell +1} - 1 بحسب صيغة مجموع المتتاليات الهندسية.

سنجعل EE تُسقط ff على تمثيل أصغر برنامج يحسب ff. وبما أنّ f∈SIZEn,m(s)f \in SIZE_{n,m}(s)، فهناك برنامج PP من ss سطراً على الأكثر يمكن تمثيله بسلسلة طولها cslog⁡sc s \log s على الأكثر بحسب مبرهنة — تمثيل البرامج كسلاسل. علاوةً على ذلك، فإنّ الإسقاط f↦E(f)f \mapsto E(f) أحاديّ، إذ إنّ لكل f,f′:{0,1}n→{0,1}f,f':\{0,1\}^n \rightarrow \{0,1\} مختلفتين يجب أن يوجد مدخلٌ ما x∈{0,1}nx\in \{0,1\}^n تكون عنده f(x)≠f′(x)f(x) \neq f'(x). وهذا يعني إنّ البرنامجين اللذين يحسبان ff وf′f' على التوالي لا يمكن أن يكونا متطابقين.

مبرهنة — عدِّ البرامج له نتيجة تالية مهمة. عدد الدوال التي يمكن حسابها بدوائر/برامج صغيرة أصغر بكثير من العدد الكلي للدوال، وعليه توجد دوال تتطلّب دوائر كبيرة جداً (بل أسّية الحجم) لحسابها. ولرؤية السبب، لاحظ أنّ دالةً تُسقِط {0,1}2\{0,1\}^2 إلى {0,1}\{0,1\} يمكن التعرّف عليها بقائمتها من قيمها الأربع على المدخلات 00,01,10,1100,01,10,11. ودالةٌ تُسقِط {0,1}3\{0,1\}^3 إلى {0,1}\{0,1\} يمكن التعرّف عليها بقائمتها من قيمها الثماني على المدخلات 000,001,010,011,100,101,110,111000,001,010,011,100,101,110,111. وبشكلٍ أعمّ، كل دالة F:{0,1}n→{0,1}F:\{0,1\}^n \rightarrow \{0,1\} يمكن التعرّف عليها بقائمتها من قيمها الـ 2n2^n على المدخلات {0,1}n\{0,1\}^n. وعليه فإنّ عدد الدوال التي تُسقِط {0,1}n\{0,1\}^n إلى {0,1}\{0,1\} يساوي عدد قوائم القيم الممكنة ذات الطول 2n2^n، وهو بالضبط 22n2^{2^n}. لاحظ أنّ هذا أسّي مزدوج (double exponential) في nn، وعليه حتى لقيم nn الصغيرة (مثل n=10n=10) يكون عدد الدوال من {0,1}n\{0,1\}^n إلى {0,1}\{0,1\} فلكياً حقاً.[2] وكما ذُكر، فإنّ هذا ينتج عنه النتيجة التالية:

مبرهنة — حدٌّ أدنى من عدِّ الدوال

يوجد ثابت δ>0\delta > 0 بحيث إنّ لكل nn كبيرٍ بما يكفي، هناك دالة f:{0,1}n→{0,1}f:\{0,1\}^n\rightarrow \{0,1\} بحيث f∉SIZEn(δ2nn)f \not\in SIZE_n \left(\tfrac{\delta 2^n}{n} \right). أي إنّ أقصر برنامج NAND-CIRC يحسب ff يتطلّب أكثر من δ⋅2n/n\delta \cdot 2^n/n سطراً.[3]

برهان

البرهان بسيط. إن كان cc ثابتاً بحيث ∣SIZEn(s)∣≤2cslog⁡s|SIZE_n(s)| \leq 2^{c s \log s} و δ=1/c\delta = 1/c، فعند ضبط s=δ2n/ns = \delta 2^n/n نرى أنّ ∣SIZEn(δ2nn)∣≤2cδ2nnlog⁡s<2cδ2n=22n |SIZE_n(\tfrac{\delta 2^n}{n})| \leq 2^{c \tfrac{\delta 2^n}{n} \log s} < 2^{c \delta 2^n} = 2^{2^n} باستعمال الحقيقة أنّه بما أنّ s<2ns < 2^n فإنّ log⁡s<n\log s < n و δ=1/c\delta = 1/c. لكن بما أنّ ∣SIZEn(s)∣أصغرمنالعددالكليللدوالالتيتُسقِط|SIZE_n(s)| أصغر من العدد الكلي للدوال التي تُسقِط nبتإلىبتواحد،فلابدّمنوجوددالةواحدةعلىالأقلّليستفي بت إلى بت واحد، فلا بدّ من وجود دالة واحدة على الأقلّ ليست في SIZE_n(s)$، وهذا هو ما أردنا إثباته.

لقد رأينا من قبل أنّ كلَّ دالة تُسقِط {0,1}n\{0,1\}^n إلى {0,1}\{0,1\} يمكن حسابها ببرنامج من O(2n/n)O(2^n /n) سطراً. ويُظهر مبرهنة — حدٌّ أدنى من عدِّ الدوال أنّ هذا الحدّ مُحكَم، بمعنى أنّ بعض الدوال تتطلّب فعلاً هذا العدد الفلكي من السطور لحسابها.

بعض الدوال f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} لا يمكن حسابها بدائرة منطقية (Boolean circuit) تستعمل عدداً من البوابات أقلّ من عدد أسّي (في nn).

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

ملاحظة — تمثيل أكفأ (متقدّم، اختياري)

تمثيل ASCII ليس أقصر تمثيلٍ لبرامج NAND-CIRC. برامج NAND-CIRC مكافئة لدوائر ببوابات NAND، ما يعني أنّ برنامج NAND-CIRC من ss سطراً وnn مدخلاً وmm مخرجاً يمكن تمثيله برسم بياني موجّه مُعنوَن من s+ns+n رأساً، منها nn ذات درجة داخلية صفرية، والـ ss الباقية ذات درجة داخلية لا تتجاوز اثنتين. وباستعمال تمثيل مصفوفة التجاور (adjacency matrix) لمثل هذه الرسوم البيانية، يمكننا تخفيض الثابت الضمني في مبرهنة — عدِّ البرامج ليقترب من 55 إلى حدٍّ لا نهائي، انظر تمرين — التمثيل الأكفأ للدوائر وحدٌّ أعلى عدٍّّ أدقّ.

مبرهنة تدرّج الحجم (اختياري)

بـمبرهنة — شمولية دوائر NAND، بحدّ محسّن يحتوي الصنف SIZEn(10⋅2n/n)SIZE_{n}(10 \cdot 2^n /n) على كل الدوال من {0,1}n\{0,1\}^n إلى {0,1}\{0,1\}، بينما بـمبرهنة — حدٌّ أدنى من عدِّ الدوال هناك بعضُ دالة f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} ليست منتمية إلى SIZEn(0.1⋅2n/n)SIZE_{n}(0.1 \cdot 2^n / n). وبعبارة أخرى، لكل nn كبيرٍ بما يكفي، SIZEn(0.12nn)⊊SIZEn(102nn)  . SIZE_n\left(0.1 \tfrac{2^n}{n} \right) \subsetneq SIZE_n\left(10 \tfrac{2^n}{n} \right) \;. ويبيّن أنّنا يمكننا استعمال مبرهنة — حدٌّ أدنى من عدِّ الدوال لإثبات نتيجةٍ أكثر عمومية: كلّما زدنا "ميزانيّتنا" من البوابات، استطعنا حساب دوال جديدة.

مبرهنة — مبرهنة تدرّج الحجم

لكل nn كبيرٍ بما يكفي و10n<s<0.1⋅2n/n10n < s < 0.1 \cdot 2^n /n، SIZEn(s)⊊SIZEn(s+10n)  . SIZE_n(s) \subsetneq SIZE_n(s+10n) \;.

لإثبات المبرهنة نحتاج إلى إيجاد دالة f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} بحيث ff يمكن حسابها بدائرة من s+10ns+10n بوابة، لكن لا يمكن حسابها بدائرة من ss بوابة. وسنقوم بذلك عبر متتالية من الدوال f0,f1,f2,…,fNf_0,f_1,f_2,\ldots,f_N لها الخصائص التالية: (1) f0f_0 يمكن حسابها بدائرة من 10n10n بوابة على الأكثر، (2) fNf_N لا يمكن حسابها بدائرة من 0.1⋅2n/n0.1 \cdot 2^n/n بوابة، و__(3)__ لكل i∈{0,…,N}i\in \{0,\ldots, N\}، إذا كان fif_i يمكن حسابها بدائرة من الحجم ss، فإنّ fi+1f_{i+1} يمكن حسابها بدائرة من الحجم s+10ns + 10n على الأكثر. مجتمعةً، هذه الخصائص تعني أنّه إذا كان ii أصغر عددٍ بحيث fi∉SIZEn(s)f_i \not\in SIZE_n(s)، فإنّه بما أنّ fi−1∈SIZEn(s)f_{i-1} \in SIZE_n(s) لا بدّ من أن يكون fi∈SIZEn(s+10n)f_i \in SIZE_n(s+10n)، وهذا هو ما نحتاج إلى إثباته. انظر الشكل التوضيحي لتوضيح.

نُثبت sizehiearchythm بتقديم قائمة  من الدوال بحيث  هي الدالة الصفرية كلّها، و دالةٌ (مُستخرجة من counting-lb) خارج ، وتختلف  و عن بعضهما على مدخلٍ واحدٍ على الأكثر. ويمكننا إثبات أنّ عدد البوابات اللازمة لحساب  لا يزيد عن عدد البوابات اللازمة لحساب  بمقدار  على الأكثر لكلّ ؛ وبالتالي، إذا اخترتَ  أصغر عددٍ بحيث ، فإنّ .

برهان

لنكن f∗:{0,1}n→{0,1}f^*: \{0,1\}^n \rightarrow \{0,1\} هي الدالة (وجودها مضمون لنا بـمبرهنة — حدٌّ أدنى من عدِّ الدوال) بحيث f∗∉SIZEn(0.1⋅2n/n)f^* \not\in SIZE_n(0.1 \cdot 2^n /n). نُعرّف الدوال f0,f1,…,f2nf_0,f_1,\ldots, f_{2^n} التي تُسقِط {0,1}n\{0,1\}^n إلى {0,1}\{0,1\} على النحو التالي. لكل x∈{0,1}nx\in \{0,1\}^n، إذا كان lex(x)∈{0,1,…,2n−1}lex(x) \in \{0,1,\ldots, 2^n-1\} هو ترتيب xx في الترتيب المعجميّ فإنّ fi(x)={f∗(x)lex(x)<i0otherwise  . f_i(x) = \begin{cases} f^*(x) & lex(x)< i \\ 0 & \text{otherwise} \end{cases} \;.

الدالة f0f_0 هي ببساطة دالة الصفر الثابتة، في حين أنّ الدالة f2nf_{2^n} تساوي f∗f^*. علاوةً على ذلك، لكل i∈[2n]i\in [2^n]، تختلف الدالتان fif_i وfi+1f_{i+1} على مدخلٍ واحد على الأكثر (أي المدخل x∈{0,1}nx \in \{0,1\}^n بحيث lex(x)=ilex(x)=i). لنكن 10n<s<0.1⋅2n/n10n < s < 0.1 \cdot 2^n /n، ولنكن ii أوّل فهرسٍ بحيث fi∉SIZEn(s)f_i \not\in SIZE_n(s). ولأنّ f2n=f∗∉SIZEn(0.1⋅2n/n)f_{2^n} = f^* \not\in SIZE_n(0.1 \cdot 2^n / n) فلا بدّ من وجود فهرسٍ ii من هذا النوع، بل إنّ i>0i>0 لأنّ دالة الصفر الثابتة تنتمي إلى SIZEn(10n)SIZE_n(10n).

وباختيارنا لـii، فإنّ fi−1f_{i-1} تنتمي إلى SIZEn(s)SIZE_n(s). ولإتمام البرهان، نحتاج إلى إظهار أنّ fi∈SIZEn(s+10n)f_i \in SIZE_n(s + 10n). لنكن x∗x^* هي السلسلة بحيث lex(x∗)=ilex(x^*)=i، ولنكن b∈{0,1}b\in \{0,1\} هي قيمة f∗(x∗)f^*(x^*). إذن يمكننا تعريف fif_i أيضاً على النحو التالي fi(x)={bx=x∗fi−1(x)x≠x∗ f_i(x) = \begin{cases} b & x=x^* \\ f_{i-1}(x) & x \neq x^* \end{cases} أو بمعنى آخر fi(x)=IF(EQUAL(x∗,x),b,fi−1(x)) f_i(x) = IF(EQUAL(x^*,x),b,f_{i-1}(x)) حيث EQUAL:{0,1}2n→{0,1}EQUAL:\{0,1\}^{2n} \rightarrow \{0,1\} هي الدالة التي تُسقِط x,x′∈{0,1}nx,x' \in \{0,1\}^n إلى 11 إذا كانا متساويين وإلى 00 خلاف ذلك. وبما أنّه (باختيارنا لـii) يمكن حساب fi−1f_{i-1} باستعمال ss بوابة على الأكثر، وأنّ (كما يمكن التحقّق بسهولة) EQUAL∈SIZEn(9n)EQUAL \in SIZE_n(9n)، فإنّنا نستطيع حساب fif_i باستعمال s+9n+O(1)≤s+10ns + 9n +O(1) \leq s +10n بوابة على الأكثر، وهذا هو ما أردنا إثباته.

تصويرٌ لبعض ما نعرفه عن طبقات تعقيد الحجم (بحجمٍ غير مطابق!). تُصوِّر هذه الصورة طبقاتٍ على النمط ، لكنّ وضع طبقات تعقيد الحجم الأخرى مثل  مماثل. فنعلم بحسب NAND-univ-thm (مع تحسين tight-upper-bound) أنّ كلّ الدوال التي تُطابق  بت إلى  بت يمكن حسابها بدائرةٍ حجمها  حيث ، بينما يُظهر حدّ العدّ الأدنى (counting-lb، انظر أيضًا countingmultibitex) أنّ بعض هذه الدوال سيتطلّب ، وتُظهر مبرهنة تدرّج الحجم (sizehiearchythm) وجود دوالٍ في  كلّما كان ، انظر أيضًا sizehiearchyex. ونَعتبر أيضًا أمثلةً معيّنة: جمع عددين من  بت يمكن إنجازه بخطوط ، بينما لا نعرف برنامجًا كهذا لـ_ضرب_ عددين من  بت، وإن كنّا نعلم أنّه يمكن إنجازه بحجم  وبالفعل بحجمٍ أفضل من ذلك. وفي ما سبق، يُقابل  مشكلة الضرب العكسية — أي إيجاد التحليل إلى عوامل أوّلية لعددٍ مُعطى. وفي الوقت الحالي لا نعرف أيّ دائرةٍ بعدد خطوطٍ متعدّد الحدود (أو حتّى دون أسّي) يمكنها حساب .

ملاحظة — الدوال الصريحة

بينما تُضمن مبرهنة تدرّج الحجم وجود بعضِ دالة يمكن حسابها، مثلاً، باستعمال n2n^2 بوابة لكن لا باستعمال 100n100n بوابة، فإنّنا لا نعرف أي مثالٍ صريحٍ لمثل هذه الدالة. ومع أنّنا نشتبه بأنّ ضرب الأعداد الصحيحة هو مثالٌ من هذا النوع، فإنّنا لا نملك أي برهان على أنّ الأمر كذلك.

تمثيل القوالب (tuples)

ASCII هو عرضٍ جيّد للبرامج، لكن في بعض التطبيقات يكون من المفيد أن نملك تمثيلاً أكثر تجريداً لبرامج NAND-CIRC. في هذا القسم نصف اختياراً معيّناً، سيكون مريحاً لنا لاحقاً. برنامج NAND-CIRC هو ببساطة متتالية من سطور من الشكل

blah = NAND(baz,boo)

لا شيء طبعاً في الأسماء الخاصة التي نستعملها للمتغيّرات. وإذ كانت قراءتها أصعب، يمكننا كتابة كل برامجنا باستعمال متغيّرات عملٍ فقط مثل temp_0 وtemp_1 إلخ. لذلك، يتجاهل تمثيلنا لبرامج NAND-CIRC الأسماء الفعلية للمتغيّرات، ويربط كل متغيّر ب عددٍ. ونحن نرمّز سطراً من البرنامج كثلاثية أعداد. إذا كان السطر على الشكل foo = NAND(bar,blah) فإنّنا نرمّزه بالثلاثية (i,j,k)(i,j,k) حيث ii هو العدد المقابل للمتغيّر foo، وjj وkk هما العددان المقابلان لـbar وblah على التوالي.

وبشكلٍ أكثر تحديداً، سنربط كل متغيّر بعددٍ من المجموعة [t]={0,1,…,t−1}[t]= \{0,1,\ldots,t-1\}. الأعداد nn الأولى {0,…,n−1}\{0,\ldots,n-1\} تقابل متغيّرات المدخلات (input)، والأعداد mm الأخيرة {t−m,…,t−1}\{t-m,\ldots,t-1\} تقابل متغيّرات المخرجات (output)، والأعداد الوسطى {n,…,t−m−1}\{ n,\ldots, t-m-1\} تقابل بقية متغيّرات "مساحة العمل" (workspace). رسمياً، نُعرّف تمثيلنا على النحو التالي:

تعريف — تمثيل قائمة القوالب

لنكن PP برنامج NAND-CIRC من nn مدخلاً وmm مخرجاً وss سطراً، ولنكن tt هو عدد المتغيّرات المتمايزة المستعملة في PP. تمثيل قائمة القوالب لـPP هو الثلاثية (n,m,L)(n,m,L) حيث LL قائمة من ثلاثيات على الشكل (i,j,k)(i,j,k) لكل i,j,k∈[t]i,j,k \in [t].

نُسند عدداً لكل متغيّر من متغيّرات PP على النحو التالي:

  • لكل i∈[n]i\in [n]، يُسند المتغيّر X[ii] العدد ii.

  • لكل j∈[m]j\in [m]، يُسند المتغيّر Y[jj] العدد t−m+jt-m+j.

  • كل متغيّرٍ آخر يُسند عدداً من {n,n+1,…,t−m−1}\{n,n+1,\ldots,t-m-1\} بترتيب ظهور المتغيّر في البرنامج PP.

تمثيل قائمة القوالب هو خيارنا الافتراضي لتمثيل برامج NAND-CIRC. ولأنّ عبارة "تمثيل قائمة القوالب" بعض الشيء مُرهقة، فإنّنا سنسمّيها غالباً مجرّداً "التمثيل" (representation) للبرنامج PP. وأحياناً، حين يكون عدد المدخلات nn وعدد المخرجات mm معروفَين من السياق، فإنّنا نمثّل البرنامج ببساطة كقائمة LL بدلاً من الثلاثية (n,m,L)(n,m,L).

مثال — تمثيل برنامج XOR

برنامج NAND-CIRC المفضّل لدينا، وهو البرنامج

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

الذي يحسب دالة XOR، يُمثَّل بالثلاثية (2,1,L)(2,1,L) حيث L=((2,0,1),(3,0,2),(4,1,2),(5,3,4))L=((2, 0, 1), (3, 0, 2), (4, 1, 2), (5, 3, 4)). أي أنّ المتغيّرات X[0] وX[1] تُعطى الفهارس 00 و11 على التوالي، والمتغيّرات u وv وw تُعطى الفهارس 2,3,42,3,4 على التوالي، بينما المتغيّر Y[0] يُعطى الفهرس $5`.

تحويل برنامج NAND-CIRC من تمثيله كشيفرة إلى تمثيله كقائمة قوالب هو تمرينٌ برمجيٌّ مباشر إلى حدٍّ كبير، ويمكن تحديداً أن يُنجَز في بضعة أسطر من Python.[4] يفقد تمثيل قائمة القوالب معلوماتٍ مثل الأسماء الخاصة التي استعملناها للمتغيّرات، لكن هذا مقبول لأنّ هذه الأسماء لا تُحدث فرقاً في وظائف البرنامج.

من القوالب إلى السلاسل

إذا كان PP برنامجاً من الحجم ss، فإنّ عدد المتغيّرات tt هو 3s3s على الأكثر (إذ إنّ كل سطرٍ يمسّ ثلاثة متغيّرات كحدٍّ أقصى). وعليه يمكننا ترميز كل فهرس متغيّر في [t][t] كسلسلة من الطول ℓ=⌈log⁡(3s)⌉\ell = \ceil{\log (3s)}، بإضافة أصفارٍ بادئة عند الحاجة. ولأنّ هذا ترميزٌ بطولٍ ثابت، فإنّه خالٍ من البوابات (prefix free)، وعليه يمكننا ترميز القائمة LL من ss ثلاثيات (المقابلة لترميز ss سطوراً من البرنامج) ببساطة كسلسلة من الطول 3ℓs3\ell s نحصل عليها بدمج كل هذه الترميزات.

نُعرّف S(s)S(s) على أنّها طول السلسلة التي تمثّل القائمة LL المقابلة لبرنامج من الحجم ss. من ما سبق نرى أنّ S(s)=3s⌈log⁡(3s)⌉  . S(s) = 3s\ceil{\log (3s)} \;.

يمكننا تمثيل P=(n,m,L)P=(n,m,L) كسلسلة عبر إلحاق تمثيلٍ خالٍ من البوابات لـnn وmm في مقدّمة القائمة LL. ولأنّ n,m≤3sn,m \leq 3s (فبرنامج يجب أن يمسّ كل متغيّرات مدخلاته ومخرجاته مرّةً واحدة على الأقلّ)، فإنّ تلك التمثيلات الخالية من البوابات يمكن ترميزها باستعمال سلاسل من الطول O(log⁡s)O(\log s). وبشكلٍ خاص، كل برنامج PP من ss سطراً على الأكثر يمكن تمثيله بسلسلة من الطول O(slog⁡s)O(s\log s). وبالمثل، كل دائرة CC من ss بوابة على الأكثر يمكن تمثيلها بسلسلة من الطول O(slog⁡s)O(s \log s) (مثلاً بترجمة CC إلى البرنامج المكافئ PP).

مُفسِّر NAND-CIRC بلغة NAND-CIRC

بما أنّنا نستطيع تمثيل البرامج كسلاسل، فإنّنا نستطيع أيضاً أن نفكّر في البرنامج كمدخلٍ لدالة. وبشكلٍ خاص، لكل عددٍ طبيعي s,n,m>0s,n,m>0 نُعرّف الدالة EVALs,n,m:{0,1}S(s)+n→{0,1}mEVAL_{s,n,m}:\{0,1\}^{S(s)+n} \rightarrow \{0,1\}^m على النحو التالي: EVALs,n,m(px)={P(x)p∈{0,1}∣S(s)∣ represents a size-s program P with n inputs and m outputs0motherwise EVAL_{s,n,m}(px) = \begin{cases} P(x) & \text{$p\in \{0,1\}^{|S(s)|}$ represents a size-$s$ program $P$ with $n$ inputs and $m$ outputs} \\ 0^m & \text{otherwise} \end{cases} حيث S(s)S(s) مُعرَّفة كما في المعادلة، ونحن نستعمل مخطّط التمثيل التجريدي الموصوف في تمثيل البرامج كسلاسل.

أي أنّ EVALs,n,mEVAL_{s,n,m} يأخذ كمدخل دمج سلسلتين: سلسلة p∈{0,1}∣S(s)∣p\in \{0,1\}^{|S(s)|} وسلسلة x∈{0,1}nx\in \{0,1\}^n. إذا كانت pp سلسلة تمثّل قائمة ثلاثيات LL بحيث يكون (n,m,L)(n,m,L) تمثيلَ قائمة قوالب لبرنامج NAND-CIRC PP من الحجم ss، فإنّ EVALs,n,m(px)EVAL_{s,n,m}(px) تساوي التقييم P(x)P(x) للبرنامج PP على المدخل xx. وإلا فإنّ EVALs,n,m(px)EVAL_{s,n,m}(px) تساوي 0m0^m (وهذه الحالة ليست مهمّة جداً: يمكنك ببساطة أن تفكّر في 0m0^m على أنّها نوعٌ من "قيمة المهملات" الذي يدلّ على خطأ).

ما يجب استخلاصه. التفاصيل الدقيقة لتعريف EVALs,n,mEVAL_{s,n,m} ليست جوهرية إلى حدٍّ كبير. بل ما يلزمك تذكّره عن EVALs,n,mEVAL_{s,n,m} هو ما يلي:

  • EVALs,n,mEVAL_{s,n,m} دالة منتهية تأخذ سلسلةً ذات طولٍ ثابت كمدخل وتُخرج سلسلةً ذات طولٍ ثابت كمخرج.

  • EVALs,n,mEVAL_{s,n,m} دالةٌ واحدة، بحيث إنّ حساب EVALs,n,mEVAL_{s,n,m} يتيح تقييم أيّ برامج NAND-CIRC بطولٍ ما على مدخلات أيّ وبالطول المناسب.

  • EVALs,n,mEVAL_{s,n,m} هي دالة لا برنامج (تذكّر النقاش في المواصفة مقابل التنفيذ (مرة أخرى)). أي أنّ EVALs,n,mEVAL_{s,n,m} هي مواصفة (specification) لما يقابل كلَّ مدخلٍ من المخرجات. ووجود برنامج يحسب EVALs,n,mEVAL_{s,n,m} (أي تنفيذٍ (implementation) لـEVALs,n,mEVAL_{s,n,m}) واقعةٌ منفصلة، تحتاج إلى إثبات (وسنقوم بذلك في مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC، مع برنامج أكفأ معروض في مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC).

أوّل الأمثلة على الدائريّة ذاتية المرجع (self circularity) التي سنراها في هذا الكتاب هو المبرهنة التالية، التي يمكننا التفكير فيها على أنّها تُظهر "مُفسِّر NAND-CIRC بلغة NAND-CIRC":

مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC

لكل s,n,m∈Ns,n,m \in \N بحيث s≥ms\geq m هناك برنامج NAND-CIRC Us,n,mU_{s,n,m} يحسب الدالة EVALs,n,mEVAL_{s,n,m}.

أي أنّ برنامج NAND-CIRC Us,n,mU_{s,n,m} يأخذ وصفَ أيّ برنامج NAND-CIRC آخر PP (بالطول والمدخلات/المخرجات الصحيحة) و_أيّ مدخلٍ_ xx، ويحسب نتيجة تقييم البرنامج PP على المدخل xx. وبناءً على التكافؤ بين برامج NAND-CIRC والدوائر المنطقية، يمكننا أيضاً أن نفكّر في Us,n,mU_{s,n,m} كدائرة تأخذ كمدخل وصف دوائر أخرى ومدخلاتها، وتُرجع تقييمها، انظر الشكل التوضيحي. نسمّي برنامج NAND-CIRC هذا Us,n,mU_{s,n,m} الذي يحسب EVALs,n,mEVAL_{s,n,m} برنامجاً كونيّاً مقيَّداً (bounded universal program) (أو دائرة كونية، انظر الشكل التوضيحي). "الكوني" تشير إلى أنّ هذا برنامجٌ واحد يستطيع تقييم أيّ شيفرة، بينما "المقيَّد" تشير إلى أنّ Us,n,mU_{s,n,m} يقيّم برامج ذات حجمٍ مقيَّد فقط. طبعاً فإنّ هذا القيد متأصّل في لغة برمجة NAND-CIRC، إذ إنّ برنامجاً من ss سطراً (أو، بما يعادل، دائرة من ss بوابة) يمكنه أن يأخذ على الأكثر 2s2s مدخلاً. ولاحقاً، في الحلقات وما لا نهاية، سنُقدّم مفهوم الحلقات (loops) (ونموذج آلات تورينغ (Turing machines))، الذي يتيح التخلّص من هذا القيد.

برهان

مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC نتيجةٌ مهمّة، لكنّها في الحقيقة ليست صعبة الإثبات. وبشكلٍ محدّد، بما أنّ EVALs,n,mEVAL_{s,n,m} دالة منتهية، فإنّ مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC نتيجةٌ فورية من مبرهنة — شمولية NAND، التي تنصّ على أنّ كلّ دالة منتهية يمكن حسابها بـ_بعضِ_ برامج NAND-CIRC.

توقّف وتأمّل

مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC بسيطة لكنّها مهمّة. تأكّد أنّك تفهم ما تعنيه هذه المبرهنة، ولماذا هي نتيجةٌ لازمة من مبرهنة — شمولية NAND.

/images/introtcs/lec_04_code_and_data-4.webp

برامج كونية أكفأ

مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC تُثبت وجود برنامج NAND-CIRC لحساب EVALs,n,mEVAL_{s,n,m}، لكنها لا تقدّم أي حدٍّ صريح على حجم هذا البرنامج. ومبرهنة — شمولية NAND، التي استعملناها لإثبات مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC، تضمن وجود برنامج NAND-CIRC قد يبلغ حجمه أسّياً (exponential) في طول مدخله. وهذا يعني أنّه حتى لقيم s,n,ms,n,m المتوسطة الصغر (مثل n=100,s=300,m=1n=100,s=300,m=1)، قد يتطلّب حساب EVALs,n,mEVAL_{s,n,m} برنامج NAND بعدد من السطور أكبر من عدد الذرّات في الكون المرصود! ولحسن الحظّ، يمكننا أن نفعل أفضل بكثير من ذلك. وفعلاً، لكل s,n,ms,n,m يوجد برنامج NAND-CIRC لحساب EVALs,n,mEVAL_{s,n,m} حجمه متعدّد الحدود (polynomial) في طول مدخله. وهذا ما تُظهره المبرهنة التالية.

مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC

لكل s,n,m∈Ns,n,m \in \N هناك برنامج NAND-CIRC من O(s2log⁡s)O(s^2 \log s) سطراً على الأكثر يحسب الدالة EVALs,n,m:{0,1}S+n→{0,1}mEVAL_{s,n,m}:\{0,1\}^{S+n} \rightarrow \{0,1\}^m المعرَّفة أعلاه (حيث SS هو عدد البتات اللازمة لتمثيل برامج من ss سطراً).

توقّف وتأمّل

إن لم تكن قد راجعت ذلك من قبل، فقد يكون هذا وقتاً مناسباً لمراجعة رمز OO في المقاربات وترميز O الكبير. وبشكلٍ خاص، فإنّ طريقةً مكافئة لصياغة مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC هي القول إنّها تنصّ على أنّه يوجد عدد c>0c>0 بحيث إنّ لكل s,n,m∈Ns,n,m \in \N، يوجد برنامج NAND-CIRC PP من cs2log⁡sc s^2 \log s سطراً على الأكثر يحسب الدالة EVALs,n,mEVAL_{s,n,m}.

على خلاف مبرهنة — الكونية المقيّدة لبرامج NAND-CIRC، فإنّ مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC ليست نتيجةً لازمة تافهة من حقيقة أنّ كل دالة منتهية يمكن حسابها بدائرة ما. فإثبات مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC يتطلّب منا أن نقدّم برنامج NAND-CIRC تجريدياً لحساب الدالة EVALs,n,mEVAL_{s,n,m}. وسنقوم بذلك على عدّة مراحل.

  1. أوّلاً، سنصف الخوارزمية التي تقيّم EVALs,n,mEVAL_{s,n,m} في "شيفرةٍ زائفة" (pseudo code).

  2. ثمّ، سنُظهر كيف يمكننا كتابة برنامج لحساب EVALs,n,mEVAL_{s,n,m} في Python. لن نستعمل من Python سوى القليل، ويمكن لقارئٍ متمرّس على البرمجة بأيّ لغة أن يتابع معنا.

  3. أخيراً، سنُظهر كيف يمكننا تحويل برنامج Python هذا إلى برنامج NAND-CIRC.

يسفر هذا النهج عن أكثر بكثير من مجرّد إثبات مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC: سنرى أنّه من الممكن بالفعل دائماً تحويل شيفرةٍ (خالٍة من الحلقات) المكتوبة في لغاتٍ عالية المستوى مثل Python إلى برامج NAND-CIRC (وعليه إلى الدوائر المنطقية أيضاً).

مُفسِّر NAND-CIRC بـ"الشيفرة الزائفة"

لإثبات مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC يكفي تقديم برنامج NAND-CIRC من O(s2log⁡s)O(s^2 \log s) سطراً يستطيع تقييم برامج NAND-CIRC من ss سطراً. لنبدأ إذن بالتفكير في كيفية تقييمنا لمثل هذه البرامج لو كنا مقيدين بتنفيذ عمليات NAND فقط. أي لنصف بإيجاز صورةَ خوارزميةٍ تُدخل عليها n,m,sn,m,s، وقائمة ثلاثيات LL، وسلسلة x∈{0,1}nx\in \{0,1\}^n، فتقيم البرنامج الذي تمثّله (n,m,L)(n,m,L) على السلسلة xx.

توقّف وتأمّل

سيكون من المفيد جداً أن تتوقّف هنا وتحاول حلّ هذه المسألة بنفسك. على سبيل المثال، يمكنك أن تفكّر في كيفية كتابة برنامج NANDEVAL(n,m,s,L,x) يحسب هذه الدالة بلغة البرمجة التي تختارها.

سنصف الآن خوارزميةً من هذا النوع. نحن نفترض أنّ لدينا في المتناول بنية بيانات من نوع مصفوفة بتّات (bit array) تستطيع أن تخزّن لكل i∈[t]i\in [t] بتّاً Ti∈{0,1}T_i \in \{0,1\}. وبشكلٍ تحديد، إذا كان Table متغيّراً يحمل هذه البنية، فإنّنا نفترض أنّنا نستطيع تنفيذ العمليتين:

  • GET(Table,i) التي تسترجع البت المقابل لـi في Table. ويُفترض أن تكون قيمة i عدداً صحيحاً في [t][t].

  • Table = UPDATE(Table,i,b) التي تُحدِّث Table بحيث يصبح البت المقابل لـi مساوياً الآن لـb. ويُفترض أن تكون قيمة i عدداً صحيحاً في [t][t] وأن يكون b بتّاً في {0,1}\{0,1\}.

خوارزمية — Eval NAND-CIRC programs

Input: Numbers $n,m,s$ and $t\leq 3s$, as well as  a list $L$ of $s$ triples of numbers in $[t]$, and  a string $x\in \{0,1\}^n$.

Output: Evaluation of the program represented by $(n,m,L)$ on the -input $x\in \{0,1\}^n$.

Let `Vartable` be table of size $t$
For{$i$ in $[n]$}
`Vartable = UPDATE(Vartable,`$i$`,`$x_i$`)`
Endfor
For{$(i,j,k)$ in $L$}
$a \leftarrow$ `GET(Vartable,`$j$`)`
$b \leftarrow$ `GET(Vartable,`$k$`)`
`Vartable = UPDATE(Vartable,`$i$,`NAND(`$a$`,`$b$`))`
Endfor
For{$j$ in $[m]$}
$y_j \leftarrow$ `GET(Vartable,`$t-m+j$`)`
Endfor
Return $y_0,\ldots,y_{m-1}$

خوارزمية — Eval NAND-CIRC programs يقيّم البرنامج المُعطى له كمدخل سطراً بعد سطر، مُحدِّثاً جدول Vartable ليحتوي على قيمة كل متغيّر. وفي نهاية التنفيذ يُخرج المتغيّرات في المواضع t−m,t−m+1,…,t−1t-m,t-m+1,\ldots,t-1، وهي المتغيّرات المقابلة لمدخلات.

مُفسِّر NAND بلغة Python

لنجعل الأمر أكثر تجريداً، ولنرَ كيف نُنفّذ خوارزمية — Eval NAND-CIRC programs في لغة البرمجة Python. (لا شيء خاص في Python؛ كان من اليسير أن نقدّم الدالة المقابلة في JavaScript أو C أو OCaml أو أيّ لغة برمجة أخرى.) سنبني دالة NANDEVAL التي على مدخلات n,m,L,xn,m,L,x تُخرج نتيجة تقييم البرنامج الذي تمثّله (n,m,L)(n,m,L) على xx. ولتبسيط المسألة، لن نهتمّ بالحالة التي لا تكون فيها LL تمثيلاً لبرنامجٍ صحيح من nn مدخلاً وmm مخرجاً. الشيفرة معروضة في Code for evaluating a NAND-CIRC program given in the list-of-tuples representation.

Code for evaluating a NAND-CIRC program given in the list-of-tuples representation

def NANDEVAL(n,m,L,X):
    # Evaluate a NAND-CIRC program from list of tuple representation.
    s = len(L) # num of lines
    t = max(max(a,b,c) for (a,b,c) in L)+1 # max index in L + 1
    Vartable = [0] * t # initialize array

    # helper functions
    def GET(V,i): return V[i]
    def UPDATE(V,i,b):
        V[i]=b
        return V

    # load input values to Vartable:
    for i in range(n):
        Vartable = UPDATE(Vartable,i,X[i])

    # Run the program
    for (i,j,k) in L:
        a = GET(Vartable,j)
        b = GET(Vartable,k)
        c = NAND(a,b)
        Vartable = UPDATE(Vartable,i,c)

    # Return outputs Vartable[t-m], Vartable[t-m+1],....,Vartable[t-1]
    return [GET(Vartable,t-m+j) for j in range(m)]

# Test on XOR (2 inputs, 1 output)
L = ((2, 0, 1), (3, 0, 2), (4, 1, 2), (5, 3, 4))
print(NANDEVAL(2,1,L,(0,1))) # XOR(0,1)
# [1]
print(NANDEVAL(2,1,L,(1,1))) # XOR(1,1)
# [0]

الوصول إلى عنصر في المصفوفة Vartable عند فهرسٍ معيّن يستغرق عدداً ثابتاً من العمليات الأساسية. وعليه (بما أنّ n,m≤sn,m \leq s وt≤3st \leq 3s)، فإنّ البرنامج أعلاه سيستعمل O(s)O(s) عملية أساسية.[5]

بناء مُفسِّر NAND-CIRC بلغة NAND-CIRC

ننتقل الآن إلى وصف برهان مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC. ولإثبات المبرهنة لا يكفي تقديم برنامج بايثون. بل نحتاج إلى إظهار كيف نحسب الدالة EVALs,n,mEVAL_{s,n,m} باستعمال برنامج NAND-CIRC. بعبارة أخرى، مهمّتنا هي أن نحوّل، لكل s,n,ms,n,m، شيفرة بايثون الموجودة في #nandevalpythonsec إلى برنامج NAND-CIRC Us,n,mU_{s,n,m} يحسب الدالة EVALs,n,mEVAL_{s,n,m}.

توقّف وتأمّل

قبل أن تقرأ المزيد، حاول أن تفكّر في كيفية تقديمك أنت لـ"برهانٍ بنّاء" لـمبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC. أي فكّر في كيف تكتب، بلغة البرمجة التي تختارها، دالة universal(s,n,m) تُخرج على المدخلات s,n,ms,n,m شيفرة برنامج NAND-CIRC Us,n,mU_{s,n,m} بحيث يحسب Us,n,mU_{s,n,m} الدالة EVALs,n,mEVAL_{s,n,m}. هناك فرقٌ دقيق لكنّه حاسم بين هذه الدالة وبين برنامج بايثون NANDEVAL الموصوف أعلاه. فبدلاً من تقييم برنامجٍ معطى PP فعلياً على مدخلٍ ما ww، ينبغي أن تُخرج الدالة universal شيفرةَ برنامج NAND-CIRC يحسب الخرائط (P,x)↦P(x)(P,x) \mapsto P(x).

سيتبع بناؤنا عن قربٍ شديداً تنفيذ بايثون لـEVAL أعلاه. سنستعمل المتغيّرات Vartable[00],…\ldots,Vartable[2ℓ−12^\ell-1], حيث ℓ=⌈log⁡3s⌉\ell = \ceil{\log 3s} لتخزين متغيّراتنا. غير أنّ NAND لا تملك متغيّرات ذات قيم صحيحة، لذا لا يمكننا كتابة شيفرة مثل Vartable[i] لمتغيّرٍ ما i. لكنّنا نستطيع أن نُنفّذ الدالة GET(Vartable,i) التي تُخرج البت رقم i من المصفوفة Vartable. وهذا ليس إلا الدالة LOOKUPℓLOOKUP_\ell التي رأيناها في مبرهنة — دالة البحث!

توقّف وتأمّل

من فضلك تأكّد أنّك تفهم لماذا GET وLOOKUPℓLOOKUP_\ell هما الدالة نفسها.

رأينا أنّه يمكننا حساب LOOKUPℓLOOKUP_\ell في زمن O(2ℓ)=O(s)O(2^\ell) = O(s) لاختيارنا لـℓ\ell.

لكل ℓ\ell، لتكن UPDATEℓ:{0,1}2ℓ+ℓ+1→{0,1}2ℓUPDATE_\ell:\{0,1\}^{2^\ell + \ell +1} \rightarrow \{0,1\}^{2^\ell} مقابلَ الدالة UPDATE للمصفوفات من الطول 2ℓ2^\ell. أي أنّها على المدخلات V∈{0,1}2ℓV\in \{0,1\}^{2^\ell}، وi∈{0,1}ℓi\in \{0,1\}^\ell، وb∈{0,1}b\in \{0,1\}، تساوي UPDATEℓ(V,i,b)UPDATE_\ell(V,i,b) السلسلة V′∈{0,1}2ℓV' \in \{0,1\}^{2^\ell} بحيث Vj′={Vjj≠ibj=i V'_j = \begin{cases} V_j & j \neq i \\ b & j = i \end{cases} حيث نُعرّف السلسلة i∈{0,1}ℓi \in \{0,1\}^\ell مع عددٍ في {0,…,2ℓ−1}\{0,\ldots, 2^{\ell}-1 \} باستعمال التمثيل الثنائي. ويمكننا حساب UPDATEℓUPDATE_\ell باستعمال برنامج NAND-CIRC من O(2ℓℓ)=(slog⁡s)O(2^\ell \ell)=(s \log s) سطراً على النحو التالي:

  1. لكل j∈[2ℓ]j\in [2^\ell]، يوجد برنامج NAND-CIRC من O(ℓ)O(\ell) سطراً لحساب الدالة EQUALSj:{0,1}ℓ→{0,1}EQUALS_j: \{0,1\}^\ell \rightarrow \{0,1\} التي على المدخل ii تُخرج 11 إذا وفقط إذا كان ii مساوياً لـ(التمثيل الثنائي لـ)jj. (نترك التحقّق من ذلك كتمارين في تمرين — دالة المساواة وتمرين — الدالة المساوية لدالة ثابتة.)

  2. رأينا أنّنا يمكننا حساب الدالة IF:{0,1}3→{0,1}IF:\{0,1\}^3 \rightarrow \{0,1\} بحيث تساوي IF(a,b,c)IF(a,b,c) القيمة bb إذا كان a=1a=1 والقيمة cc إذا كان a=0a=0.

مجتمعةً، يعني هذا أنّنا يمكننا حساب UPDATE (باستعمال بعض "سكر النحو" للحلقات ذات الطول المقيَّد) على النحو التالي:

def UPDATE_ell(V,i,b):
    # Get V[0]...V[2^ell-1], i in {0,1}^ell, b in {0,1}
    # Return NewV[0],...,NewV[2^ell-1]
    # updated array with NewV[i]=b and all
    # else same as V
    for j in range(2**ell): # j = 0,1,2,....,2^ell -1
        a = EQUALS_j(i)
        NewV[j] = IF(a,b,V[j])
    return NewV

ولأنّ الحلقة على j في UPDATE تُنفَّذ 2ℓ2^\ell مرّة، ولأنّ حساب EQUALS_j يستغرق O(ℓ)O(\ell) سطراً، فإنّ العدد الإجمالي للأسطر اللازمة لحساب UPDATE هو O(2ℓ⋅ℓ)=O(slog⁡s)O(2^\ell \cdot \ell) = O(s \log s). ومتى استطعنا حساب GET وUPDATE، فإنّ بقية التنفيذ تتلخّص في "مسك دفاتر" (book keeping) يجب أن يُنجَز بعناية، لكنّه ليس بحدّ ذاته بالكثير من البصيرة، ولذلك نُغفل التفاصيل الكاملة. ولأنّنا نُنفّذ GET وUPDATE ss مرّة، فإنّ العدد الإجمالي للأسطر اللازمة لحساب EVALs,n,mEVAL_{s,n,m} هو O(s2)+O(s2log⁡s)=O(s2log⁡s)O(s^2) + O(s^2 \log s) = O(s^2 \log s). وهذا يُكمل (باستثناء التفاصيل المُغفلة) برهان مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC.

ملاحظة — التحسين إلى تكلفةٍ شبه خطّية (ملاحظة متقدّمة اختيارية)

برنامج NAND-CIRC أعلاه أقلّ كفاءةً من نظيره في بايثون، إذ إنّ NAND لا تقدّم مصفوفات ذات وصولٍ عشوائيٍّ كفء. فمثلاً تستغرق عملية LOOKUP على مصفوفة من ss بتّ Ω(s)\Omega(s) سطراً في NAND، رغم أنّها تستغرق O(1)O(1) خطوة (أو ربما O(log⁡s)O(\log s) خطوة، بحسب طريقة عدّنا) في Python.

ويبيّن أنّه من الممكن تحسين حدّ مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC، وتقييم برامج NAND-CIRC من ss سطراً باستعمال برنامج NAND-CIRC من O(slog⁡s)O(s \log s) سطراً. المفتاح هو النظر في وصف برامج NAND-CIRC كدوائر، وبشكلٍ خاص كرسوم بيانية موجّهةٍ مُعطّلة الحلقات (DAGs) ذات درجة داخلية مقيّدة. وسيقابل البرنامج الكوني UsU_s لبرامج ss سطراً رسمٌ بياني كونيّ (universal graph) HsH_s لمثل رسوم DAG ذات ss رأساً. ويمكننا أن نفكّر في مثل هذا الرسم البياني UsU_s على أنّه "توصيلٌ ثابت" (wiring) لشبكة اتصالات، ينبغي أن تكون قادرة على استيعاب أيّ نمطٍ اعتباطي من الاتصال بين ss رأساً (حيث يقابل هذا النمط برنامج NAND-CIRC من ss سطراً). ويبيّن أنّه توجد [شبكات توجيه] (https://goo.gl/NnkkjM) بكفاءةٍ تتيح تضمين أيّ دائرة من ss رأساً داخل رسم بياني كونيّ من الحجم O(slog⁡s)O(s \log s)، انظر الملاحظات المرجعية ملاحظات مرجعية للمزيد عن هذه المسألة.

مُفسِّر Python بلغة NAND-CIRC (نقاش)

لإثبات مبرهنة — الكونية المقيّدة الأكفأ لبرامج NAND-CIRC قمنا بالأساس بترجمة كل سطرٍ في برنامج بايثون لـEVAL إلى مقطع NAND-CIRC مكافئ. لكنّ أيّاً من استدلالاتنا لم يكن خاصّاً بالدالة EVALEVAL بعينها. فمن الممكن ترجمة كلّ برنامج بايثون إلى برنامج NAND-CIRC مكافئ بكفاءةٍ مماثلة. (وبشكلٍ أكثر تحديداً، إذا كان برنامج بايثون يستغرق T(n)T(n) عمليةً على مدخلات طولها nn على الأكثر، فإنّ هناك برنامج NAND-CIRC من O(T(n)log⁡T(n))O(T(n) \log T(n)) سطراً يوافق برنامج بايثون على المدخلات ذات الطول nn.) والاضطرار إلى فعل ذلك يتطلّب العناية بالتفاصيل الكثيرة وهو خارج نطاق هذا الكتاب، لكنّ دعني أحاول أن أقنعك بأنّ ذلك ممكن من حيث المبدأ.

للبداية، يمكننا استعمال CPython (التنفيذ المرجعي لبايثون) لتقييم كلّ برامج بايثون باستعمال برنامج C. ويمكننا أن نجمع هذا مع مُصرِّف C لتحويل برنامج بايثون إلى نكهاتٍ مختلفة من "لغة الآلة". إذن، لتحويل برنامج بايثون إلى برنامج NAND-CIRC مكافئ، يكفي أن نُظهر كيفية تحويل برنامج بلغةِ آلةٍ ما إلى برنامج NAND-CIRC مكافئ. ومن عائلات لغات الآلة المُبسَّطة (وبالتالي المريحة) ما يُعرف بـ_معمارية ARM_ (ARM architecture) التي تُشغّل الكثير من الأجهزة المحمولة بما فيها كلّ أجهزة Android تقريباً.[6] وهناك لغات آلةٍ أبسط من ذلك، مثل معمارية LEG التي نُفّذ لها واجهةٌ خلفية (backend) لـمُصرِّف LLVM (وعليه يمكن أن يكون الهدفَ من ترجمة أيٍّ من القائمة الكبيرة والمتزايدة من اللغات التي يدعمها هذا المُصرِّف). ومن الأمثلة الأخرى معمارية TinyRAM (أُلهمت بأنظمة البرهان التفاعلي التي سنتناولها في البراهين والخوارزميات)، ومعمارية الحاسوب البسيط بشكلٍ مُضحك (Ridiculously Simple Computer) المُوجَّهة للتعليم. إنّ المرور واحداً واحداً على مجموعات التعليمات لمثل هذه الحواسيب وترجمتها إلى مقاطع NAND ليس ممتعاً، لكنّه أمرٌ يمكن إنجازه. وفعلاً، في نهاية المطاف هذا قريبٌ جداً من التحويل الذي يجري عند تحويل شيفرتنا عالية المستوى إلى بوابات سيليكون حقيقية، وهي ليست مختلفة كثيراً عن عمليات برنامج NAND-CIRC. وفعلاً إنّ أدواتً مثل MyHDL التي تحوّل "من بايثون إلى السيليكون" أن تُستعمل لتحويل برنامج بايثون إلى برنامج NAND-CIRC.

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

قد يلاحظ القارئ الدقيق أنّ الفقرات أعلاه لم توضّح إلّا لماذا ينبغي أن يكون من الممكن، لكل دالة ff معيّنة قابلة للحساب ببايثون، إيجاد برنامج NAND-CIRC معيّنٍ بكفاءةٍ مماثلة يحسب ff. لكنّ هذا يبدو أيضاً قاصراً عن هدفنا، وهو كتابة "مُفسِّر Python بلغة NAND"، ما يعني أنّه لكل وسيط nn، نجد برنامج NAND-CIRC واحداً UNIVsUNIV_s بحيث إنّ هذا البرنامج، معطى وصفَ برنامج بايثون PP ومدخلاً معيّناً xx وحدّاً TT على عدد العمليات (حيث إنّ أطوال PP وxx وقيمة TT هي كلّها ss على الأكثر)، يُرجع نتيجة تنفيذ PP على xx في TT خطوة على الأكثر. بعد كلّ شيء، التحويل أعلاه يأخذ كلّ برنامج بايثون إلى برنامج NAND-CIRC مختلف، وعليه لا ينتج "برنامج NAND-CIRC واحدٌ يحكمهم جميعاً" (بالإنجليزية: "one NAND-CIRC program to rule them all")يستطيع تقييم كلّ برامج بايثون حتى تعقيدٍ ما. لكنّنا نستطيع في الحقيقة أن نُنجز برنامج NAND-CIRC واحداً يقيّم برامج بايثون أيّاً كانت. والسبب في ذلك هو وجود مُفسِّر Python بلغة Python: برنامج بايثون UU يأخذ سلسلةَ بتّات، ويفسّرها كشيفرة بايثون، ثمّ ينفّذ تلك الشيفرة. وعليه، لا نحتاج إلّا إلى إظهار وجود برنامج NAND-CIRC U∗U^* يحسب الدالة نفسها التي يحسبها برنامج بايثون UU بعينه، وهذا سيمنحنا طريقةً لتقييم كل برامج بايثون.

إنّ ما نراه مرّةً بعد أخرى هو مفهوم كونية (universality) الحساب أو الإحالة الذاتية (self reference)، أي أنّ كلّ نماذج الحساب الغنية بما يكفي تعبيريّةٌ بما يكفي لتتمكن من "محاكاة نفسها". ولا يمكن المبالغة في أهمية هذه الظاهرة بالنسبة لنظرية الحساب وممارسته، بل وللأبعد من ذلك بكثير، بما في ذلك أسس الرياضيات والمسائل الأساسية في العلم.

أطروحة كيرش-تورينغ الفيزيائية الموسّعة (نقاش)

لقد رأينا أنّ بوابات NAND (وغيرها من العمليات المنطقية) يمكن تنفيذها باستعمال أنظمةٍ مختلفةٍ تماماً في العالم الفيزيائي. وماذا عن الاتجاه المعاكس؟ هل تستطيع برامج NAND-CIRC محاكاة أيّ حاسوبٍ فيزيائي؟

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

"أطروحة كيرش-تورينغ الفيزيائية الموسّعة" (PECTT): إذا كانت الدالة F:{0,1}n→{0,1}mF:\{0,1\}^n \rightarrow \{0,1\}^m قابلة للحساب في العالم الفيزيائي باستعمال مقدار ss من "الموارد الفيزيائية"، فإنّه يمكن حسابها ببرنامج دوائر منطقية من نحو ss بوابة.

قد يبدو على نظرية ساذجة أنّ افتراض أنّ نموذجنا المتواضع لبرامج NAND-CIRC أو للدوائر المنطقية يلتقط كلّ الحساب الفيزيائي الممكن هو افتراضٌ متطرّف إلى حدٍّ كبير. لكنّنا، في أكثر من قرنٍ من تقنيات الحوسبة، لم يبنِ أحدٌ بعد أيّ جهاز حوسبة قابل للتوسّع يتحدّى هذا الفرضية.

لنناقش الآن "التفاصيل الدقيقة" لـ PECTT بمزيدٍ من التفصيل، وكذلك المحاولات (التي لم تنجح حتى الآن) التي طُرحت ضدّه. لا توجد صياغةٌ رسمية واحدة متّفقٌ عليها عالميّاً على"نحو ss من الموارد الفيزيائية"، لكنّ بإمكاننا تقريب هذا المفهوم بالنظر في حجم أيّ جهاز حوسبة فيزيائي والزمن الذي يستغرقه حساب المخرجات، وطلب أن يُحاكى أيّ جهازٍ من هذا النوع بواسطة دائرة منطقية يكون عدد بواباتها متعدّداً للحدود (بأسّ لا كبير إلى حدٍّ ما) في حجم النظام وفي الزمن الذي يستغرقه كي يعمل.

بعبارة أخرى، يمكننا صياغة PECTT على أنّها تقرّر أنّ أيّ دالة يمكن حسابها بجهاز يشغل حجماً VV من الفضاء ويحتاج زمناً tt لإتمام الحساب، لا بدّ أن تكون قابلة للحساب بدائرة منطقية عدد بواباتها p(V,t)p(V,t) متعدّدٌ للحدود في VV وtt.

أمّا الصيغة الدقيقة للدالة p(V,t)p(V,t) فلا يوجد اتفاقٌ عام عليها، لكنّه يُقبل عموماً أنّه إذا كانت f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} دالةً صعبة أُسّياً (exponentially hard)، بمعنى أنّها لا تملك برنامج NAND-CIRC من أقلّ من 2n/22^{n/2} سطراً مثلاً، فإنّ إظهار جهازٍ فيزيائي يستطيع حساب ff في العالم الحقيقي عند أطوال مدخلات متوسطة (مثل n=500n=500) سيكون انتهاكاً لـ PECTT.

ملاحظة — ملاحظة متقدّمة: جعل PECTT ملموسة (متقدّمة، اختياري)

يمكننا أن نحاول صياغةً أدقّ لـ PECTT على النحو التالي. لنفترض أنّ ZZ نظامٌ فيزيائي يقبل nn منبّهات (stimuli) ثنائية، وذا مخرجاً ثنائياً، ويمكن احتواءه في كرةٍ حجمها VV. نقول إنّ النظام ZZ يحسب الدالة f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} خلال tt ثانية إذا، كلّما ضبطنا المنبهات على قيمة x∈{0,1}nx\in \{0,1\}^n، وقسنا المخرج بعد tt ثانية، حصلنا على f(x)f(x).

عندئذٍ يمكننا صياغة PECTT على أنّها تقرّر أنّه إذا وُجد نظامٌ ZZ من هذا النوع يحسب FF خلال tt ثانية، فإنّ هناك برنامج NAND-CIRC يحسب FF ولا يتجاوز عدد أسطره α(Vt)2\alpha(Vt)^2، حيث α\alpha ثابتُ تطبيع. (ويمكننا أيضاً أن نأخذ في الحسبان صوراً بديلة نستعمل فيها [مساحة السطح] (https://goo.gl/ALgbVS) بدل الحجم، أو نأخذ (Vt)(Vt) أُسّاً غير 22. لكنّه لا يوجد أيّ من هذه الاختيارات فرقاً نوعياً في النقاش التالي.) وبشكلٍ خاص، لنفترض أنّ f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} دالة تتطلّب 2n/(100n)>20.8n2^n/(100n)>2^{0.8n} سطراً لأيّ برنامج NAND-CIRC (وهذه دالة موجودة بحكم مبرهنة — حدٌّ أدنى من عدِّ الدوال). عندئذٍ ستنفي PECTT أنّ حجم نظامٍ يحسب FF أو زمنه لا بدّ أن يكون 20.2n/α2^{0.2 n}/\sqrt{\alpha} على الأقلّ. وبما أنّ هذه الكمية تنمو أُسّياً في nn، فإنّه من السهل ضبط المعاملات بحيث لا يستطيع حتى لقيم nn المتوسطة الكبيرة أن يتّسع نظامٌ من هذا النوع داخل كوننا.

ولجعل PECTT ملموسةً تماماً، نحتاج إلى أن نقرّر وحدات قياس الزمن والحجم، وثابت التطبيع α\alpha. وإحدى الخيارات المتحفّظة هي أن نفترض أنّه يمكن ضغط الحساب حتى الحدود الفيزيائية المطلقة (وهي حدودٌ تتجاوز التقنية الحالية بمراتبٍ من الرتبة). وهذا يقابل ضبط α=1\alpha=1 واستعمال [وحدات بلانك] (https://goo.gl/gkpmBF) (Planck units) للحجم والزمن. فيُعدّ طول بلانك (Planck length) ℓP\ell_P (الذي هو، بمعنى تقريبي، أقصر مسافة يمكن قياسها منطقياً) نحو 2−1202^{-120} متراً. ويُعدّ زمن بلانك (Planck time) tPt_P (الذي هو الزمن الذي يستغرقه الضوء لقطع طولٍ بلانك واحد) نحو 2−1502^{-150} ثانية. وفي الإعداد أعلاه، إذا كانت دالة FF تأخذ مدخلاً بحجم 1KB مثلاً (أي نحو 10410^4 بت، وهو ما يمكن أن يرمّز صورةً نقطيةً بمقاس 100100 في 100100)، ويتطلّب 20.8n=20.8⋅1042^{0.8 n}= 2^{0.8 \cdot 10^4} سطر NAND على الأقلّ لحسابه، فإنّ أيّ نظامٍ فيزيائي يحسبه سيتطلّب إمّا حجماً قدره 20.2⋅1042^{0.2\cdot 10^4} من طول بلانك مكعّباً، وهو أكثر من 215002^{1500} متر مكعّباً، أو سيستغرق 20.2⋅1042^{0.2 \cdot 10^4} من وحدات زمن بلانك على الأقلّ، وهو أكبر من 215002^{1500} ثانية. ولكي ندرك حجم هذا العدد، لاحظ أنّ عمر الكون لا يتجاوز نحو 2602^{60} ثانية، ونصف قطره المرصود لا يتجاوز نحو 2902^{90} متراً. ويشير النقاش أعلاه إلى أنّه من الممكن إبطال PECTT تجريبياً (empirically falsify) بتقديم نظامٍ أصغر من حجم الكون يحسب دالةً من هذا النوع.

وثمّة بالطبع عدّة عقبات في وجه هذا الإبطال، إحداها أنّنا لا نستطيع في الواقع اختبار النظام على كلّ المدخلات الممكنة. لكنّ بيّّن أنّ بإمكاننا التخلّص من هذه المسألة باستعمال مفاهيم مثل البراهين التفاعلية (interactive proofs) و_فحص البرامج_ (program checking) التي قد نصادفها لاحقاً في هذا الكتاب. وهناك مسألة أخرى،ربّما أوضح، وهي أنّه بينما نعرف وجود دوالٍ صعبة كثيرة، فإنّ في الوقت الحالي لا يوجد دالةٌ صريحة واحدة F:{0,1}n→{0,1}F:\{0,1\}^n \rightarrow \{0,1\} يمكننا أن نبرهن لها حداً أدنى ω(n)\omega(n) (فلا يقل عن Ω(2n/n)\Omega(2^n/n)) على عدد الأسطر التي يحتاجها برنامج NAND-CIRC لحسابها.

محاولات إبطال PECTT

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

  • ترتيبُ المعكرونة: إحدى أوائل الحدود الدنيا التي يصطدم بها طلاب علوم الحاسوب هي أنّ ترتيب nn عدداً يتطلّب إجراء Ω(nlog⁡n)\Omega(n \log n) مقارنة. و"ترتيب المعكرونة" (spaghetti sort) هو وصفٌ لحاسوبٍ "ميكانيكي" مقترح يقوم بذلك على نحوٍ أسرع. والفكرة أنّه لترتيب nn عدداً x1,…,xnx_1,\ldots,x_n يمكننا قصّ nn خيطاً من المعكرونة إلى أطوال x1,…,xnx_1,\ldots,x_n، ثمّ إن أمسكناها معاً في أيدينا وأنزلناها إلى سطحٍ مستوٍ، فإنّها ستخرج مرتّبة. وهناك أسبابٌ عديدة لكيل هذا ليس تحدياً حقيقياً لفرضية PECTT، ولن أحرم القارئ من متعة اكتشافها بنفسه.

  • فقاعات الصابون: إحدى الدوال F:{0,1}n→{0,1}F:\{0,1\}^n \rightarrow \{0,1\} التي يُفتَرَض أنّها تتطلّب عدداً كبيراً من أسطر NAND لحلّها هي مسألة شجرة شتاينر الإقليدية (Euclidean Steiner Tree). وهذه هي المسألة التي يُعطى فيها mm نقطة في المستوى (x1,y1),…,(xm,ym)(x_1,y_1),\ldots,(x_m,y_m) (ولنفرض بإحداثيات صحيحة تمتدّ من 11 إلى mm، وعليه يمكن تمثيل القائمة كسلسلة من الحجم n=O(mlog⁡m)n=O(m \log m)) مع عددٍ ما KK. والهدف هو تحديد ما إذا كان من الممكن وصل كلّ النقط بمقطعات مستقيمة يكون مجموع طولها KK على الأكثر. ويفترض أنّ هذه الدالة صعبة لأنّها كاملة في صنف NP (NP complete) — وهو مفهوم سنصادفه لاحقاً في هذه المساق — بل إنّ من المعقول أن نفترض أنّه مع نموّ mm، ينمو عدد أسطر NAND اللازمة لحساب هذه الدالة أُسّياً في mm، ما يعني أنّ PECTT تتنبّأ بأنّه إذا كان mm كبيراً بما يكفي (مثل بضع مئات مثلاً) لن يستطيع أيّ جهاز فيزيائي حساب FF. ومع ذلك، ادّعى بعض الناس أنّ هناك في الواقع جهازاً فيزيائياً بسيطاً جداً يستطيع حلّ هذه المسألة، وأنّه يمكن بناؤه باستعمال بعض أوتاد الخشب والصابون. والفكرة أنّنا إذا أخذنا لوحين زجاجيين ووضعنا بينهما mm وتداً خشبياً في المواضع (x1,y1),…,(xm,ym)(x_1,y_1),\ldots,(x_m,y_m)، فإنّ فقاعات ستتشكّل تمسّ حوافّها تلك الأوتاد على نحوٍ يُصغّر الطاقة الكلية، وهو ما يبيّن أنّه دالةً في مجموع أطوال المقاطع المستقيمة. والمشكلة في هذا الجهاز أنّ الطبيعة، تماماً كالبشر، كثيراً ما تعلق في "المتفارعات المحلية" (local optima). أي أنّ التهيئة الناتجة لن تكون هي التهيئة التي تحقّق الحدّ الأدنى المطلق للطاقة الكلية، بل ستكون تهيئةً لا يمكن تحسينها بتغييراتٍ محلية. وقد أجرى آرونسون تجاربً فعلية (انظر الشكل التوضيحي)، فرأى أنّه بينما ينجح هذا الجهاز غالباً مع ثلاثة أو أربعة أوتاد، إلا أنّه يبدأ بإعطاء نتائج دون الأمثلية ما إن يزيد عدد الأوتاد عن ذلك.

يُجرّب سكوت آرونسون مرشّحًا لحساب أشجار شتاينر باستعمال فقاعات الصابون.

  • حوسبة الـ DNA. اقترح الناس استعمال خصائص الـ DNA لحلّ مسائل حوسبية صعبة. والمزايا الرئيسية للـ DNA هي القدرة، من حيث المبدأ، على ترميز كثيرٍ من المعلومات في مساحةٍ فيزيائية صغيرة نسبياً، وكذلك إجراء الحساب على هذه المعلومات بطريقة شديدة التوازي. ووقت كتابة هذا النص، كان قد أُبْرِزَت إمكانية استعمال الـ DNA لتخزين نحو 101610^{16} بتّاً من المعلومات في منطقة نصف قطرها نحو مليمتر، مقابل نحو 101010^{10} بتّاً بأفضل تقنية أقراصٍ صلبة معروفة. وهذا لا يضع تحدّياً حقيقياً أمام PECTT، لكنّه يشير إلى ضرورة التحفّظ في اختيار الثابت وعدم افتراض أنّ تقنيات الأقراص الصلبة + السيليكون الحالية هي الأفضل الممكن على الإطلاق.[7]

  • الحواسيب المستمرة/الحقيقية. يُوصف العالم الفيزيائي غالباً باستعمال كمّياتٍ مستمرة مثل الزمن والفضاء، واقترح الناس أنّ الأجهزة التناظرية (analog) قد تكون لها وصولٌ مباشر إلى الحساب بالكمّيات ذات القيمة الحقيقية، وأنّها ستكون بطبيعتها أقوى من النماذج المتفرّقة مثل آلات NAND. أمّا ما إذا كان العالم الفيزيائي "الحقيقي" مستمراً أم متفرّقاً فهو سؤالٌ مفتوح. وفعلاً، لا نعرف حتى كيف نصوغ هذا السؤال بدقّة، فضلاً عن الإجابة عليه. لكنّه، بغضّ النظر عن الإجابة، يبدو واضحاً أنّ الجهد المبذول لقياس كمّيةٍ مستمرة يزداد مع مستوى الدقة المطلوب، وعليه لا يوجد "غداءٌ مجاني" ولا طريقة لتجاوز PECTT باستعمال مثل هذه الآلات (انظر أيضاً هذه الورقة). ويرتبط بذلك مقترحات تُعرف بـ"الحساب الفائق" (hypercomputing) أو "حواسيب زينو" (Zeno's computers)، والتي تحاول استغلال استمرارية الزمن بأن تنفّذ العملية الأولى في ثانية، والثانية في نصف ثانية، والثالثة في ربع ثانية وهكذا.. وتفشل هذه لأسبابٍ مشابهة لتلك التي تضمن أنّ أخيل سيلحق بالسلحفاة في النهاية رغم إشكال زينو الأصلي.

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

  • البشر. نظام حوسبةٍ آخر اقترح بوصفه مثالاً مضاداً لـ PECTT هو حاسوبٌ يزن 3 أرطال ونصف قطره نحو 0.1m، وهو الدماغ البشري. يستطيع البشر التنقّل والحديث والإحساس وفعل أمورٍ أخرى لا تُنجَز عادةً برامج NAND-CIRC، لكنّهم هل يستطيعون حساب دوال جزئية لا تستطيعها برامج NAND-CIRC؟ وهناك بالطبع مهامّ حوسبية يؤديها حالياً البشر أفضل من الحواسيب (مثلاً لعب بعض ألعاب الفيديو في الوقت الحالي)، لكنّ، بناءً على فهمنا الحالي للدماغ، لا يملك البشر (ولا الحيوانات الأخرى) أيّ ميزة حوسبية فطرية على الحواسيب. يحتوي الدماغ على نحو 101110^{11} خلية عصبية (neuron)، كلٌّ منها يعمل بسرعة نحو 10001000 عملية في الثانية. ومن ثمّ فإنّ التقريب الأوّل الخام هو أنّ دائرةً منطقية من نحو 101410^{14} بوابة تستطيع محاكاة ثانيةٍ واحدة من نشاط الدماغ.[8] ولاحظ أنّ وجود مثل هذه الدائرة (على الأرجح) لا يعني أنّ من السهل العثور عليها. بعد كلّ شيء، استغرق تطوّرُ هذه الدائرة مليارات السنين. وتركّز كثير من البحوث الحديثة في الذكاء الاصطناعي على إيجاد برامج تكرّر بعض قدرات الدماغ، ويحتاج اكتشافها إلى جهدٍ حوسبي هائل، غير أنّ هذه البرامج غالباً ما يتبيّن أصغر بكثير من التقديرات المتحفّظة أعلاه. فمثلاً، وقت كتابة هذا النص، تحتوي الشبكة العصبية للترجمة الآلية من Google على نحو 10410^4 عقدة (ويمكن محاكاتها ببرنامج NAND-CIRC من حجمٍ مماثل). ومنذ قديم الزمان، ادّعى الفلاسفةُ والكهنةُ وكثيرون غيرهم أنّ في البشر شيئاً لا يمكن لآلاتٍ ميكانيكية مثل الحواسيب استيعابه؛ وسواء أكان الأمر كذلك أم لا، فإنّ الدليل ضعيف على أنّ البشر يستطيعون أداء مهامّ حوسبية يستحيل على الحواسيب من تعقيدٍ مماثل إنجازها بطبيعتها.[9]

  • الحوسبة الكمّية. يأتي أقوى هجوم على أطروحة كيرش-تورينغ الفيزيائية الموسّعة من فكرة الحوسبة الكمّية (quantum computing). وقد انطلقت الفكرة من الملاحظة أنّ الأنظمة ذات التأثيرات الكمّية القوية يصعب جداً محاكاتها على حاسوب. وبقلب هذه الملاحظة رأساً على عقب، اقترح الناس استعمال مثل هذه الأنظمة لإجراء حسابات لا نعرف كيف نجريها بغير ذلك. ووقت كتابة هذا النص، لم تبنَ بعد حواسيب كمّية قابلة للتوسّع، لكنّها إمكانيةٌ مذهلة، ولا تبدو مناقضةً لأيّ قانونٍ من قوانين الطبيعة المعروف. وسنتناول الحوسبة الكمّية بمزيدٍ من التفصيل بكثير في الحساب الكمّي. وينطوي نمذجة الحوسبة الكمّية على توسيع نموذج الدوائر المنطقية إلى دوائر كمّية (Quantum circuits) ذات بوابةٍ واحدةٍ إضافية (خاصة جداً). غير أنّ الخلاصة الرئيسة هي أنّه بينما تُشير الحوسبة الكمّية إلى حاجتنا إلى تعديل PECTT، فإنّها لا تتطلّب مراجعةً كاملة لنظرةنا إلى العالم. وفعلاً، يبقى كلّ محتوى هذا الكتاب تقريباً كما هو سواء أكان نموذج الحوسبة الأساسيّ دوائر منطقية أم دوائر كمّية.

ملاحظة — أطروحة كيرش-تورينغ الفيزيائية الموسّعة والتعمية

ولو كانت الصياغة الدقيقة لـ PECTT نفسها، فضلاً عن فهم صحّتها، ما تزال موضوع بحثٍ نشط، فإنّ بعض صورها مفترضةٌ ضمنياً في الممارسة بالفعل. تعتمد الحكوماتُ والشركاتُ والأفرادُ حالياً على التعمية (cryptography) لحماية بعض أثمن أصولهم، بما فيها أسرار الدولة، والتحكّم في أنظمة الأسلحة والبنية التحتية الحرجة، وتأمين التجارة، وحماية سرّية المعلومات الشخصية. وفي التعمية التطبيقية، يُصادف غالباً عباراتٌ مثل "النظام المُعمّى XX يوفّر 128 بتّاً من الأمان". وما تعنيه هذه العبارة حقاً هو أنّ (أ) يُفتَرَض أنّه لا توجد دائرة منطقية (أو، بما يعادلها، برنامج NAND-CIRC) من حجم أصغر بكثير من 21282^{128} يستطيع كسر XX، و (ب) نفترض أنّه لا توجد آلية فيزيائية أخرى تستطيع أداء أفضل، ومن ثمّ فإنّ كسر XX سيتطلّب نحو 21282^{128} من "الموارد". ونقول "يُفتَرَض" لا "مُثبَت" لأنّه، بينما يمكننا صياغة عبارة أنّ كسر النظام لا يمكن إنجازه بدائرة من ss بوابة بوصفها حدساً رياضياً دقيقاً، فإنّنا في الوقت الحالي عاجزون عن إثبات عبارةٍ من هذا النوع لأيّ نظام تعميةٍ غير تافه. وهذا مرتبطٌ بسؤال P\mathbf{P} مقابل NP\mathbf{NP} الذي سنتناوله في الفصول القادمة. وسنستكشف التعمية في التشفير.

خلاصة

  • يمكننا أن نفكّر في البرامج على أنّها تصف عمليةً (process)، وكذلك ببساطة قائمةَ رموزٍ يمكن اعتبارها بياناتٍ (data) يمكن إطعامها كمدخلات لبرامج أخرى.
  • يمكننا كتابة برنامج NAND-CIRC يقيّم برامج NAND-CIRC اعتباطية (أو بما يعادلها دائرةً تقيّم دوائر أخرى). وعلاوةً على ذلك، فإنّ فاقد الكفاءة في ذلك ليس كبيراً إلى حدٍّ ما.
  • يمكننا حتى كتابة برنامج NAND-CIRC يقيّم برامجٍ بلغات برمجة أخرى مثل Python وC وLisp وJava وGo إلخ.
  • بقفزة إيمان، يمكننا أن نفترض أنّ عدد البوابات في أصغر دائرة تحسب دالة ff يلتقط نحو Quantity من الموارد الفيزيائية اللازمة لحساب ff. وتُعرف هذه العبارة بـ_أطروحة كيرش-تورينغ الفيزيائية الموسّعة (PECTT)_.
  • تلتقط الدوائر المنطقية (أو بما يعادلها برامج AON-CIRC أو NAND-CIRC) مجموعةً مدهشة من نماذج الحوسبة المتنوّعة. وأقوى تحدٍّ معروف حالياً لـ PECTT يأتي من إمكانية استعمال التأثيرات الميكانيكية الكمّية لتسريع الحساب، وهو نموذجٌ يُعرف بـ_الحواسيب الكمّية_ (quantum computers).

/images/introtcs/lec_04_code_and_data-6.webp

خلاصة الجزء الأول: الحوسبة المنتهية

يُختم هذا الفصل الجزء الأول من الكتاب الذي يتناول الحوسبة المنتهية (finite computation) (أي حساب الدوال التي تُسقِط عدداً ثابتاً من المدخلات المنطقية إلى عددٍ ثابتٍ من المخرجات المنطقية). والخلاصات الرئيسة من تعريف الحوسبة والسكر النحوي، وحساب كل دالة والشيفرة بيانات والبيانات شيفرة هي كما يلي (انظر أيضاً الشكل التوضيحي):

  • يمكننا تعريف رسميّ فكرة أنّ الدالة f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m قابلة للحساب باستعمال ss عملية أساسية. ولا يهمّ كثيراً ما إذا كانت هذه العمليات هي AND/OR/NOT أو NAND أو أساساً كونيّاً آخر. يمكننا وصف حسابٍ من هذا النوع إمّا باستعمال دائرة (circuit) وإمّا باستعمال برنامج سطريّ (straight-line program).

  • نُعرّف SIZEn,m(s)SIZE_{n,m}(s) على أنّها مجموعة الدوال التي يمكن حسابها بدوائر NAND من ss بوابة على الأكثر. وهذه المجموعة تساوي مجموعة الدوال القابلة للحساب ببرنامج NAND-CIRC من ss سطراً على الأكثر، بحدٍّ ثابت في ss (لن نعره بالينا)؛ وهي أيضاً نفسها مجموعة الدوال القابلة للحساب بدائرة منطقية من ss بوابة AND/OR/NOT على الأكثر. والصنف SIZEn,m(s)SIZE_{n,m}(s) مجموعة من الدوال لا من البرامج/الدوائر.

  • كلُّ دالة f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m يمكن حسابها بدائرة من على الأكثر O(m⋅2n/n)O(m \cdot 2^n / n) بوابة. و_بعضُ_ الدوال تتطلّب على الأقلّ Ω(m⋅2n/n)\Omega(m \cdot 2^n /n) بوابة. ونُعرّف SIZEn,m(s)SIZE_{n,m}(s) على أنّها مجموعة الدوال من {0,1}n\{0,1\}^n إلى {0,1}m\{0,1\}^m التي يمكن حسابها باستعمال ss بوابة على الأكثر.

  • يمكننا وصف دائرة/برنامج PP كسلسلة. ولكل ss، هناك دائرة/برنامج كونيّ (universal) UsU_s يستطيع تقييم برامج من الطول ss معطى وصفَها كسلاسل. ويمكننا استعمال هذا التمثيل أيضاً لعدِّ عدد الدوائر التي عدد بواباتها ss على الأكثر، ومن ثمّ إثبات أنّ بعض الدوال لا يمكن حسابها بدوائر أصغر حجماً من الأسّي.

  • إذا كانت هناك دائرة من ss بوابة تحسب دالة ff، فإنّنا نستطيع بناء جهاز فيزيائي لحساب ff باستعمال ss مكوّناً أساسياً (مثل الترانزستورات). و"أطروحة كيرش-تورينغ الفيزيائية الموسّعة" تقرّر أنّ الاتجاه المعاكس صحيح أيضاً: إذا كانت ff دالة يتطلّب كلّ دائرةٍ فيها ss بوابة على الأقلّ، فإنّ كلّ جهاز فيزيائي لحساب ff سيتطلّب نحو ss من "الموارد الفيزيائية". وأبرز تحدٍّ لـ PECTT هو الحوسبة الكمّية، وسنتناولها في الحساب الكمّي.

معاينة سريعة: في الجزء التالي سنتناول كيفية نمذجة المهامّ الحوسبية على مدخلاتٍ غير مقيّدة (unbounded)، حيث تُصنَّف بدوال F:{0,1}∗→{0,1}∗F:\{0,1\}^* \rightarrow \{0,1\}^* (أو F:{0,1}∗→{0,1}F:\{0,1\}^* \rightarrow \{0,1\}) تستطيع أن تأخذ عدداً غير مقيَّد من المدخلات المنطقية.

التمارين

تمرين

أيٌّ من العبارات التالية خاطئة؟

a. يوجد برنامج NAND-CIRC من O(s3)O(s^3) سطراً، يُعطى كمدخل البرنامج PP من ss سطراً في تمثيل قائمة القوالب، ويحسب مخرجات PP حين تكون كلّ مدخلاته مساوية لـ11.

b. يوجد برنامج NAND-CIRC من O(s3)O(s^3) سطراً، يُعطى كمدخل البرنامج PP من ss محرفاً مُرمَّزاً كسلسلة من 7s7s بت باستخدام ترميز ASCII، ويحسب مخرجات PP حين تكون كلّ مدخلاته مساوية لـ11.

c. يوجد برنامج NAND-CIRC من O(s)O(\sqrt{s}) سطراً، يُعطى كمدخل البرنامج PP من ss سطراً في تمثيل قائمة القوالب، ويحسب مخرجات PP حين تكون كلّ مدخلاته مساوية لـ11.

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

لكل k∈Nk \in \N، برهن أنّ هناك برنامج NAND-CIRC من O(k)O(k) سطراً يحسب الدالة EQUALSk:{0,1}2k→{0,1}EQUALS_k:\{0,1\}^{2k} \rightarrow \{0,1\} حيث EQUALS(x,x′)=1EQUALS(x,x')=1 إذا وفقط إذا كان x=x′x=x'.

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

لكل k∈Nk \in \N وx′∈{0,1}kx' \in \{0,1\}^k، برهن أنّ هناك برنامج NAND-CIRC من O(k)O(k) سطراً يحسب الدالة EQUALSx′:{0,1}k→{0,1}EQUALS_{x'} : \{0,1\}^k \rightarrow \{0,1\} التي على المدخل x∈{0,1}kx\in \{0,1\}^k تُخرج 11 إذا وفقط إذا كان x=x′x=x'.

تمرين — الحدّ الأدنى بالعدّ للدوال متعدّدة البتات

برهن أنّه يوجد عدد δ>0\delta>0 بحيث إنّ لكل nn كبيرٍ بما يكفي ولكل mm، توجد دالة f:{0,1}n→{0,1}mf:\{0,1\}^n \rightarrow \{0,1\}^m تتطلّب δm⋅2n/n\delta m \cdot 2^n / n بوابة NAND على الأقلّ لحسابها. انظر الحاشية للتلميح.[10]

تمرين — مبرهنة تدرّج الحجم للدوال متعدّدة البتات

برهن أنّه يوجد عدد CC بحيث إنّ لكل n,mn,m وn+m<s<m⋅2n/(Cn)n+m < s < m\cdot 2^n / (Cn)، توجد دالة f∈SIZEn,m(C⋅s)∖SIZEn,m(s)f \in SIZE_{n,m}(C\cdot s) \setminus SIZE_{n,m}(s). انظر الحاشية للتلميح.[11]

تمرين — التمثيل الأكفأ للدوائر وحدٌّ أعلى عدٍّّ أدقّ

استعمل أفكار ملاحظة — تمثيل أكفأ (متقدّم، اختياري) لإظهار أنّه لكل ϵ>0\epsilon>0 ولكل s,n,ms,n,m كبيرين بما يكفي، ∣SIZEn,m(s)∣<2(2+ϵ)slog⁡s+nlog⁡n+mlog⁡s  .|SIZE_{n,m}(s)| < 2^{(2+\epsilon)s \log s + n\log n + m\log s}\;. استنتج أنّ الثابت الضمني في مبرهنة — عدِّ البرامج يمكن جعله يقترب من 55 إلى حدٍّ لا نهائي. انظر الحاشية للتلميح.[12]

تمرين — حدٌّ أدنى بالعدّ أدقّ

برهن أنّه لكل δ<1/2\delta< 1/2، إذا كان nn كبيراً بما يكفي، فإنّ هناك دالة f:{0,1}n→{0,1}f:\{0,1\}^n \rightarrow \{0,1\} بحيث f∉SIZEn,1(δ2nn)f \not\in SIZE_{n,1}\left( \tfrac{\delta 2^n}{n} \right). انظر الحاشية للتلميح.[13]

تمرين — الدوال العشوائية صعبة

لنفترض n>1000n>1000 وأنّنا اخترنا دالة F:{0,1}n→{0,1}F:\{0,1\}^n \rightarrow \{0,1\} عشوائياً، باختيار قيمة F(x)F(x) لكل x∈{0,1}nx\in \{0,1\}^n لتكون نتيجة رمي عملةٍ مستقيمةٍ مستقلّة. برهن أنّ احتمال وجود برنامج من 2n/(1000n)2^n/(1000n) سطراً يحسب FF هو 2−1002^{-100} على الأكثر.[14]

تمرين

الآتي عبارة عن قالب يمثّل برنامج NAND: (3,1,((3,2,2),(4,1,1),(5,3,4),(6,2,1),(7,6,6),(8,0,0),(9,7,8),(10,5,0),(11,9,10)))(3, 1, ((3, 2, 2), (4, 1, 1), (5, 3, 4), (6, 2, 1), (7, 6, 6), (8, 0, 0), (9, 7, 8), (10, 5, 0), (11, 9, 10))).

  1. اكتب جدولاً بالقيم الثماني P(000)P(000) وP(001)P(001) وP(010)P(010) وP(011)P(011) وP(100)P(100) وP(101)P(101) وP(110)P(110) وP(111)P(111) بهذا الترتيب.

  2. صِف ما يفعله البرنامج بالكلمات.

تمرين — EVAL مع XOR

لكل nn كبيرٍ بما يكفي، لتكن En:{0,1}n2→{0,1}E_n:\{0,1\}^{n^2} \rightarrow \{0,1\} الدالة التي تأخذ سلسلةً من الطول n2n^2 ترمّز زوجاً (P,x)(P,x) حيث x∈{0,1}nx\in \{0,1\}^n وPP برنامج NAND من nn مدخلاً ومخرجٍ واحد، ومن n1.1n^{1.1} سطراً على الأكثر، وتُرجع مخرج PP على xx.[15] أي إنّ En(P,x)=P(x)E_n(P,x)=P(x).

برهن أنّه لكل nn كبيرٍ بما يكفي، لا توجد دائرة XOR CC تحسب الدالة EnE_n، حيث تملك دائرة XOR البوابة XORXOR إضافةً إلى الثابتين 00 و11 (انظر تمرين — XOR ليست شاملة). أي برهن أنّه يوجد ثابث n0n_0 بحيث إنّ لكل n>n0n>n_0 ولكل دائرة XOR CC من n2n^2 مدخلاً ومخرجٍ واحد، يوجد زوج (P,x)(P,x) بحيث C(P,x)≠En(P,x)C(P,x) \neq E_n(P,x).

تمرين — تعلّم الدوائر (تحدٍّ، اختياري، يفترض خلفيةً أكبر)

(يفترض هذا التمرين خلفيةً في نظرية الاحتمالات و/أو تعلّم الآلة قد لا تكون لديك في هذه المرحلة. لا تتردّد في العودة إليه لاحقاً، وبخاصةٍ بعد خاصّاً بعد أن مررت على نظرية الاحتمالات 101.) في هذا التمرين سنستعمل حدّنا على عدد الدوائر من الحجم ss لنُظهر أنّه (إذا أهملنا كلفة الحساب) كلّ دائرةٍ من هذا النوع يمكن تعلّمها (learned) من عددٍ ليس كبيراً من عينات التدريب. وبشكلٍ تحديد، إذا وجدنا دائرةً من الحجم ss تُصنِّف مجموعة تدريبٍ من O(slog⁡s)O(s \log s) عيّنةٍ من بعض التوزيع DD تصنيفاً صحيحاً، فإنّه مضمونٌ أن تُحسن الأداء على التوزيع DD كلّه. ولأنّ الدوائر المنطقية تُمثّل عملياتٍ فيزيائية كثيرة جداً (وربّما كلّها، إذا صحّت أطروحة كيرش-تورينغ الفيزيائية الموسّعة [المتنازعة في المسألة])، فإنّ هذا يُظهر أنّ كلّ تلك العمليات يمكن تعلّمها أيضاً (مع إهمال كلفة الحساب مرّةً أخرى لإيجاد مُصنِّف يُحسن الأداء على بيانات التدريب).

لنكن DD أيّ توزيع احتمالي على {0,1}n\{0,1\}^n، ولتكن CC دائرة NAND من nn مدخلاً ومخرجٍ واحد ومن الحجم s≥ns \geq n. برهن أنّه يوجد ثابث cc بحيث إنّ الاحتمال 0.9990.999 على الأقلّ، ينطبق ما يلي: إذا كان m=cslog⁡sm = c s \log s واختيرت x0,…,xm−1x_0,\ldots,x_{m-1} باستقلالٍ من DD، فإنّ لكل دائرة C′C' بحيث C′(xi)=C(xi)C'(x_i)=C(x_i) عند كل i∈[m]i \in [m]، يكون Pr⁡x∼D[C′(x)≤C(x)]≤0.99\Pr_{x \sim D}[C'(x) \leq C(x)] \leq 0.99.

بعبارة أخرى، إذا كانت C′C' هي ما يُعرف بـ"مُقلِّل المخاطرة التجريبي" (empirical risk minimizer) التي توافق CC على كلّ أمثلة التدريب x0,…,xn−1x_0,\ldots,x_{n-1}، فإنّها أيضاً ستوافق CC باحتمالٍ كبير على العينات المسحوبة من التوزيع DD (أي أنّها "تعمّم"، باستعمال لغة تعلّم الآلة). انظر الحاشية للتلميح.[16]

ملاحظات مرجعية

الدالة EVALEVAL تُعرف عادةً باسم دائرةٍ كونية (universal circuit). وليس التنفيذ الذي نصفه هو الأكفأ المعروف. وقد أظهر Valiant [@Valiant1976] أوّلاً دائرةً كونية من الحجم O(nlog⁡n)O(n \log n) حيث nn هو حجم المدخل. وقد لقيت الدوائر الكونية في السنوات الأخيرة دوافع جديدة بفضل تطبيقاتها في التعمية، انظر [@lipmaa2016valiant, @Gunther2017] .

بينما رأينا أنّ "أغلب" الدوال التي تُسقِط nn بتّاً إلى بتّ واحد تتطلّب دوائر ذات حجم أسّي Ω(2n/n)\Omega(2^n/n)، فإنّنا في الواقع لا نعرف أيّ دالةٍ صريحة (explicit) يمكننا أن نبرهن أنّها تتطلّب، مثلاً، حجماً n100n^{100} أو حتى 100n100n. ووقتَ الحاضر، أقوى حدٍّ أدنى من هذا النوع نعرفه هو أنّ هناك دوالاً بدوال nn متغيّراً بسيطةً وصريحة إلى حدٍّ كبير تتطلّب (5−o(1))n(5-o(1))n سطراً على الأقلّ لحسابها، انظر هذه الورقة لـIwama وزملائه وكذلك هذا العمل الأحدث لـKulikov وزملائه. وإثبات الحدود الدنية للنماذج المقيّدة من الدوائر هو مجال بحثٍ مثير للاهتمام للغاية، ويقدّم كتاب Jukna [@Jukna12] (وانظر أيضاً Wegener [@wegener1987complexity]) مدخلاً ومراجعةً ممتازين إلى حدٍّ كبير. وتعلّمتُ برهان مبرهنة تدرّج الحجم (مبرهنة — مبرهنة تدرّج الحجم) من Sasha Golovnev.

منشور مدوّنة Scott Aaronson بعنوان how information is physical نقاشٌ جيّد حول المسائل المتعلقة بالفيزياء كيرش-تورينغ الفيزيائية الموسّعة. وتناقش مسحته لـAaronson عن مسائل NP الكاملة والواقع الفيزيائي [@aaronson2005physicalreality] هذه المسائل أيضاً، لكنّه قد يكون أسهل قراءةً بعد أن نصل إلى صنف NP والاكتمال في NP ومبرهنة كوك-ليفن عن NP\mathbf{NP} و NP\mathbf{NP}-اكتمال.


  1. الثابت الضمني في رمز O(⋅)O(\cdot) أصغر من 1010. أي إنّ لكل ss كبير بما يكفي، ∣SIZEn,m(s)∣<210slog⁡s|SIZE_{n,m}(s)|< 2^{10s\log s}، انظر ملاحظة — تمثيل أكفأ (متقدّم، اختياري). وكما ناقشنا في هذا الكتاب: الترميز والاصطلاحات، فإننا نستعمل الحدّ 1010 لمجرّد أنّه عددٌ مستدير. ↩︎

  2. "فلكياً" هنا هي كلمة متحفّظة: فثَمّ نجوم، بل وجسيمات، أقلّ بكثير من 22102^{2^{10}} في الكون المرصود. ↩︎

  3. الثابت δ\delta هو 0.10.1 على الأقل، وفعلاً يمكن تحسينه ليقترب من 1/21/2 إلى حدٍّ لا نهائي، انظر تمرين — حدٌّ أدنى بالعدّ أدقّ. ↩︎

  4. إن كنت فضولياً بشأن هذه الأسطر القليلة، فطالع مستودعنا على GitHub. ↩︎

  5. لا تميّز Python بين القوائم والمصفوفات، لكنها تتيح وصولاً عشوائياً بعُرف زمنٍ ثابت إلى عناصر مفهرسة في كليهما. ويمكن القول إنّنا لو سمحنا ببرامج ذات طولٍ غير مقيَّد فعلاً (مثلاً أكبر من 2642^{64}) لف كان الثمن ثابتاً، بل لوغاريتمياً في طول المصفوفة/القائمة، لكنّ الفرق بين O(s)O(s) وO(slog⁡s)O(s \log s) لن يكون مهمّاً في مناقشاتنا. ↩︎

  6. ARM اختصار لعبارة "Advanced RISC Machine"، حيث RISC بدورها اختصار لعبارة "Reduced instruction set computer". ↩︎

  7. كنا متحفّظين إلى حدٍّ كبير في المعاملات المقترحة لـ PECTT، بافتراضنا أنّه يُمكن تخزين ما يصل إلى ℓP−210−6∼1061\ell_P^{-2}10^{-6} \sim 10^{61} بتّاً في منطقة نصف قطرها مليمتر. ↩︎

  8. هذا تقريبٌ خام جداً قد يكون خاطئاً بمراتب رتبة في أيّ من الاتجاهين. فمن جهةٍ، هناك بنى أخرى في الدماغ عدا الخلايا العصبية قد يحتاج المرء إلى محاكاتها، ممّا يستلزم تكلفةً أعلى. ومن جهةٍ أخرى، ليس واضحاً البتّة أنّنا نحتاج إلى استنساخ الدماغ بالكامل كي نحقّق المهامّ الحوسبية نفسها التي يحقّقها. ↩︎

  9. هناك بعض العلماء المعروفين الذين دعوا أنّ للبشر مزايا حوسبية فطرية على الحواسيب. انظر أيضاً هذه. ↩︎

  10. كم عدد الدوال من {0,1}n\{0,1\}^n إلى {0,1}m\{0,1\}^m الموجودة؟ ولاحظ أنّ تعريفنا للدوائر يتطلّب أن يقابل كلّ مخرجٍ بوابةً فريدة، وإنّ هذا القيد يُحدث فرقاً إضافياً لا يتجاوز O(m)O(m) في عدد البوابات. ↩︎

  11. اتبع برهان مبرهنة — مبرهنة تدرّج الحجم، مع استبدال استعمال حجّة العدّ بـتمرين — الحدّ الأدنى بالعدّ للدوال متعدّدة البتات. ↩︎

  12. باستعمال تمثيل قائمة التجاور، يمكن تمثيل رسمٍ بياني فيه nn رأساً ذات درجة داخلية صفرية وss رأساً ذات درجة داخلية اثنتين بنحو 2slog⁡(s+n)≤2s(log⁡s+O(1))2s\log(s+n) \leq 2s (\log s + O(1)) بتّاً. ويمكن تحديد تسمية رؤوس المدخلات nn والمخرجات mm بقائمة من nn تسمية في [n][n] ومن mm تسمية في [m][m]. ↩︎

  13. تلميح: استعمل نتائج تمرين — التمثيل الأكفأ للدوائر وحدٌّ أعلى عدٍّّ أدقّ والحقيقة أنّه في هذا النطاق m=1m=1 وn≪sn\ll s. ↩︎

  14. تلميح: طريقةٌ مكافئة لقول هذا إنّ عليك إثبات أنّ مجموعة الدوال التي يمكن حسابها باستعمال 2n/(1000n)2^n/(1000n) سطراً على الأكثر تحتوي على عدداً أصغر من 2−10022n2^{-100}2^{2^n}. هل ترى لماذا؟ ↩︎

  15. لاحظ أنّه إذا كان nn كبيراً بما يكفي، فإنّ تمثيل مثل هذا الزوج بـn2n^2 بتّاً سهل، إذ يمكننا تمثيل البرنامج باستعمال O(n1.1log⁡n)O(n^{1.1}\log n) بتّاً، ويمكننا دائماً تبطين تمثيلنا ليصل طوله بالضبط إلى n2n^2. ↩︎

  16. تلميح: استعمل حدّنا على عدد البرامج/الدوائر من الحجم ss (مبرهنة — عدِّ البرامج)، وكذلك حدّ Chernoff (مبرهنة — حدّ تشرنوف/هويفدينغ) وحدّ الاتحاد. ↩︎