تحية!
لذلك ، في
الجزء الأول ، قمنا بالفعل بعمل جيد من خلال القيام بالمشتقات والتبسيط والتجميع. لذا ، ماذا يجب أن تفعل لدينا آلة حاسبة بسيطة؟ حسنا ، على الأقل معادلات النموذج
( x - b ) ( t a n ( s i n ( x ) ) 2 - 3 t a n ( s i n ( x ) ) + c ) = 0
بالتأكيد يجب أن تقرر. كما أنه من الجميل رسم هذه الحالة في وقت متأخر ، وسيكون على ما يرام! دعنا نذهب!

تنصلعلى الرغم من أن الكود موجود هنا في C # ، إلا أنه صغير جدًا هنا بحيث لا يؤدي عدم معرفة اللغة أو الكراهية إلى تعقيد القراءة إلى حد كبير.
تجميع التسارع
في الجزء الأخير ، قمنا بتجميع الوظيفة بحيث يمكننا تجميعها مرة واحدة ، ثم تشغيلها عدة مرات في المستقبل.
لذلك ، نقدم الوظيفة
s i n ( x 2 ) + c o s ( x 2 ) + x 2 + s i n ( x 2 )
في نهاية الجزء الأول ، كانت سرعة هذه الوظيفة على النحو التالي:
ماذا؟البديل - طريقة كلاسيكية لحساب قيمة التعبير ، على سبيل المثال
var x = MathS.Var("x"); var expr = x + 3 * x; Console.WriteLine(expr.Substitute(x, 5).Eval()); >>> 20
وظيفتنا المترجمة هي عندما نفعل الشيء نفسه ، ولكن بعد تجميعها
var x = MathS.Var("x"); var expr = x + 3 * x; var func = expr.Compile(x); Console.WriteLine(func.Substitute(5));
وظيفة مكتوبة مباشرة في التعليمات البرمجية هي عندما نفعل
static Complex MyFunc(Complex x) => x + 3 * x;
كما نرى ، هناك أجزاء مكررة في هذه الوظيفة ، على سبيل المثال ،
× 2 ، وسيكون من الجيد تخزينها مؤقتًا.
للقيام بذلك ، نقدم اثنين من التعليمات الأخرى PULLCACHE و TOCACHE. الأولى ستدفع الرقم في ذاكرة التخزين المؤقت على العنوان الذي نمره إلى المكدس. سيقوم الثاني
بنسخ (
stack.Peek()
) آخر رقم من المكدس إلى ذاكرة التخزين المؤقت ، وكذلك في عنوان محدد.
يبقى أن نضع جدولًا فيه أثناء التجميع سنكتب وظائف للتخزين المؤقت. ما نحن لن مخبأ؟ حسنًا ، أولاً ، ماذا يحدث مرة واحدة. التعليمات الإضافية للوصول إلى ذاكرة التخزين المؤقت ليست جيدة. ثانياً ، العمليات البسيطة جدًا لا تجعل التخزين المؤقت ذا معنى. حسنًا ، على سبيل المثال ، الوصول إلى متغير أو رقم.
عند تفسير قائمة الإرشادات ، سيكون لدينا صفيف تم إنشاؤه مسبقًا للتخزين المؤقت. الآن تبدو التعليمات الخاصة بهذه الوظيفة
PUSHCONST (2, 0) PUSHVAR 0 CALL powf TOCACHE 0 # , , - . CALL sinf TOCACHE 1 # PULLCACHE 0 # , . PULLCACHE 0 CALL cosf PULLCACHE 1 CALL sumf CALL sumf CALL sumf
بعد ذلك نحصل على نتيجة أفضل بوضوح:
في اللفت ، يتم تنفيذ كل من تجميع وتفسير التعليمات
هنا .
مطاط
هذا هو تنسيق معروف لتسجيل الصيغ الرياضية (وإن لم يكن فقط!) ، والتي يتم تقديمها في صور جميلة. إنه أيضًا على المحور ، وجميع الصيغ التي أكتبها مكتوبة فقط بهذا التنسيق.
وجود شجرة التعبير يجعل التقديم في اللاتكس بسيط جدا. كيف نفعل هذا من حيث المنطق؟ لذلك ، لدينا الجزء العلوي من الشجرة. إذا كان الرقم أو متغيرًا ، فكل شيء بسيط. إذا كانت هذه القمة ، على سبيل المثال ، عامل التقسيم ، نود المزيد
fracab من
a/b ، لذلك للقسمة نكتب شيء من هذا القبيل
public static class Div { internal static string Latex(List<Entity> args) => @"\frac{" + args[0].Latexise() + "}{" + args[1].Latexise() + "}"; }
كل شيء بسيط جدا ، كما نرى. المشكلة الوحيدة التي واجهتها أثناء التنفيذ هي أنه من غير الواضح كيفية وضع الأقواس. إذا دفعناهم إلى كل مشغل ، فسوف نحصل على مثل هذا الهراء:
left( left( left( left(x+3 right)+ left(a+b right) right) right)+c right)
ومع ذلك ، فإن الشخص اليقظ على الفور (وليس مثلي) يلاحظ أنه إذا تمت إزالته بالكامل ، فعندئذٍ عند إنشاء تعبير للنموذج
c∗(a+b) ، سوف نطبع
c∗a+b
يتم حلها ببساطة ، ندخل أولويات المشغلين
args[0].Latexise(args[0].Priority < Const.PRIOR_MUL) + "*" + args[1].Latexise(args[1].Priority < Const.PRIOR_MUL);
وفويلا ، أنت جميلة.
Latexirization القيام به
هنا . وكلمة اللاتكس لا وجود لها ، لقد اخترعت ذلك بنفسي ، لا تركلني على ذلك.
حل المعادلات
في الواقع ، من وجهة نظر الرياضيات ، لا يمكنك كتابة خوارزمية تجد جميع الحلول لبعض المعادلات. لذلك ، نريد أن نجد أكبر عدد ممكن من الجذور المختلفة ، ونحقق عدم إمكانية الوصول إلى الهدف النهائي. هناك مكونان: الحل العددي (كل شيء بسيط قدر الإمكان) والتحليلي (ولكن هذا هو التاريخ).
الحل العددي. طريقة نيوتن
انه بسيط للغاية ، معرفة الوظيفة
f(x) سنبحث عن الجذر باستخدام صيغة تكرارية
xn+1=xn− fracf(x)f(x)′
نظرًا لأننا جميعًا نحل في طائرة معقدة ، فيمكننا أن نكتب بشكل أساسي دورة ثنائية الأبعاد تبحث عن حلول ثم تعود إلى حلول فريدة. علاوة على ذلك ، يمكننا الآن إيجاد مشتق الوظيفة تحليليًا ، ثم كلتا الدالتين
f(x) و
f(x)′ ترجمة.
نيوتن يجلس
هنا .
الحل التحليلي
الأفكار الأولى واضحة جدا. لذلك ، التعبير ، جذور المعادلة
a(x)∗b(x) مجموع متساو من الجذور
a(x) و
b(x) ، بالمثل للقسمة:
internal static void Solve(Entity expr, VariableEntity x, EntitySet dst) { if (expr is OperatorEntity) { switch (expr.Name) { case "mul": Solve(expr.Children[0], x, dst); Solve(expr.Children[1], x, dst); return; case "div": Solve(expr.Children[0], x, dst); return; } } ...
بالنسبة للجيب ، سيبدو هذا مختلفًا قليلاً:
case "sinf": Solve(expr.Children[0] + "n" * MathS.pi, x, dst); return;
بعد كل شيء ، نريد أن نجد كل الجذور ، وليس فقط تلك التي هي 0.
بعد التأكد من أن التعبير الحالي ليس منتجًا ، وليس مشغلين ووظائف أخرى مبسطة بسهولة ، نحتاج إلى محاولة العثور على قالب لحل المعادلة.
الفكرة الأولى هي استخدام الأنماط التي قمنا بها لتبسيط التعبير. وفي الواقع ، سوف نحتاج إلى ذلك تقريبًا ، ولكن أولاً نحتاج إلى القيام باستبدال متغير. وبالفعل ، إلى المعادلة
sin(x)2+ sin(x)−0.4=0
لا يوجد قالب ، ولكن للزوج
startcasest2+t−0.4=0t= sin(x) endcases
حتى كما هو موجود.
لذلك ، نجعل دالة من النوع GetMinimumSubtree ، والتي من شأنها أن ترجع البديل المتغير الأكثر كفاءة. ما هو بديل فعال؟ هذا هو هذا البديل الذي نحن
- تعظيم استخدام هذا الاستبدال
- تعظيم عمق الشجرة (بحيث في المعادلة sin( sin(x))2+3 كان لدينا بديل sin( sin(x)) )
- نتأكد من أننا مع هذا الاستبدال قمنا باستبدال جميع الإشارات إلى متغير ، على سبيل المثال ، إذا كانت في المعادلة sin(x)2+ sin(x)+x سنجعل بديل t= sin(x) ثم كل شيء سيصبح سيئا. لذلك ، في هذه المعادلة ، أفضل بديل ل x إنه كذلك x (وهذا هو ، لا يوجد بديل جيد) ، ولكن على سبيل المثال في sin(x)2+ sin(x)+c يمكننا أن نجعل بأمان بديل t= sin(x) .
بعد استبدال المعادلة تبدو أبسط بكثير.
متعدد الحدود
لذا ، فإن أول شيء نقوم به ، نقوم به
expr.Expand()
- افتح كل الأقواس من أجل التخلص من الوحل في النموذج
x(x+2) تتحول إلى
x2+2x . الآن ، بعد الكشف ، نحصل على شيء من هذا القبيل
c+x3+3x2−a∗x3+x
لا الكنسي؟ ثم نجمع أولاً معلومات حول كل نموذج أحادي ، ونطبق "الأطفال الخطيين" (في المقال
الأخير ) ونوسع كل المصطلحات.
ماذا لدينا عن المونوميال؟ المونوميل هو نتاج عوامل ، أحدها متغير ، أو عامل لدرجة متغير و عدد صحيح. لذلك ، سوف نقدم اثنين من المتغيرات ، واحد سوف يكون على درجة ، والآخر معامل. بعد ذلك ، فقط استعرض العوامل ، وفي كل مرة نتأكد من وجودها هناك
x إلى حد ما ، أو بدون درجة على الإطلاق. إذا التقينا byaku - نعود مع فارغة.
byakaإذا التقينا فجأة أي x3.2 . log(x،2) وغيرها من الأشياء التي ذكرها x ، ولكن لا يتناسب مع نمط أحادي - وهذا أمر سيء.
حسنًا ، قمنا بتجميع قاموس يكون فيه المفتاح هو درجة (عدد صحيح) وتكون القيمة معاملًا أحاديًا. هذا هو ما يبدو للمثال السابق:
0 => c 1 => 1 2 => 3 3 => 1 - a
هنا ، على سبيل المثال ، تنفيذ حل المعادلة التربيعية
if (powers[powers.Count - 1] == 2) { var a = GetMonomialByPower(2); var b = GetMonomialByPower(1); var c = GetMonomialByPower(0); var D = MathS.Sqr(b) - 4 * a * c; res.Add((-b - MathS.Sqrt(D)) / (2 * a)); res.Add((-b + MathS.Sqrt(D)) / (2 * a)); return res; }
هذه النقطة لم تكتمل بعد (على سبيل المثال ، تحتاج إلى القيام بذلك بشكل صحيح لمتعدد الحدود مكعب) ، ولكن بشكل عام يحدث
هنا .
عكس الاجتياح
لذلك ، هنا قمنا ببعض استبدال النوع
t= arcsin(x3+a2) ، والآن نريد الحصول على x من هناك. هنا لدينا مجرد نشر تدريجي للوظائف والمشغلين ، مثل
t= arcsin(x3+a2)
sin(t)=x3+a2
sin(t)−a2=x3
( s i n ( t ) - a 2 ) f r a c 1 3 = x
يبدو مقتطف الشفرة مثل هذا:
switch (func.Name) { case "sinf":
رمز هذه الوظائف
هنا .
كل شيء ، الحل النهائي للمعادلات (وداعا!) يبدو شيء من هذا القبيل
- إذا علمنا بوجود وظيفة أو عامل صفر ، فقم بإرسال حل هناك (على سبيل المثال ، إذا a ∗ b = 0 ثم قم بتشغيل Solve (a) و Solve (b))
- العثور على بديل
- حل كما كثير الحدود إذا كان ذلك ممكنا
- بالنسبة لجميع الحلول ، قم بنشر بديل للحصول على الحل النهائي
- إذا ، كما متعدد الحدود ، لم يتم حلها ولدينا متغير واحد فقط ، فإننا حلها بطريقة نيوتن
لذلك ، نحن اليوم:
- تسريع وظيفة المترجمة بسبب ذاكرة التخزين المؤقت
- المقدمة في اللاتكس
- لقد حللنا جزءًا صغيرًا من حالات المعادلات
ربما لن أتحدث عن الحساب العددي لبعض العناصر ، لأنه
بسيط للغاية . لم أتحدث عن التحليل ، حيث تلقيت انتقادات حول هذا الأمر في مقال سابق. كانت الفكرة أن تكتب بنفسك. ومع ذلك ، هل من الضروري التحدث عن كيفية تحليل تعبير من سلسلة؟
المشروع
هنا .
في الجزء التالي ...إذا كان أي شخص آخر مهتمًا ، فسوف نحاول تعقيد حل المعادلات من خلال إضافة درجة مكعب أو رابع ، بالإضافة إلى نظام قابل للطي أكثر ذكاءً. ربما سنختار الأنماط ، لأن أي طالب سيقرر
1− sin(x)2+ cos(x)+0.5
ولكن ليس لدينا خوارزمية. علاوة على ذلك ، قد يكون هناك تكامل غير محدد (البداية). إن زيادة العدد إلى رباعيات أو مصفوفات لا معنى لها حتى الآن ، لكن بطريقة ما سيكون ذلك ممكنًا. إذا كانت لديك فكرة محددة عن الجزء الثالث ، فاكتب في الرسائل أو التعليقات الشخصية.
عن المشروعأولئك الذين نظروا إلى الكود يمكن أن يلاحظوا كيف كان الخام وليس إعادة تشكيله. وبطبيعة الحال ، سأقوم بإعادة الرد ، وبالتالي فإن الفكرة الرئيسية لهذه المقالة هي نقل المعلومات من الناحية النظرية ، وعندها فقط يظهر في الكود. وطلبات السحب هي موضع ترحيب ، على الرغم من أننا ربما لا نتمكن من الوصول إلى wolfram.alpha. المشروع مفتوح.
وإليك بعض الأمثلة على ما فعلناه اليوم
var x = MathS.Var("x"); var y = MathS.Var("y"); var expr = x.Pow(y) + MathS.Sqrt(x + y / 4) * (6 / x); Console.WriteLine(expr.Latexise()); >>> {x}^{y}+\sqrt{x+\frac{y}{4}}*\frac{6}{x}
س ذ +قفصر س + F ص ل ج ص 4 *وصلج 6 س var x = MathS.Var("x"); var expr = MathS.Sin(x) + MathS.Sqrt(x) / (MathS.Sqrt(x) + MathS.Cos(x)) + MathS.Pow(x, 3); var func = expr.Compile(x); Console.WriteLine(func.Substitute(3)); >>> 29.4752368584034
Entity expr = "(sin(x)2 - sin(x) + a)(b - x)((-3) * x + 2 + 3 * x ^ 2 + (x + (-3)) * x ^ 3)"; foreach (var root in expr.Solve("x")) Console.WriteLine(root); >>> arcsin((1 - sqrt(1 + (-4) * a)) / 2) >>> arcsin((1 + sqrt(1 + (-4) * a)) / 2) >>> b >>> 1 >>> 2 >>> i >>> -i
شكرا لاهتمامكم!