مصطفى 36a2

محاولة لمحاكاة اللعبة الفرنسية المشهورة : Le compte est bon

17 ردود في هذا الموضوع

بسم الله الرحمن الرحيم

دلني الأستاذ أحمد الشنقيطي منذ فترة على لعبة الوصول لرقم عن طريق القيام بعمليات حسابية على الأعداد المعطاة

كتمرين برمجي ... لمنع الركود الفكري :lol: جزاه االه خيراً

وشرح اللعبة ..

مثلاً كيف نصل للرقم 15 عن طريق القيام بعمليات حسابية على الأرقام 1 و2و3و4و5 الحل هو 1+2=3 ثم 3+3=6 ثم 6+4=10 ثم 10+5=15

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

و تعميماً للفائدة أضع بين يديكم كما نصحني الأستاذ أحمد الكود ... وقد شرحته بناءً على القاعدة الرائعة والتي تقول :

حسب رأيي المتواضع :

مشروع بدون documation لا أعتبره open source :)

:happy:

وأرجو أن يكون فيه الفائدة ... أترككم مع الكود والشرح والسلام عليكم :


//قبل البدء :أود تذكيرك أخي بأن البرمجة هي كتابة الخوارزمية فإذا أردت أخي الاستفادة من الشرح دون تضييع وقتك فاذهب إلى جسم الدالة التالية وستجده في آخر البرنامج
void NewProblem(int*,int,int);//أهم دالة هي التي تقوم بإدارة حل المسألة ككل وكل مسألة جزئية
int sum(int a,int b){return a+b;}//تابع يعيد مجموع الدخل
int mul(int a,int b){return a*b;}//تابع يعيد جداء الدخل
int sub(int a,int b){if(a>b)return a-b;else return b-a;}//تابع يعيد فرق عددين بالقيمة المطلقة
int div_(int a,int b){if(a>b&&a%b==0)return a/b;else return 0;}//تابع يعيد ناتج قسمة الدخل الاول على الثاني ان كان الناتج عدد صحيح
int C(int a){return a*(a-1)/2;}//C(n,2) number of couples
//هذا التابع يحسب عدد احتمالات الأزواج من أجل ن عدد
int function(int,int,char );//للقيام بعملية على أول وسيطين نمرر العملية على شكل رمز بالوسيط الثالث
void mov(int*,int*,int,int);//دالة نسخ مصوفة مع استثناء احد العناصر
void ShowOperators();//لإظهار العمليات المتبعة بعد الانتهاء
char operation[4]={'+','-','*','/'};//مصفوفة لتعداد العمليات المتوفرة
char *Operators;//مجرد مؤشر سيؤشر لا حقا على مصفوفة تحتفظ بنوع العملية المجراة على الأعداد
int *Oprands;//مؤشر سيؤشر لا حقاً على مصفوفة للاحتفاظ بالأرقام التي أجريت عليها العمليات
int operators_=0;//عدد العمليات المجراة
#include<iostream.h>//تضمين مكتبة الدخل والخرج
#include<windows.h>//تضمين مكتبة التعامل مع ويندوز
//ملاحظة :لمحبي اللينوكس يمكنهم إلغاء المكتبة السابقة وأترك لهم أن يضعوا داخل التوابع التالية ما يكافئ عملها
void pause(){system("pause");}//لإيقاف البرنامج مؤقتا
void exit(){exit(0);}//للخروج قبل نهاية الدالة الرئيسية
void cls(){system("cls");};//لمحو كل ماهو ظاهر على الكونسول
void main()//الدالة الرئيسية
{
cls();// محو الشاشة ... قد تبدو بلا فائدة ولكن ستتضح فائدتها في السطر 74
int nsize;//حجم مصفوفة المدخلات وهي عدد الأعداد
cout<<"How Many Numbers You Want To enter ?\n";//سؤال المستخدم
cin>>nsize;//ادخال عدد الاعداد
int *n=new int[nsize];//انشاء مصفوفة بعدد الارقام تماما
for(int i=0;i<nsize;i++){cout<<"Enter Number("<<i<<") :";cin>>n[i];}//حلقة لادخال العناصر الرقمية كلها
cout<<"Enter The Goal Number : ";//سؤال
int N;//الرقم الهدف
cin>>N;//ادخال الرقم الهدف
Operators=new char[nsize-1];//عدد العمليات الأعظمي هو عدد الأرقام ناقصا 1 لذلك ننشئ مصفوفة العمليات بهذا الحجم
Oprands=new int[(nsize-1)*2];//سنستعمل عددين في كل عملية لذلك ننشئ مصفوفة للاحتفاظ بهذه الأعداد بهذا الحجم
NewProblem(n,nsize,N);//استدعاء دالة حل المسألة ووسطاؤها هم على الترتيب مصفوفة الأعداد ثم حجمها ثم الرقم الهدف
}
int function(int a,int b,char c)
{//هذه الدالة للتعرف على رمز العملية المراد القيام بها ثم القيام بها
if(c=='+')return sum(a,b);
else if(c=='-')return sub(a,b);
else if(c=='*')return mul(a,b);
else if(c=='/'&&b!=0)return div_(a,b);
else return -1;//في حالة الخطأ في الكود... وليس لها داع
}
void mov(int*newn,int*n,int i_,int new_size)
{//عندما نختار رقما من المصفوفة فيجب حذفه من المصفوفة عند اختيار الرقم الثاني حتى لا نختار هذا الرقم مرتين وهذه هي وظيفة هذه الدالة
for(int i=0;i<new_size&&i!=i_;i++)
newn[i]=n[i];
for(;i<new_size;i++)
newn[i]=n[i+1];
}
void SaveOperator(int a,int b,char operation)
{//تقوم بحفظ العملية في مصفوفتي العمليات والمعاملات
Operators[operators_]=operation;//حفظ العملية
Oprands[operators_*2]=a;//حفظ المعامل الاول
Oprands[operators_*2+1]=b;//حفظ المعامل الثاني
}
void ShowOperators()
{
for(int i=1;i<operators_+1;i++)
{
cout<<Oprands[i*2]//عرض المعامل الاول
<<Operators[i]//عرض العملية
<<Oprands[i*2+1]//عرض المعامل الثاني
<<'='//عرض اشارة المساواة
<<function(Oprands[i*2],Oprands[i*2+1],Operators[i])<<endl;////بدل الاحتفاظ في الجواب سنستدعي دالة الحساب ثانية
}//عرض العمليات المخزنة في مصفوفتي العمليات والمعاملات.....لمن أحب تطروير البرنامج يمكنه أن ينشئ بنية معطيات
//للعمليات بدل أن نستخدم مصفوفتين ولا بأس بإضافة الناتج أيضا للبنية كما أنه يجب تغيير حالة العرض عند عملية طرح
//عدد صغير من عدد كبير .. العملية التي يجريها التابع هي الفرق وهو عملية تبديلية أما عند العرض سيظن المستخدم أن
//البرنامج أوجد الناتج معكوس الإشارة لذلك يمكن اضافة شرط لعكس المعاملات في حالة الطرح عندما يكون المعامل الاول اصغر من الثاني
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////Algorithm's Function .............////////////////////////////////
////////////////////////////////////////BY Mostafa 36a2/////////////////////////////////////////////
void NewProblem(int*n,int new_size,int Goal)
{//دالة حل المسالة ككل والمسائل الجزئية ..... هنا الخوارزمية .... هنا لب البرنامج ... وكل الدوال الباقية مجرد مظاهر
for(int w=0;w<new_size;w++)//من اجل جميع أرقام الدخل
if(n[w]==Goal){cout<<n[w]<<"\tIt's In Your Numbers Man!!!\nIs It A Test For Stupidity :)?\nNo Problem Try Again .."<<endl;pause();main();}
//الشرط السابق في حال أدخل المستخدم الرقم الهدف ضمن أرقام الدخل ... وهنا لا داعي للبحث عنه فهو موجود... لاحظ استدعاء الدالة الرئيسية بدل الخروج من البرنامج ... فقد يكون المستخدم أخطأ سهواً
int new_element=0;//العنصر الجديد هو ناتج العملية التي ستجرى على المعاملين
int a,b;//المعاملان الذين ستجرى عليهما العملية
for(int i=0;i<C(new_size);i++)
{//سنكرر بعدد الأزواج الممكن تشكيلها من أرقام الدخل
a=n[i];//نختار أحد الأرقام
int *newn=new int[new_size-1];//ننشئ مصفوفة جديدة أصغر من السابقة بواحد
mov(newn,n,i,new_size-1);//المصفوفة الجديدة هي نفس القديمة بعد استثناء الرقم المختار
//لمن أراد تطوير البرنامج يمكنه تغيير طريقة استثناء الرقم المختار
for(int j=i;j<new_size-1;j++)//الآن لدينا مصفوفة خالية من المعامل الاول سنكرر من اجل جميع عناصرها
{
b=newn[j];//نختار احد هذه العناصر
for(int op=0;op<4;op++)//نكرر من اجل جميع العمليات
{
new_element=function(a,b,operation[op]);//العنصر الجديد سيكون ناتج العملية التي ستجرى على المعاملين
operators_++;//نزيد عدد العمليات المجراة لأننا أجرينا عملية للتو
SaveOperator(a,b,operation[op]);//نحفظ العملية التي قمنا بها
if(new_element==Goal)//هذا يعني اننا وصلنا للرقم المطلوب
{
cout<<"It Takes "<<operators_<<" operators"<<endl;//رسالة تخبر المستخدم بعدد العمليات المجراة
cout<<"I Think It was Speeder Than You Thought Mr.Ahmad ? isn't it!!\n";//هذه رسالة خاصة للأستاذ أحمد تخبره بأن البرنامج سريع جدا حتى لو كانت الخوارزمية غبية!!هههه
//ملاحظة هامة : قد تنزعج أخي أثناء قراءتك للشرح من وجود مثل السطر السابق ولكنني أصررت على عدم حذفه وذلك للتأكيد على أن هذه المظاهر غير مهمة والمهم هو الخوارزمية
ShowOperators();//طبعا سنعرض للمستخدم العمليات التي قمنا بها
if(operators_>7)cout<<"IT Sometimes Look Crazy To Do That Like This :p\n";//رسالة البرءة من الحول والقوة فالخوارزمية قد تؤدي الى سلوك طريق طويل دون الحاجة للتطويل
//وهنا يكمن جوهر التطوير بإضافة بعض الخطوات والشروط في الخوارزمية كي تسلك الطريق القصير حتى لا تلتقي بالذئب
cout<<"With All My Thanks For You MY Teacher \nI am Ready For Any Thing :)\n";//رسالة الشكر للأستاذ أحمد
//goto : ملاحظة هامة
pause();//ايقاف البرنامج مؤقتا حتى يلحق المستخدم يقرا
cout.flush();//لافراغ ذاكرة العرض فأحيانا مع استخدام دالة الايقاف السابقة يمتنع البرنامج عن افراغ ذاكرة العرض
pause();//لا أدري لماذا أضفتها ثانية ولكن لا مانع
exit();//انهاء البرنامج
//ويمكن بدل الانهاء استدعاء الدالة الرئيسة ثانية ولكن يجب تصفير المتحولات وخصوصا عدد العمليات
}
else if(new_element!=0&&new_size!=2)//ان لم نصل للرقم الهدف وكانت المسألة الحالية بها أكثر من رقمين
//سبب الشرط الأخير هو أنه إن لم يكن هناك سوى رقمين وجربنا جميع العمليات فهذا يعني احد امرين اما اننا في مسألة جزئية لا تفيد في الحل
//وإما أن الحل أصلاً غير ممكن وذلك ان كنا في المسألة الرئيسسة
{//انتباه: الآن سننشئ مسألة جديدة
//المسألة الجديدة تعالج الاحتمالات الناتجة عن استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما
newn[j]=new_element;//لاحظ كيف استبدلنا المعامل الثاني بالرقم الناتج ...ولا تنسى أن هذه المصوفة قد حُذف منها المعامل الاول سابقا
NewProblem(newn,new_size-1,Goal);//استدعاء حلال المسائل لمسألتنا الجديدة ... الهدف هو نفسه والحجم أصغر بواحد والأعداد هي نفس أعداد المسألة السابقة بعد استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما
newn[j]=b;//إذا وصلنا لهذه التعليمة فهذا يعني أن التفريع السابقة والتي دخلت إلى حل مسألة جديدة قد فشلت في الوصول للهدف ... والآن سنعيد للخانة التي استبدلناها قبل الاستدعاء قيمتها الأصلية جتى نتابع الحساب وكأن شيئا لم يكن
}//انتهاء الشرط
operators_--;//إذا وصلنا إلى هنا فهذا يعني أن العملية التي أجريت بين المعامل الاول والثاني كانت خاطئة لأننا جربنا كل الاحتمالات الناتجة عنها وفشلت... ولذلك سننقص عدد العمليات المجراة لأن العملية المجراةكانت خاطئة
}//انتهاء تجربة العمليات بين المعاملين الاول والثاني
}
delete newn;//إذا وصلنا إلى هنا فهذا يعني أن المعامل الاول من البداية كان خاطئاً لأننا جربنا كل الاحتمالات الناتجة عن اختياره وفشلت لذلك سنحذف المصفوفة الناتجة عن حذفه لأننا سنختار غيره
}//انتهاء دالة
}//انتهاء دالة حل المسألة
void By_Mostafa_36a2(){
/* ما كان من خير فمن الله جل جلاله
وما كنا من تقصير فمني ... أستغفر الله العظيم
أخي القارئ... إذا كان هذا الشرح قد أفادك فلا تحرمني دعوة في ظهر الغيب جزاك الله خيراً
وأرجو أن نتعاون لسد أي خلل
والسلام عليكم*/
};

تم تعديل بواسطه أحمد الشنقيطي
تعديل العنوان.
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

. موضوع رائع ... ولكن

عيننا رحمك الله ...

ميزة التنسيق التلقائى موجودة بكل بيئات البرمجة .... نقرة زر واحدة تفى بالغرض أنظر وقارن سهولة القراء ...

//قبل البدء :أود تذكيرك أخي بأن البرمجة هي كتابة الخوارزمية فإذا أردت أخي الاستفادة من الشرح دون تضييع وقتك فاذهب إلى جسم الدالة  التالية وستجده في آخر البرنامج
void NewProblem(int*, int, int); //أهم دالة هي التي تقوم بإدارة حل المسألة ككل وكل مسألة جزئية

int sum(int a, int b)
{
return a + b;
}//تابع يعيد مجموع الدخل

int mul(int a, int b)
{
return a*b;
}//تابع يعيد جداء الدخل

int sub(int a, int b)
{
if (a > b)return a - b;
else return b - a;
}//تابع يعيد فرق عددين بالقيمة المطلقة

int div_(int a, int b)
{
if (a > b && a % b == 0)return a / b;
else return 0;
}//تابع يعيد ناتج قسمة الدخل الاول على الثاني ان كان الناتج عدد صحيح

int C(int a)
{
return a * (a - 1) / 2;
}//C(n,2) number of couples
//هذا التابع يحسب عدد احتمالات الأزواج من أجل ن عدد
int function(int, int, char); //للقيام بعملية على أول وسيطين نمرر العملية على شكل رمز بالوسيط الثالث
void mov(int*, int*, int, int); //دالة نسخ مصوفة مع استثناء احد العناصر
void ShowOperators(); //لإظهار العمليات المتبعة بعد الانتهاء
char operation[4] = {'+', '-', '*', '/'}; //مصفوفة لتعداد العمليات المتوفرة
char *Operators; //مجرد مؤشر سيؤشر لا حقا على مصفوفة تحتفظ بنوع العملية المجراة على الأعداد
int *Oprands; //مؤشر سيؤشر لا حقاً على مصفوفة للاحتفاظ بالأرقام التي أجريت عليها العمليات
int operators_ = 0; //عدد العمليات المجراة
#include<iostream.h>//تضمين مكتبة الدخل والخرج
#include<windows.h>//تضمين مكتبة التعامل مع ويندوز
//ملاحظة :لمحبي اللينوكس يمكنهم إلغاء المكتبة السابقة وأترك لهم أن يضعوا داخل التوابع التالية ما يكافئ عملها

void pause()
{
system("pause");
}//لإيقاف البرنامج مؤقتا

void exit()
{
exit(0);
}//للخروج قبل نهاية الدالة الرئيسية

void cls()
{
system("cls");
}; //لمحو كل ماهو ظاهر على الكونسول

void main()//الدالة الرئيسية
{
cls(); // محو الشاشة ... قد تبدو بلا فائدة ولكن ستتضح فائدتها في السطر 74
int nsize; //حجم مصفوفة المدخلات وهي عدد الأعداد
cout << "How Many Numbers You Want To enter ?\n"; //سؤال المستخدم
cin >> nsize; //ادخال عدد الاعداد
int *n = new int[nsize]; //انشاء مصفوفة بعدد الارقام تماما
for (int i = 0; i < nsize; i++)
{
cout << "Enter Number(" << i << ") :";
cin >> n[i];
}//حلقة لادخال العناصر الرقمية كلها
cout << "Enter The Goal Number : "; //سؤال
int N; //الرقم الهدف
cin >> N; //ادخال الرقم الهدف
Operators = new char[nsize - 1]; //عدد العمليات الأعظمي هو عدد الأرقام ناقصا 1 لذلك ننشئ مصفوفة العمليات بهذا الحجم
Oprands = new int[(nsize - 1)*2]; //سنستعمل عددين في كل عملية لذلك ننشئ مصفوفة للاحتفاظ بهذه الأعداد بهذا الحجم
NewProblem(n, nsize, N); //استدعاء دالة حل المسألة ووسطاؤها هم على الترتيب مصفوفة الأعداد ثم حجمها ثم الرقم الهدف
}

int function(int a, int b, char c)
{//هذه الدالة للتعرف على رمز العملية المراد القيام بها ثم القيام بها
if (c == '+')return sum(a, b);
else if (c == '-')return sub(a, b);
else if (c == '*')return mul(a, b);
else if (c == '/' && b != 0)return div_(a, b);
else return -1; //في حالة الخطأ في الكود... وليس لها داع
}

void mov(int*newn, int*n, int i_, int new_size)
{//عندما نختار رقما من المصفوفة فيجب حذفه من المصفوفة عند اختيار الرقم الثاني حتى لا نختار هذا الرقم مرتين وهذه هي وظيفة هذه الدالة
for (int i = 0; i < new_size && i != i_; i++)
newn[i] = n[i];
for (; i < new_size; i++)
newn[i] = n[i + 1];
}

void SaveOperator(int a, int b, char operation)
{//تقوم بحفظ العملية في مصفوفتي العمليات والمعاملات
Operators[operators_] = operation; //حفظ العملية
Oprands[operators_ * 2] = a; //حفظ المعامل الاول
Oprands[operators_ * 2 + 1] = b; //حفظ المعامل الثاني
}

void ShowOperators()
{
for (int i = 1; i < operators_ + 1; i++)
{
cout << Oprands[i * 2]//عرض المعامل الاول
<< Operators[i]//عرض العملية
<< Oprands[i * 2 + 1]//عرض المعامل الثاني
<< '='//عرض اشارة المساواة
<< function(Oprands[i * 2], Oprands[i * 2 + 1], Operators[i]) << endl; ////بدل الاحتفاظ في الجواب سنستدعي دالة الحساب ثانية
}//عرض العمليات المخزنة في مصفوفتي العمليات والمعاملات.....لمن أحب تطروير البرنامج يمكنه أن ينشئ بنية معطيات
//للعمليات بدل أن نستخدم مصفوفتين ولا بأس بإضافة الناتج أيضا للبنية كما أنه يجب تغيير حالة العرض عند عملية طرح
//عدد صغير من عدد كبير .. العملية التي يجريها التابع هي الفرق وهو عملية تبديلية أما عند العرض سيظن المستخدم أن
//البرنامج أوجد الناتج معكوس الإشارة لذلك يمكن اضافة شرط لعكس المعاملات في حالة الطرح عندما يكون المعامل الاول اصغر من الثاني
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////Algorithm's Function .............////////////////////////////////
////////////////////////////////////////BY Mostafa 36a2/////////////////////////////////////////////

void NewProblem(int*n, int new_size, int Goal)
{//دالة حل المسالة ككل والمسائل الجزئية ..... هنا الخوارزمية .... هنا لب البرنامج ... وكل الدوال الباقية مجرد مظاهر
for (int w = 0; w < new_size; w++)//من اجل جميع أرقام الدخل
if (n[w] == Goal)
{
cout << n[w] << "\tIt's In Your Numbers Man!!!\nIs It A Test For Stupidity :)?\nNo Problem Try Again .." << endl;
pause();
main();
}
//الشرط السابق في حال أدخل المستخدم الرقم الهدف ضمن أرقام الدخل ... وهنا لا داعي للبحث عنه فهو موجود... لاحظ استدعاء الدالة الرئيسية بدل الخروج من البرنامج ... فقد يكون المستخدم أخطأ سهواً
int new_element = 0; //العنصر الجديد هو ناتج العملية التي ستجرى على المعاملين
int a, b; //المعاملان الذين ستجرى عليهما العملية
for (int i = 0; i < C(new_size); i++)
{//سنكرر بعدد الأزواج الممكن تشكيلها من أرقام الدخل
a = n[i]; //نختار أحد الأرقام
int *newn = new int[new_size - 1]; //ننشئ مصفوفة جديدة أصغر من السابقة بواحد
mov(newn, n, i, new_size - 1); //المصفوفة الجديدة هي نفس القديمة بعد استثناء الرقم المختار
//لمن أراد تطوير البرنامج يمكنه تغيير طريقة استثناء الرقم المختار
for (int j = i; j < new_size - 1; j++)//الآن لدينا مصفوفة خالية من المعامل الاول سنكرر من اجل جميع عناصرها
{
b = newn[j]; //نختار احد هذه العناصر
for (int op = 0; op < 4; op++)//نكرر من اجل جميع العمليات
{
new_element = function(a, b, operation[op]); //العنصر الجديد سيكون ناتج العملية التي ستجرى على المعاملين
operators_++; //نزيد عدد العمليات المجراة لأننا أجرينا عملية للتو
SaveOperator(a, b, operation[op]); //نحفظ العملية التي قمنا بها
if (new_element == Goal)//هذا يعني اننا وصلنا للرقم المطلوب
{
cout << "It Takes " << operators_ << " operators" << endl; //رسالة تخبر المستخدم بعدد العمليات المجراة
cout << "I Think It was Speeder Than You Thought Mr.Ahmad ? isn't it!!\n"; //هذه رسالة خاصة للأستاذ أحمد تخبره بأن البرنامج سريع جدا حتى لو كانت الخوارزمية غبية!!هههه
//ملاحظة هامة : قد تنزعج أخي أثناء قراءتك للشرح من وجود مثل السطر السابق ولكنني أصررت على عدم حذفه وذلك للتأكيد على أن هذه المظاهر غير مهمة والمهم هو الخوارزمية
ShowOperators(); //طبعا سنعرض للمستخدم العمليات التي قمنا بها
if (operators_ > 7)cout << "IT Sometimes Look Crazy To Do That Like This :p\n"; //رسالة البرءة من الحول والقوة فالخوارزمية قد تؤدي الى سلوك طريق طويل دون الحاجة للتطويل
//وهنا يكمن جوهر التطوير بإضافة بعض الخطوات والشروط في الخوارزمية كي تسلك الطريق القصير حتى لا تلتقي بالذئب
cout << "With All My Thanks For You MY Teacher \nI am Ready For Any Thing :)\n"; //رسالة الشكر للأستاذ أحمد
//goto : ملاحظة هامة
pause(); //ايقاف البرنامج مؤقتا حتى يلحق المستخدم يقرا
cout.flush(); //لافراغ ذاكرة العرض فأحيانا مع استخدام دالة الايقاف السابقة يمتنع البرنامج عن افراغ ذاكرة العرض
pause(); //لا أدري لماذا أضفتها ثانية ولكن لا مانع
exit(); //انهاء البرنامج
//ويمكن بدل الانهاء استدعاء الدالة الرئيسة ثانية ولكن يجب تصفير المتحولات وخصوصا عدد العمليات
}
else if (new_element != 0 && new_size != 2)//ان لم نصل للرقم الهدف وكانت المسألة الحالية بها أكثر من رقمين
//سبب الشرط الأخير هو أنه إن لم يكن هناك سوى رقمين وجربنا جميع العمليات فهذا يعني احد امرين اما اننا في مسألة جزئية لا تفيد في الحل
//وإما أن الحل أصلاً غير ممكن وذلك ان كنا في المسألة الرئيسسة
{//انتباه: الآن سننشئ مسألة جديدة
//المسألة الجديدة تعالج الاحتمالات الناتجة عن استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما
newn[j] = new_element; //لاحظ كيف استبدلنا المعامل الثاني بالرقم الناتج ...ولا تنسى أن هذه المصوفة قد حُذف منها المعامل الاول سابقا
NewProblem(newn, new_size - 1, Goal); //استدعاء حلال المسائل لمسألتنا الجديدة ... الهدف هو نفسه والحجم أصغر بواحد والأعداد هي نفس أعداد المسألة السابقة بعد استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما
newn[j] = b; //إذا وصلنا لهذه التعليمة فهذا يعني أن التفريع السابقة والتي دخلت إلى حل مسألة جديدة قد فشلت في الوصول للهدف ... والآن سنعيد للخانة التي استبدلناها قبل الاستدعاء قيمتها الأصلية جتى نتابع الحساب وكأن شيئا لم يكن
}//انتهاء الشرط
operators_--; //إذا وصلنا إلى هنا فهذا يعني أن العملية التي أجريت بين المعامل الاول والثاني كانت خاطئة لأننا جربنا كل الاحتمالات الناتجة عنها وفشلت... ولذلك سننقص عدد العمليات المجراة لأن العملية المجراةكانت خاطئة
}//انتهاء تجربة العمليات بين المعاملين الاول والثاني
}
delete newn; //إذا وصلنا إلى هنا فهذا يعني أن المعامل الاول من البداية كان خاطئاً لأننا جربنا كل الاحتمالات الناتجة عن اختياره وفشلت لذلك سنحذف المصفوفة الناتجة عن حذفه لأننا سنختار غيره
}//انتهاء دالة
}//انتهاء دالة حل المسألة

void By_Mostafa_36a2()
{
/* ما كان من خير فمن الله جل جلاله
وما كنا من تقصير فمني ... أستغفر الله العظيم
أخي القارئ... إذا كان هذا الشرح قد أفادك فلا تحرمني دعوة في ظهر الغيب جزاك الله خيراً
وأرجو أن نتعاون لسد أي خلل
والسلام عليكم*/
};

تم تعديل بواسطه محمد عودة
3

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

:lol: جزاك الله خيراً أخي ... دوماً في المقدمة :wink:

ولكنني أحب عندما أكتب الكود أن أقصر من طوله لتسهل قراءته ...

وعلى أي حال صار لدينا شكلان للاختيار بينهما ... :happy: ماشاء الله عليك فوراً طورت الكود ... الله يسلم ايديك وان شاء الله بكرة منسمع اعدام مبارك :stop: ...

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

محاولة لا بأس بها أخي مصطفى, بارك الله فيك.

بالسبة لملاحظاتي فهي كالتالي :

  1. الكود غير خاضع لقوانين السي++ القياسية.
  2. الكود لا يدعم الأعداد السالبة :

    post-219439-057231200 1338570884_thumb.p


  3. يمكن أن تكون قيمة المفتاح غير قابلة للتوليد من طرف الأعداد المُدخلة. (لم تدرس هذه الحالة !)
  4. أرى أنه من الأفضل استخدام binary tree من أجل الحصول على الإحتمالات الممكنة :

    Algo.h3.gif


للفائدة, وضعت لك لُعبة Le compte est bon مكتوبة بالسي++ في بيئة Microsoft visual studio 6 :

أرجو لك التوفيق.

تم تعديل بواسطه أحمد الشنقيطي
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بسم الله الرحمن الرحيم

أستاذ أحمد جزاك الله خيراً .... ولكن أسألك سؤالاً ...

هل كتابة الكود باللغة القياسية تعني إزالة ال.h في آخر اسم المكتبة ثم كتابة using namespace std

إذا كان الأمر كذلك فلا أرى أنه يستحق الذكر ... مجرد سطر ...أما إن كان غير ذلك فأرجو منك أن تفيدنا به ..مع تبيان فائدته... فقد سمعت هذه الملاحظة اكثر من مرة ويبدو أنها عقبة امام الكود الكامل (نسبياً)... والكمال لله وحده ..

أما بالنسبة لدعم الأعداد السالبة فالأمر بسيط جداً :

كل ما هنالك أن نزيل الشرط في التابع التالي(وإنما وضعت الشرط لأنني ظننت أننا لسنا بحاجة إلى الأعداد السالبة) :



int sub(int a, int b)

{ if (a > b)return a - b;

else return b - a;

}//تابع يعيد فرق عددين بالقيمة المطلقة

ويصبح التابع:



int sub(int a, int b)

{ return a - b;}//تابع يعيد ناتج طرح عددين بالقيمة الجبرية موجبة أو سالبة

ولكن على هذه الحالة سنكرر عملية الطرح مرتين وذلك داخل جسم التابع function أي أننا أضفنا عملية جديدة للحساب وهي الطرح مع تبديل العاملين...

لذلك أصبح عدد العمليات خمسة وسنرمز للعملية الجديدة برمز ما وليكن '_' وبالتالي سنغير مصفوفة العمليات ونغير من تكرار حلقةالعمليات الى 5 ...وسأغير في دوال حفظ وعرض العمليات

وهكذا أصبح لدينا بين كل رقمين 5 عمليات وهذا سيغير عدد الاحتمالات الذي سنبرهن عليه في النهاية

وبالنسبة لموضوع أن تكون غير قابلة للتوليد ... فهي فكرة تستحق الذكر ولكن حلها في قمة البساطة ...

بما أن دالة حل المسألة يفترض بها أن تنهي البرنامج وحدها بعد الانتهاء من الحساب ...

فسنكتب في جسم الدالة الرئيسية main بعد استدعاء دالة حل المسألة العبارة التي تعلم المستخدم بأن الحل لم يتم لعدم توافر الأرقام المناسبة ... ثم نستدعي main لإعطاء فرصة أخرى للمستخدم .ما رأيك أخي ....

أما بالنسبة لطريقة إيجاد الاحتمالات ... فهناك عدة طرق .. ولكن كتابتها ككود ... أظن انه متشابه ... أم بالنسبة لخوارزمية إيجاد عدد الاحتمالات....

فلا تستغرب أنني أثناء كتابتي للخوارزمية والتفكير بها ... قمت بالفعل بحل كل الاحتمالات الممكنة من أجل مجموعة مكونة من أربع أرقام واستغرق ذلك حوالي ساعة ...

1338909159461.jpg

وكنت قد توصلت قبل ذلك بيومين إلى أن عدد الاحتمالات الكلي هو :

133890915982.png

وإليك البرهان :

لدينا n عددا

نختار واحد (وعدد احتمالات ذلك n) ولنسمه a

بقي n-1 عددا

نختار واحد( وعدد احتمالات ذلكn-1 (ولنسمه b

لدينا 4 عمليات لنجريها بحيث يكون المعامل الأول هو a

وبالتالي صار عدد الاحتمالات الكلي للقيام بهذا الاختيار هو

جداء الاحتمالات السابقة لأنها مستقلة عن بعضها

n*(n-1)*4

وعند حساب ناتج العملية السابقة سيكون لدينا

مجموعة من n-1 عددا عناصرها هي نفس عناصر المجموعة الرئيسية ولكن باستبدال العنصرين الذين أجرينا عليهما عملية حسابية .. بناتج العملية وبذلك نقص عدد الأرقام الكلي واحدا ...

//////////////////////////////////////////////

والآن سنكرر العملية السابقة كلها من أجل كل مجموعة جديدة حتى يصبح عدد الأرقام المتبقية واحد .. وبذلك نكون قد مسحنا كل الاحتمالات ولكننا كررنا عمليتي الضرب والجمع .....

عند تكرار العملية بعد ذلك (n-1)*(n-2)*4

وعند التكرار ثانية (n-2)*(n-3)*4

وهكذا إلى أن نصل إلى 2*1*4

وعندها نضرب جميع الاحتمالات السابقة ببعضها لينتج لدينا عدد الاحتمالات الكلي :

n*(n-1)*4*(n-1)*(n-2)*4*(n-2)*(n-3)*4*.................*2*1*4

نلاحظ أننا نضرب بالرقم4 عدداًn-1 من المرات وبذلك نحصل على 4^n-1 والذي أصبح بعد تعديل الخوارزمية كما نوه الأستاذ أحمد 5^n

ونلاحظ اننا نضرب بكل من n-1 و n-2 و n-3 وحتى ال 2 و 1 (نضرب بها مرتين )

وهذا العدد يكافئ (n-1)!*(n-1)!

وبما اننا نضرب بn مرة واحدة فستصبح العلاقة (n!^2)/n

وبذلك فعدد الاحتمالات الكلي بعد التعديل هو :

1338909159423.png

انتهى البرهان ... أرجو ان يكون واضحاً ... لكن ........

الطريقة السابقة في ترتيب الحل تؤدي إلى تكرار العمليات على الأزواج .. فكل زوج سيتكرر مرتين ... في كل مجموعة ... لذلك للحصول على عدد الاحتمالات دون تكرار نقسم الجواب السابق على 2 مرفوعا للأس عدد المجموعات .. حيث عدد المجموعات هو n-1

1338909159464.png

وبرهان ذلك : سنقوم بالحل كما يلي :

عدد احتمالات المجموعة الواحدة هو عدد الأزواج *5 وذلك لكل مجموعة

عدد الأزواج هو عدد العناصر * (عدد العناصر -1)مقسوما على 2 وهو :C(n,2)

أي: (n*(n-1))/2

وفي كل مجموعة سيتكرر الرقم السابق مع إنقاص الn أكثر وأكثر

حيث سيصبح الجواب النهائي :

{n*(n-1)/2}*5*{(n-1)*(n-2)/2}*5*...................*{2*1/2}*5

وهذا سيعطينا نفس الجواب السابق مقصوماً على 2 مرفوعة للأس n-1

1338909159423.png

العجيب أنه رغم وجود 2.5 فإن الجواب هو قيمة صحيحة تماما وهذا يزيد التأكيد على صحته

وهو نفس الجواب الذي ستحصل عليه أستاذي بالطريقة الشجرية والتي بالطبع أسهل للفهم

أعتذر على إطالة الشرح الرياضي ولكن المسألة كلها تعتمد على الرياضيا وأنا أحب الرياضيات والاحتمالات خصوصاً ....

والآن أستاذي إليك الكود مشروحا بعد التعديل عليه بما تفضّلت...:



//قبل البدء :أود تذكيرك أخي بأن البرمجة هي كتابة الخوارزمية فإذا أردت أخي

// الاستفادة من الشرح دون تضييع وقتك فاذهب إلى جسم الدالة التالية وستجده في آخر البرنامج

void NewProblem(int*, int, int); //أهم دالة هي التي تقوم بإدارة حل المسألة ككل وكل مسألة جزئية



int sum(int a, int b)

{

return a + b;

}//تابع يعيد مجموع الدخل



int mul(int a, int b)

{

return a*b;

}//تابع يعيد جداء الدخل



int sub(int a, int b)

{

return a-b;

}//تابع يعيد ناتج طرح عددين بقيمة موجبة أوسالبة



int div_(int a, int b)

{

if (a > b && a % b == 0)return a / b;

else return 0;

}//تابع يعيد ناتج قسمة الدخل الاول على الثاني ان كان الناتج عدد صحيح

int C(int a)

{

return a * (a - 1) / 2;

}//C(n,2) number of couples

//هذا التابع يحسب عدد احتمالات الأزواج من أجل ن عدد

int function(int, int, char); //للقيام بعملية على أول وسيطين نمرر العملية على شكل رمز بالوسيط الثالث

void mov(int*, int*, int, int); //دالة نسخ مصوفة مع استثناء احد العناصر

void ShowOperators(); //لإظهار العمليات المتبعة بعد الانتهاء

char operation[5] = {'+', '-', '*', '/','_'}; //مصفوفة لتعداد العمليات المتوفرة والعملية الأخيرة من اليمين هي طرح مع عكس الإشارة

char *Operators; //مجرد مؤشر سيؤشر لا حقا على مصفوفة تحتفظ بنوع العملية المجراة على الأعداد

int *Oprands; //مؤشر سيؤشر لا حقاً على مصفوفة للاحتفاظ بالأرقام التي أجريت عليها العمليات

int operators_ = 0; //عدد العمليات المجراة

#include<iostream>//تضمين مكتبة الدخل والخرج

using namespace std;//لاستعمال اللغة القياسية... ولا أدري ما الفرق

#include<windows.h>//تضمين مكتبة التعامل مع ويندوز

//ملاحظة :لمحبي اللينوكس يمكنهم إلغاء المكتبة السابقة وأترك لهم أن يضعوا داخل التوابع التالية ما يكافئ عملها



void pause()

{

system("pause");

}//لإيقاف البرنامج مؤقتا



void exit()

{

exit(0);

}//للخروج قبل نهاية الدالة الرئيسية



void cls()

{

system("cls");

}; //لمحو كل ماهو ظاهر على الكونسول



void main()//الدالة الرئيسية

{

cls(); // محو الشاشة ... قد تبدو بلا فائدة ولكن ستتضح فائدتها في السطر 128وكذلك في نهاية هذه الدالة

int nsize; //حجم مصفوفة المدخلات وهي عدد الأعداد

cout << "How Many Numbers You Want To enter ?\n"; //سؤال المستخدم

cin >> nsize; //ادخال عدد الاعداد

int *n = new int[nsize]; //انشاء مصفوفة بعدد الارقام تماما

for (int i = 0; i < nsize; i++)

{

cout << "Enter Number(" << i << ") :";

cin >> n[i];

}//حلقة لادخال العناصر الرقمية كلها

cout << "Enter The Goal Number : "; //سؤال

int N; //الرقم الهدف

cin >> N; //ادخال الرقم الهدف

Operators = new char[nsize - 1]; //عدد العمليات الأعظمي هو عدد الأرقام ناقصا 1 لذلك ننشئ مصفوفة العمليات بهذا الحجم

Oprands = new int[(nsize - 1)*2]; //سنستعمل عددين في كل عملية لذلك ننشئ مصفوفة للاحتفاظ بهذه الأعداد بهذا الحجم

NewProblem(n, nsize, N); //استدعاء دالة حل المسألة ووسطاؤها هم على الترتيب مصفوفة الأعداد ثم حجمها ثم الرقم الهدف

cout<<"Sorry Can't Reach The Goal Number Cause \n\tthe numbers you give unsufficient\n Please Try Again"<<endl;//سنصل إلى هنا في حالة لم يتم إيجاد الحل

pause();//للانتظار للقراءة

main();//إعادة استدعاء الدالة الرئيسية

}



int function(int a, int b, char c)

{//هذه الدالة للتعرف على رمز العملية المراد القيام بها ثم القيام بها

if (c == '+')return sum(a, b);

else if (c == '-')return sub(a, b);

else if (c =='_')return sub(b,a);//هذه العملية للدلالة على الطرح مع تبديل العاملين

else if (c == '*')return mul(a, b);

else if (c == '/' && b != 0)return div_(a, b);

else return -1; //في حالة الخطأ في الكود... وليس لها داع

}



void mov(int*newn, int*n, int i_, int new_size)

{//عندما نختار رقما من المصفوفة فيجب حذفه من المصفوفة عند اختيار الرقم الثاني حتى لا نختار هذا الرقم مرتين وهذه هي وظيفة هذه الدالة

for (int i = 0; i < new_size && i != i_; i++)

newn[i] = n[i];

for (; i < new_size; i++)

newn[i] = n[i + 1];

}



void SaveOperator(int a, int b, char operation)

{//تقوم بحفظ العملية في مصفوفتي العملياتوالمعاملات

if(operation!='_'){//لأنها تتطلب عكس المعاملات

Operators[operators_] = operation; //حفظ العملية

Oprands[operators_ * 2] = a; //حفظ المعامل الاول

Oprands[operators_ * 2 + 1] = b; //حفظ المعامل الثاني

}

else

{

Operators[operators_] = '-'; //حفظ العملية

Oprands[operators_ * 2] = b; //حفظ المعامل الثاني مكان الأول

Oprands[operators_ * 2+1 ] = a; //حفظ المعامل الأول مكان الثاني

}



}



void ShowOperators()

{

for (int i = 1; i < operators_ + 1; i++)

{

cout << Oprands[i * 2]//عرض المعامل الاول

<< Operators[i]//عرض العملية

<< Oprands[i * 2 + 1]//عرض المعامل الثاني

<< '='//عرض اشارة المساواة

<< function(Oprands[i * 2], Oprands[i * 2 + 1], Operators[i]) << endl; ////بدل الاحتفاظ في الجواب سنستدعي دالة الحساب ثانية

}//عرض العمليات المخزنة في مصفوفتي العمليات والمعاملات.....لمن أحب تطروير البرنامج يمكنه أن ينشئ بنية معطيات

//للعمليات بدل أن نستخدم مصفوفتين ولا بأس بإضافة الناتج أيضا للبنية كما أنه يجب تغيير حالة العرض عند عملية طرح

//عدد صغير من عدد كبير .. العملية التي يجريها التابع هي الفرق وهو عملية تبديلية أما عند العرض سيظن المستخدم أن

//البرنامج أوجد الناتج معكوس الإشارة لذلك يمكن اضافة شرط لعكس المعاملات في حالة الطرح عندما يكون المعامل الاول اصغر من الثاني

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////Algorithm's Function .............////////////////////////////////

////////////////////////////////////////BY Mostafa 36a2/////////////////////////////////////////////



void NewProblem(int*n, int new_size, int Goal)

{//دالة حل المسالة ككل والمسائل الجزئية ..... هنا الخوارزمية .... هنا لب البرنامج ... وكل الدوال الباقية مجرد مظاهر

for (int w = 0; w < new_size; w++)//من اجل جميع أرقام الدخل

if (n[w] == Goal)

{

cout << n[w] << "\tIt's In Your Numbers Man!!!\nIs It A Test For Stupidity :)?\nNo Problem Try Again .." << endl;

pause();

main();

}

//الشرط السابق في حال أدخل المستخدم الرقم الهدف ضمن أرقام الدخل ... وهنا لا داعي للبحث عنه فهو موجود

//... لاحظ استدعاء الدالة الرئيسية بدل الخروج من البرنامج ... فقد يكون المستخدم أخطأ سهواً

int new_element = 0; //العنصر الجديد هو ناتج العملية التي ستجرى على المعاملين

int a, b; //المعاملان الذين ستجرى عليهما العملية

for (int i = 0; i < C(new_size); i++)

{//سنكرر بعدد الأزواج الممكن تشكيلها من أرقام الدخل

a = n[i]; //نختار أحد الأرقام

int *newn = new int[new_size - 1]; //ننشئ مصفوفة جديدة أصغر من السابقة بواحد

mov(newn, n, i, new_size - 1); //المصفوفة الجديدة هي نفس القديمة بعد استثناء الرقم المختار

//لمن أراد تطوير البرنامج يمكنه تغيير طريقة استثناء الرقم المختار

for (int j = i; j < new_size - 1; j++)//الآن لدينا مصفوفة خالية من المعامل الاول سنكرر من اجل جميع عناصرها

{

b = newn[j]; //نختار احد هذه العناصر

for (int op = 0; op < 5; op++)//نكرر من اجل جميع العمليات

{

new_element = function(a, b, operation[op]); //العنصر الجديد سيكون ناتج العملية التي ستجرى على المعاملين

operators_++; //نزيد عدد العمليات المجراة لأننا أجرينا عملية للتو

SaveOperator(a, b, operation[op]); //نحفظ العملية التي قمنا بها

if (new_element == Goal)//هذا يعني اننا وصلنا للرقم المطلوب

{

cout << "It Takes " << operators_ << " operators" << endl; //رسالة تخبر المستخدم بعدد العمليات المجراة

cout << "I Think It was Speeder Than You Thought Mr.Ahmad ? isn't it!!\n"; //هذه رسالة خاصة للأستاذ أحمد تخبره بأن البرنامج سريع جدا حتى لو كانت الخوارزمية غبية!!هههه

//ملاحظة هامة : قد تنزعج أخي أثناء قراءتك للشرح من وجود مثل السطر السابق ولكنني أصررت على عدم حذفه وذلك للتأكيد على أن هذه المظاهر غير مهمة والمهم هو الخوارزمية

//وإن حذفتها أكون سعيداً جداً لأنك ستكون قد خطوت الخطوة الأولى على طريق تطوير الكود//مع تحياتي

ShowOperators(); //طبعا سنعرض للمستخدم العمليات التي قمنا بها

if (operators_ > 7)cout << "IT Sometimes Look Crazy To Do That Like This :p\n"; //رسالة البرءة من الحول والقوة فالخوارزمية قد تؤدي الى سلوك طريق طويل دون الحاجة للتطويل

//وهنا يكمن جوهر التطوير بإضافة بعض الخطوات والشروط في الخوارزمية كي تسلك الطريق القصير

cout << "With All My Thanks For You MY Teacher \nI am Ready For Any Thing :)\n"; //رسالة الشكر للأستاذ أحمد

pause(); //ايقاف البرنامج مؤقتا حتى يلحق المستخدم يقرا

cout.flush(); //لافراغ ذاكرة العرض فأحيانا مع استخدام دالة الايقاف السابقة يمتنع البرنامج عن افراغ ذاكرة العرض

exit(); //انهاء البرنامج

//ويمكن بدل الانهاء استدعاء الدالة الرئيسة ثانية ولكن يجب تصفير المتحولات وخصوصا عدد العمليات

}

else if (new_element != 0 && new_size != 2)//ان لم نصل للرقم الهدف وكانت المسألة الحالية بها أكثر من رقمين

//سبب الشرط الأخير هو أنه إن لم يكن هناك سوى رقمين وجربنا جميع العمليات فهذا يعني احد امرين اما اننا في مسألة جزئية لا تفيد في الحل

//وإما أن الحل أصلاً غير ممكن وذلك ان كنا في المسألة الرئيسسة

{//انتباه: الآن سننشئ مسألة جديدة

//المسألة الجديدة تعالج الاحتمالات الناتجة عن استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما

newn[j] = new_element; //لاحظ كيف استبدلنا المعامل الثاني بالرقم الناتج ...ولا تنسى أن هذه المصوفة قد حُذف منها المعامل الاول سابقا

NewProblem(newn, new_size - 1, Goal);//الشرح هام

//استدعاء حلال المسائل لمسألتنا الجديدة ... الهدف هو نفسه والحجم أصغر بواحد والأعداد هي نفس أعداد المسألة السابقة بعد استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما

newn[j] = b;//الشرح هام

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

}//انتهاء الشرط

operators_--; //إذا وصلنا إلى هنا فهذا يعني أن العملية التي أجريت بين المعامل الاول والثاني كانت خاطئة لأننا جربنا كل الاحتمالات الناتجة عنها وفشلت... ولذلك سننقص عدد العمليات المجراة لأن العملية المجراةكانت خاطئة

}//انتهاء تجربة العمليات بين المعاملين الاول والثاني

}

delete newn; //إذا وصلنا إلى هنا فهذا يعني أن المعامل الاول من البداية كان خاطئاً لأننا جربنا كل الاحتمالات الناتجة عن اختياره وفشلت لذلك سنحذف المصفوفة الناتجة عن حذفه لأننا سنختار غيره

}//انتهاء دالة

}//انتهاء دالة حل المسألة



void By_Mostafa_36a2()

{

/* ما كان من خير فمن الله جل جلاله

وما كنا من تقصير فمني ... أستغفر الله العظيم

أخي القارئ... إذا كان هذا الشرح قد أفادك فلا تحرمني دعوة في ظهر الغيب جزاك الله خيراً

وأرجو أن نتعاون لسد أي خلل

والسلام عليكم*/

};

وفي الختام أضع بين يديكم آخر إصدار للبرنامج بدون شرح ..

وأنتظر رأيك أستاذ أحمد بعمله .. وأنتظر من الأخ AudaNix تحليلاً مفصلا لرأيه وذلك يعني لي الكثير جزاكم الله خيرا لمتابعة الموضوع والسلام عليكم



#include<iostream>

using namespace std;

#include<windows.h>

#define pause system("pause");

#define exit exit(0);

#define cls system("cls");

int fact(int x)

{

return(x==1?1:(x* fact(x-1)));

}

float power(float x,int power=2)

{

float y=1;for(int i=0;i<power;i++)y*=x;return y;

}

void NewProblem(int*, int , int);

char operation[5] = {'+', '-', '*', '/','_'};

char *Operators;

int *Oprands;

int operators_ = 0;

/************************************************************/

void main()

{cls;

int nsize;

cout << "How Many Numbers You Want To enter ?\n";cin >> nsize;

int *n = new int[nsize];

for (int i = 0; i < nsize; i++)

{

cout << "Enter Number(" << i << ") :";

cin >> n[i];

}

int N;

cout << "Enter The Goal Number : ";cin >> N;

Operators = new char[nsize - 1];

Oprands = new int[(nsize - 1)*2];

cout<<"\nNumber of possibilities is\t"<<power(fact(nsize),2)*power(2.5,nsize-1)/nsize<<endl;

NewProblem(n, nsize, N);

cout<<"Sorry Can't Reach The Goal Number Cause \n\tthe numbers you give unsufficient\n Please Try Again"<<endl;

pause;

main();

}

/**************************************************************/

int function(int a, int b, char c){

if (c == '+')return a+b;

else if (c == '-')return a-b;

else if (c =='_')return b-a;

else if (c == '*')return a*b;

else if (c == '/' && b != 0)return ( (a > b && a % b == 0)?(a/b):0);

else return -1; }

/*************************************************************/

void mov(int*newn, int*n, int i_, int new_size){

for (int i = 0; i < new_size && i != i_; i++)newn[i] = n[i];

for (; i < new_size; i++)newn[i] = n[i + 1];}

/***********************************************************/

void SaveOperator(int a, int b, char operation){

if(operation!='_'){

Operators[operators_] = operation;

Oprands[operators_ * 2] = a;

Oprands[operators_ * 2 + 1] = b; }

else{

Operators[operators_] = '-';

Oprands[operators_ * 2] = b;

Oprands[operators_ * 2+1 ] = a; }}

/*********************************************************/

void ShowOperators(){cout<<endl;

for (int i = 1; i < operators_ + 1; i++)

cout << Oprands[i * 2] <<'\t'

<< Operators[i ] <<'\t'

<< Oprands[i * 2 + 1] <<'\t'

<< '=' <<'\t'

<< function(Oprands[i * 2], Oprands[i * 2 + 1], Operators[i]) <<endl

<<endl;}

//////////////////////////////////Algorithm's Function .............////////////////////////////////

////////////////////////////////////////BY Mostafa 36a2/////////////////////////////////////////////

int S=1;

void NewProblem(int*n, int new_size, int Goal)

{

for (int w = 0; w < new_size; w++)

if (n[w] == Goal)

{

cout << n[w] << "\tIt's In Your Numbers Man!!!\nIs It A Test For Stupidity :)?\nNo Problem Try Again .." << endl;

pause;

main();

}

int new_element = 0;

int a, b;

for (int i = 0; i <(new_size * (new_size - 1) / 2); i++)

{

a = n[i];

int *newn = new int[new_size - 1];

mov(newn, n, i, new_size - 1);

for (int j = i; j < new_size - 1; j++)

{

b = newn[j];

for (int op = 0; op < 5; op++)

{

new_element = function(a, b, operation[op]);

operators_++; cout<<S<<'\r';S++;

SaveOperator(a, b, operation[op]);

if (new_element == Goal)

{

cout << "\nIt Takes " << operators_ << " operators" << endl;

ShowOperators();

if (operators_ > 7)cout << "IT Sometimes Look Crazy To Do That Like This :p\n";

pause;

cout.flush();

exit;

}

else if (new_element != 0 && new_size != 2)

{

newn[j] = new_element;

NewProblem(newn, new_size - 1, Goal);

newn[j] = b;

}

operators_--;

}

}

delete newn;

}



}

تم تعديل بواسطه مصطفى 36a2
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أحسنت أخي مصطفى, مشاركة رائعة :)

لا أملك الوقت الكافي الآن للرد على أسئلتك نظرا لانشغالي بمشروع التخرج.

سأرد عليك حالما أنتهي من المشروع (أسبوع على الأكثر) إن شاء الله.

أرجو لك التوفيق.

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بارك الله فيك أخي احمد ووفقك إلى ما يحبه ويرضاه ... أسأل الله لك التوفيق والتفوق ... ولا بأس في الانتظار فأنا أيضا لدي امتحانات ...

ومرحباً بالأخ asmer ووننتظر المزيد من جرعات الإنعاش للمنتدى ... جزاكم الله خيراً...

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

هل كتابة الكود باللغة القياسية تعني إزالة ال.h في آخر اسم المكتبة ثم كتابة using namespace std

كتابة الكود القياسي تعني احترام syntaxe اللغة بكل تفاصيله :)

نعلم جميعا أن السي++ احتوت على جزء من لغة السي (الأب) ، وللأسف كانت هناك مشكلتان بعدما بدأ المقياس الأول للغة سي++ !.

  • المشكلة الأولى : أسماء المكتبات
    القياسية في لغة السي ++ بالإضافة إلى التي تزودنا بها بعض المترجمات تكون بصيغ غير موحدة !, مثلا بعضها ينتهي بـ.h وبعضها ينتهي بـ.hpp
  • المشكلة الثانية : عندما نقوم بكتابه تطبيق كبير ويدخل في برمجة هذا التطبيق أكثر من مبرمج, حينها يجب أن نراعي أسماء المتغيرات التي سوف نستخدمها, فمثلا لو قام أحد المبرمجين بكتابه اسم متغير في ملف a.h ، وقمت أنت بكتابة نفس اسم المتغير في الملف b.h , عندما تقوم بترجمة التطبيق (بجميع ملفاته) ستجد خطأ في مرحلة الـ Linker و السبب يعود إلى استخدم أكثر من متغير بنفس الإسم ! و هذا ما يسبب مشكلة التصادم أو ما يعرف بـCoillision .

المقياس القديم للغة السي++ كان يحتوي على تلك العيوب إلى أن جاء المقياس الجديد (وهو ليس بجديد فهو منذ عام 1997, أي حوالي 15 سنه من الآن) ، المقياس الجديد حل المشكلة الأولى بأن يتم حذف جميع الإمتدادات من المكتبات القياسية وجعلها من غير امتداد, أما مكتبات السي القياسية الموجودة في السي++ فأيضا نحذف امتدادها مع بإضافة حرف c قبل اسم المكتبة للدلالة على أنها قادمة من لغة السي.

مثال على المقياس القديم :

#inlcude<stdio.h>
#inlcude<stdlib.h>
#inlcude<iostream.h>

في المقياس الجديد أصبحت كتابة الكود بهذا الشكل :

#inlcude<cstdio>
#inlcude<cstdlib>
#inlcude<iostream>

وهكذا تم حل مشكلة عدم وجود امتداد محدد للمكتبات القياسية.

بالنسبة للمشكلة الثانية فقد قام مطوروا اللغة بإيجاد مفهوم جديد يسمى بـ Name Space , أي مساحة الأسماء حيث سنقوم بتعريف جميع المتغيرات والفئات التي نريد, وفي حال كان هناك أكثر من namespace مثلا : مساحة لـ A و أخرى لـ B , وكل منهم احتوى على متغير بنفس الإسم.

الآن لن نجد مشكلة في التصادم, لأن كل واحد من هذه المتغيرات معرف في "مساحة" خاصة به و بهذا نكون قد أنهينا مشكله الـ Coillision, المقياس الجديد أضاف شيء جميل وهو أن جميع مكتبات السي++ القياسية ستكون داخل namespace تسمى std وذلك اختصارا لـ Standard لذلك أذا أردنا أستخدام مكتبات السي++ القياسية فيجب أن نحدد هذه المساحة باستخدام using

مثال على استخدام المكتبة iostream :

 #inlcude<iostream>
using namespace std;

هكذا نكون قد وصلنا للمكتبة iostream ولجميع ما بها عن طريق الـ namespace.

يمكنك أيضا مراجعة الموضوع التالي للأستاذ هاني :

مالفرق بين <iostream.h> و <iostream> ؟

عدد الاحتمالات الكلي بعد التعديل هو :

mimetex.cgi?\frac{n!^2*5^n}{n}

إذا كانت n=2 فإن الإحتمالات الممكنة هي :

x+y
x-y
x*y
x/y
y-x
y/x

إذن توجد 6 احتمالات, و هذا ما يُخالف علاقتك !

هناك أيضا ملاحظات أخرى :

  1. استخدام بنية شجربة أفضل من المصفوفات.
  2. التحقق من صحة الإدخال (خصوصا أنك تستخدم الحجز الديناميكي)
  3. ضع الدوال في مكتبة منفصلة و قم باستدعاءها في البرنامج الرئيسي.
  4. أُفَضِّلُ أن تنتقل إلى إحدى بيئات الــ GUI حتى يكون البرنامج أكثر أناقة.
  5. أيضا, من الأفضل استخدام دوال الوقت من أجل حساب الوقت المُستغرق لإيجاد المفتاح (تماما كما في البرنامج الذي أرفقتُه لك).

ملاحظة أخيرة : ربما أتأخر في الرد عليك حتى نهابة الشهر المُقبل نظرا لتمديد فترة مشروع التخرج :)

أرجو لك التوفيق.

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

لذلك أصبح عدد العمليات خمسة وسنرمز للعملية الجديدة برمز ما وليكن '_' وبالتالي سنغير مصفوفة العمليات ونغير من تكرار حلقةالعمليات الى 5 ...وسأغير في دوال حفظ وعرض العمليات

أعتقد أن الكود سيتغير كليا .. :D

بالنسبة لدعم الأعداد السالبة فالأمر بسيط جداً

قمت باختبار الكود الذي أرفقتَه في مشاركتك الأخيرة و قد توقف البرنامج عن العمل (الصورة في المرفقات).

post-219439-003429400 1339255739_thumb.p

بالنسبة لموضوع أن تكون غير قابلة للتوليد ... فهي فكرة تستحق الذكر ولكن حلها في قمة البساطة ...

بما أن دالة حل المسألة يفترض بها أن تنهي البرنامج وحدها بعد الانتهاء من الحساب ...

فسنكتب في جسم الدالة الرئيسية main بعد استدعاء دالة حل المسألة العبارة التي تعلم المستخدم بأن الحل لم يتم لعدم توافر الأرقام المناسبة ... ثم نستدعي main لإعطاء فرصة أخرى للمستخدم .ما رأيك أخي ....

يجب أن تكون هناك دالة تتحقق من هذه المسألة, إذا حصلنا على المفتاح بعد الانتهاء من الحساب ستعيد الدالة true و إذا لم نحصل على المفتاح ستعيد الدالة false.

تم تعديل بواسطه أحمد الشنقيطي
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

الله يجزيك الخير أستاذ أحمد ...

بالنسبة للكود القياسي وصلت الفكرة أما بالنسبة ل:

إذا كانت n=2 فإن الإحتمالات الممكنة هي :

x+y

x-y

x*y

x/y

y-x

y/x

إذن توجد 6 احتمالات, و هذا ما يُخالف علاقتك !

في الواقع عدد الاحتمالات هو 5 فقط وذلك لأن عملية القسمة ستعطي عددا كسريا في أحد شكليها y/x أو x/y ونحن لم ندرج التعامل مع الأعداد الكسرية ضمن المشروع .. laugh.gif

أما بالنسبة لعدد الاحتمالات فالصورة التي وضحتها حضرتك كان فيها خطأ مطبعي كما انه جاء بعدها بسطرين :

الطريقة السابقة في ترتيب الحل تؤدي إلى تكرار العمليات على الأزواج .. فكل زوج سيتكرر مرتين ... في كل مجموعة ... لذلك للحصول على عدد الاحتمالات دون تكرار نقسم الجواب السابق على 2 مرفوعا للأس عدد المجموعات .. حيث عدد المجموعات هو n-1

وكان عدد الاحتمالات الكلي هو :

Fpc13379.png

أما بالنسبة لتوقف البرنامج ... فلا أدري ما المشكلة ؟ ربما جهازك لا يحب الرياضيات .happy.gif

mCI13459.png

أعتقد أن الكود سيتغير كليا .. :D
بالفعل فالتغيير كان في العمليات الأساسية ( أي في قلب الخوارزمية )
يجب أن تكون هناك دالة تتحقق من هذه المسألة, إذا حصلنا على المفتاح بعد الانتهاء من الحساب ستعيد الدالة true و إذا لم نحصل على المفتاح ستعيد الدالة false.

في النسخة القادمة من البرنامج إن شاء الله نضيف شرط عند استدعاء الدالة ... فعلا هذا يغير من جمالية البرنامج ...شكرا لك أستاذي ..

أرجو ألا أكون قد أثقلت عليك بأسئلتي وتجاربي الفاشلة ... جزاك الله خيرا والسلام عليكم

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ها قد عدت, أعتذر عن التأخير :)

في الواقع عدد الاحتمالات هو 5 فقط وذلك لأن عملية القسمة ستعطي عددا كسريا في أحد شكليها y/x أو x/y ونحن لم ندرج التعامل مع الأعداد الكسرية ضمن المشروع

لا ضير في استخدام القسمة الإقليدية (القسمة الصحيحة).

يمكنك تطوير اللعبة أكثر لتشمل الخيارات التالية :

  • يمكن للبرنامج أن يتولى مهمة تحديد أرقام اللعبة بحيث يختار المستخدم مجال معين ثم يقوم البرنامج بتوليد أعداد عشوائية تقع في المجال المُختار.
  • يمكن للمستخدم اختيار النظام الرقمي الذي يريده (النظام العشري, الثنائي,السداسي, ....)
  • يمكن للمستخدم إدخال العمليات الحسابية التي يريد, مثل الجمع, الطرح, الضرب, القسمة و الأس إذا كنا في النظام العشري أو مؤثرات الــ Bitwise إذا كنا في النظام الثنائي.
  • يمكن للمستخدم أن يفرض على البرنامج استخدام كل عملية حسابية مرة واحدة على الأقل (أو مرة واحدة على الأكثر).
  • إظهار جميع الإحتمالات المؤدية للمفتاح (إن وجدت).
  • إظهار أقرب نتيجة للمفتاح في حالة عدم |إيجاد القيمة الفعلية للمفتاح.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

حمدا لله على سلامتك ... وأرجو لك كل التوفيق في مشروعك .. بأعلى العلامات ...

أستاذي أحمد ...

تطوير المشروع (على ما أعتقد ) ليس على النحو الذي ذكرته أستاذي الكريم ...

لأن الفكرة التي تطرحها هي في تحكم المستخدم في سير العملية ...

بيما أرى أننا لم ننتهي من جعل الخوارزمية ... أسرع ...

علينا إدخال بعض خوارزميات الذكاء الصنعي بحيث يستطيع الوصول بأكبر سرعة ممكنة وأقل عدد من المحاولات الفاشلة ...

وبعد إتمام هذه الخطوة تماما ... فسيكون تنفيذ أفكارك التي طرحتها مشروعاً كبيراً سنكون جاهزين للبدء فيه بإذن الله تعالى ...

فما رأيك في ذلك أستاذي جزاك الله خيراً ...

والسلام عليكم

تم تعديل بواسطه مصطفى 36a2
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

لعلك لم تفهم النفطة الأخيرة التي أشرتُ إليها :

إظهار أقرب نتيجة للمفتاح في حالة عدم |إيجاد القيمة الفعلية للمفتاح.

هذه النقطة تشمل تطوير خوارزمية البرنامج لتفادي العمليات الغير مفيدة, كما تشمل أيضا البحث عن أقرب قيمة للمفتاح في حالة عدم إيجاد قيمته الحقيقية.

نقطة أخرى, تطوير البرنامج يعني تطوير :

  • الخوارزمية
  • قوانين اللعبة
  • واجهة البرنامج

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

تمام فهمت عليك أستاذي .... أنا لم أبدأ بعد بتعلم أي أسلوبGUI لذلك إذا كان بإمكانك أن تقوم بجزء من هذه الخطوة أو أن تدلنا كيف نساعد فيها ... أما بالنسبة للباقي ... فما عليك سوى وضع الخطوات .. والأوامر وعلينا التنفيذ جزاك الله خيرا ...

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

أخي مصطفى, الــ console مرحلة عابرة يمر بها المبرمج أثناء تعلمه لأساسيات اللغة. أنصحك الآن بالإنتقال إلى الـ GUI حيث يمكن لبرامجك محاكاة أرض الواقع بشكل عملي.

حدد المكتبة التي تريد العمل عليها ( مثل wxWidget, Qt, GTK ... ) و يمكنني إعطاءك دروس تصل إلى مستوى متقدم في كل مكتبة (بالفرنسية طبعا !).

ملاحظة : تعلم الــ GUI يتطلب منك معرفة جيدة بالــ OOP.

هناك نقطة أخرى, لكي تبرمج لعبة Le compet est bon بشكل يرتقي إلى منافسة النسخ الموجودة منها الآن, يجب عليك التركيز على تطوير :

  • الخوارزمية و أقصد هنا دمج الــ artificial intelligence (الذكاء الإصطناعي).
  • قوانين اللعبة من خلال تطوير تحكم المستخدم في طريقة اللعبة.
  • واجهة البرنامج, بحيث تحاكي اللعبة النسخة الأصلية التي تُعرض على قناة TV5.

تم نقل الموضوع إلى قسم مشاريع الأعضاء.

أرجو لك التوفيق.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بعد إذنك أخي مصطفى, تم تغيير عنوان الموضوع ليناسب المحتوى أكثر.

تم تعديل بواسطه أحمد الشنقيطي
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

في الواقع أنا من كان يجب أن يطلب منك تغيير العنوان ... جزاك الله خيرا ...

على أي حال هل سيبقى الموضوع محصورا في ردود بيني وبينك ... أم تتوقع أن يزيد المتابعون نصف متابع أو ثلاثة أرباع متابعlaugh.gif

الحمد لله على كل حال ..

أود أن أخبرك شيئا عن الذكاء الصناعي ..

كنت أجري تجارب على إخوتي الصغار ...laugh.gif

كما هي قوانين اللعبة .. أعطيهم أرقام للانطلاق منها .. ورقم هدف ..

ثم أراقبهم كيف يحلون المسألة ... وأظن أن هذه الطريقة من أبسط طرق اكتشاف أفضل خوارزميات الذكاء الصنعي ...

وقد توصلت إلى عدة خوارزميات ...

يجب دوما أن يكون هناك subGool أي شبه هدف .. فمثلا إذا كان الهدف 31 فعلينا أن نضع أشباه أهداف مثل 25 و 6 أعداد تقبل القسمة ..

وعندها نسعى للوصول إلى 6 و 25 .. وهذا طبعا حسب الحالة التي لدينا ..

خوازمية أخرى تعتمد على البحث عن أقرب رقم يمكن الوصول إليه واعتبار الفرق شبه هدف

مثلا هدفنا 37 ولدينا 25 3 4 أقرب رقم هو 25 والفرق بينه وبين الهدف 12 نجعله شبه هدف ...

هناك الكثير من الأفكار ...

إذا أردت رأيي في الذكاء الصناعي :

اجلس مع نفسك بهدوء وفكر في الخطوات التي تتبعها لحل المسألة بيدك .. رتبها بحيث يمكن لطفل 6 سنوات اتباعها والوصول للحل ...

هذا هو الAI فما رأيك أستاذي ؟

تم تعديل بواسطه مصطفى 36a2
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .