لغز برمجي

rani_walhan

السلام عليكم جميعا

لا شك ان البرمجة هي طريقة تفكير و الخوارزمية هي طريقة حل المشكلة البرمجية

ويوجد حاليا اكثر من طريقة تفكير في البرمجة لن اسهب فيها

احببت ان اضيف الموضوع هنا وهو مساحة متواضعة لاضافة الالغاز البرمجية ويقوم الاعضاء هنا بحلها

ذلك سوف يساعد القادمين الجدد لتطوير قدراتهم و القدماء للمساعدة او لتعلم مهارات جديدة

و هذه تحية

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

خيارات عرض التعليق

اختر الطريقة التي تفضلها لعرض التعليقات، ثم اضغط على "احفظ الإعدادات" لتفعل التغيرات.
rani_walhan

اللغز الاول

سوف ابدأ بالالغاز البسيطة و السهلة

رسم مربع

و سوف انتظر الحلول على اية لغة برمجة يتقنها المشارك او حتى بلغة عادية

الشكل المطلوب رسمه

*******
*******
*******
*******
*******

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

en.karam1989

<br />
for (i = 0 ; i < rowsCount, i++)<br />
    for (j = 0 ; j < culomnsCount, j++)<br />
        print('*')<br />
    print('\n')<br />

en.karam1989

طيب اكتب تابع يرسم الشكل التالي:
**********
*********
********
*******
******
*****
******
*******
********
*********
**********
*********
********
*******
******
*****
******
*******
********
*********
**********
*********
********
*******
******
*****
******
*******
********
*********
**********
*********
********
*******
******
*****
******
*******
********
*********
**********
*********
********
*******
******
*****
******
*******
********
*********
**********

إلخ Smile

bayrn

كتب en.karam1989:
for (i = 0 ; i < rowsCount, i++) for (j = 0 ; j < culomnsCount, j++) print('*') print('\n')

Foot in mouth I think you've made out a homework

إذا عاش امرؤٌ ستين عاماً فنصف العمر تمحقه الليالي
ونصف النصف يذهب ليس يدري لغفلته يميناً من شمالِ
وباقي النصف أسقامٌ وهمٌ وشغلٌ بالمكاسبِ والعيالِ
الإمام الشافعي

en.karam1989
And I gave him another Mr. Green

إذا حولها للغة المطلوبة منو بالوظيفة .. حلال عليه Razz

rani_walhan

اهلا اكرم

سوف اعتبر اللغز الذي قمت انت بوضعه اللغز الثاني

هذا حلي بالنسبة للغز الاول

<br />
for (i=0 to rows*col)<br />
{</p>
<p>cout<< "*";<br />
if(!(i%col)) cout<<"\n";<br />
}<br />

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

انظر

نصف شكل
10**********
09*********
08********
07*******
06******
05*****

شكل كامل
06******
07*******
08********
09*********
10**********
09*********
08********
07*******
06******
05*****

بالنسبة للحل للغزك سوف اضعه و لكن يوجد لا بد حل ابسط

[code]

int l= 5
int rows = 10
int i;
int j;
int s=1;

while (true)
{

for (i=0 ; i

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

ChArLoK_16

<br />
    char s[10] = '*';<br />
    while (1)<br />
    {<br />
        foreach (i; 5..s.length)<br />
            writeln(s[0..i]);<br />
        foreach_reverse (i; 6..s.length - 1)<br />
            writeln(s[0..i]);<br />
    }<br />

ChArLoK_16.

rani_walhan

كتب bayrn:

كتب en.karam1989:
for (i = 0 ; i < rowsCount, i++) for (j = 0 ; j < culomnsCount, j++) print('*') print('\n')

Foot in mouth I think you've made out a homework

no it is not homework

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

mrabooode
try this one its fun
**********\
**********\\
**********\\\
**********\\\\
**********\\\\\
**********\\\\\\
**********\\\\\\\
**********\\\\\\\\
**********\\\\\\\\\
**********\\\\\\\\\\
__________\\\\\\\\\\
 __________\\\\\\\\\
  __________\\\\\\\\
   __________\\\\\\\
    __________\\\\\\
     __________\\\\\
      __________\\\\
       __________\\\
        __________\\
         __________\
rani_walhan

كتب ChArLoK_16:
<br />
    char s[10] = '*';<br />
    while (1)<br />
    {<br />
        foreach (i; 5..s.length)<br />
            writeln(s[0..i]);<br />
        foreach_reverse (i; 6..s.length - 1)<br />
            writeln(s[0..i]);<br />
    }<br />

سعيد بمشاركتك

ربما هي ليست لغة ولكن بسيدو كود

و تحية

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

rani_walhan

كتب mrabooode:

try this one its fun
**********\
**********\\
**********\\\
**********\\\\
**********\\\\\
**********\\\\\\
**********\\\\\\\
**********\\\\\\\\
**********\\\\\\\\\
**********\\\\\\\\\\
__________\\\\\\\\\\
 __________\\\\\\\\\
  __________\\\\\\\\
   __________\\\\\\\
    __________\\\\\\
     __________\\\\\
      __________\\\\
       __________\\\
        __________\\
         __________\

اذا هذا هو اللغز رقم 3 بالنسبة للاشكال

نعم الشكل ممتع وبسيط الى حد ما وهو ابسط من اللغز 2

لن اقوم بوضع الحل حتى اترك الفرصة للاعضاء لتجربة عمل الشكل

وتحية

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

mpcabd
كتب mrabooode:

try this one its fun
**********\
**********\\
**********\\\
**********\\\\
**********\\\\\
**********\\\\\\
**********\\\\\\\
**********\\\\\\\\
**********\\\\\\\\\
**********\\\\\\\\\\
__________\\\\\\\\\\
__________\\\\\\\\\
__________\\\\\\\\
__________\\\\\\\
__________\\\\\\
__________\\\\\
__________\\\\
__________\\\
__________\\
__________\

count = 10
print "\n".join([("\\" * i) + ("*" * count) for i in range(1, count + 1)])
print "\n".join([("\\" * i) + ("_" * count) for i in range(count, 0, -1)])
mrabooode


count = 10
print "\n".join([("\\" * i) + ("*" * count) for i in range(1, count + 1)])
print "\n".join([("\\" * i) + ("_" * count) for i in range(count, 0, -1)])

this is rejected Mr. Green

rani_walhan

كتب mpcabd:

كتب mrabooode:

try this one its fun
**********\
**********\\
**********\\\
**********\\\\
**********\\\\\
**********\\\\\\
**********\\\\\\\
**********\\\\\\\\
**********\\\\\\\\\
**********\\\\\\\\\\
__________\\\\\\\\\\
__________\\\\\\\\\
__________\\\\\\\\
__________\\\\\\\
__________\\\\\\
__________\\\\\
__________\\\\
__________\\\
__________\\
__________\

count = 10
print "\n".join([("\\" * i) + ("*" * count) for i in range(1, count + 1)])
print "\n".join([("\\" * i) + ("_" * count) for i in range(count, 0, -1)])

شكرا على احضار الحل سوف اقوم بتحليل الشكل

الشكل عبارة عن
10 صفوف * و \
10 صفوف - و \

بالنسبة للاعمدة فهي 10 بالنسبة لكل من * و -

و بالنسبة للـ \ فهو
متزايد يبدأ ب 1 ويصل 10 عندما ياتي بعد *
متناقص يبدأ ب 10 و ينتهي ب 1 بعد الــ -

10 + 1
**********\1
**********\\2
**********\\\3
**********\\\\4
**********\\\\\5
**********\\\\\\6
**********\\\\\\\7
**********\\\\\\\\8
**********\\\\\\\\\9
**********\\\\\\\\\\10
__________\\\\\\\\\\10
__________\\\\\\\\\9
__________\\\\\\\\8
__________\\\\\\\7
__________\\\\\\6
__________\\\\\5
__________\\\\4
__________\\\3
__________\\2
__________\1
10 + 1

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

rani_walhan

حسنا

لقد ابلينا بلاءا حسنا

سوف انتقل الى مرحلة اخرى في اللغز و ابتعد عن الاشكال مؤقتا

حيث قمت بالتنقل في ارجاء المنتدى و استوحيت من احد المواضيع
كتابة كود

طبعا اشكر صاحب الموضوع وان لم استطع ذكر الاسماء لكي لا اكشف اللغز

المطلوب معرفة بماذا يستخدم الكود او محاولة تحليله عكسيا لمعرفة مخرجاته

</p>
<p>int traceme(char c)<br />
{<br />
int b=2;<br />
if ((c>64)&&(c<91)) c+=32;<br />
if ((c==115)||(c==118)) b=1;<br />
if (c>120) b=1;</p>
<p>if ((c>96)&&(c<123)) c-=97;</p>
<p>if (c>22) return -1;<br />
else return (c/3)+b;<br />
}</p>
<p>

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

mrabooode

لا شكرا عندي منه الكثير Mr. Green

rani_walhan

ما هو الذي عندك منه الكثير ؟؟

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

rani_walhan

طيب بالنسبة للكود قمت بكتابته لاستقبال احرف لاتينية و تحويلها الى المكافئ لها على ازرار الخليوي

مثلا لو ادخلنا حرف s الى الاجراء

طبعا حرف الـ s موجود على الزر 7

و حرف الــ s معروف ان رقمه بشيفرة آسكي هو 115

c= 115-97
c=18

القيمة المرجعة سوف تكون

(18/3) +1
6 +1

النتيجة 7

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

وتحية

</p>
<p>int char2num(char c)<br />
{<br />
int b=2;<br />
if ((c>64)&&(c<91)) c+=32;<br />
if ((c==115)||(c==118)) b=1;<br />
if (c>120) b=1;</p>
<p>if ((c>96)&&(c<123)) c-=97;</p>
<p>if (c>22) return -1;<br />
else return (c/3)+b;<br />
}</p>
<p>

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

عروة ديرانية

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

rani_walhan

اهلا اخي عروة

نعم الالغاز تنمي القدرات البرمجية و التفكير

ولغز ابراج هانوي كما قلت ممتع

واعتقد القدرة على حل الالغاز دون الرجوع الى خوارزميات معروفة

هو الطريقة المثلى لتصميم خوارزميات جديدة

حسنا اليكم اللغز الخامس

[code]
int a

cin >> a

if (a>5) cout<

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

foaad

كتب rani_walhan:

المطلوب اعادة كتابة البرنامج البسيط السابق ليؤدي تماما نفس النتيجة بشرط عدم استخدام جمل الشرط و غير مسموح ايضا استخدام التكرار المشروط

طبعاً في كتير طرق بدون استخدام جمل شرط صراحةً (متل استخدام min أو max ...)
مثلاً ممكن نكتب:
<br />
int x = (int)(bool)(a / 6);<br />
cout << a*7 + a*5*x;<br />

قيمة x بتكون 0 في حال a أصغر من 6 وإلا 1
بس بظن (bool)(int) ممكن نعتبرها بمثابة شرط؟!

rani_walhan

اهلا اخي فؤاد

حلك صحيح مئة بالمئة

نعم قد تعتبر البول شرط

لان الشرط يحول بالنهاية الى امر cmp بالاسمبلي

وايضا يتبع الشرط نوع من انواع jmp على حسب المقارنة
je ، Jb

بينما بالطريقة التي قمت باستخدامها لا يوجد قفز و هو توفير بدورات المعالج

دعني فقط يا صديقي احاول اعادة ترتيب الكود

</p>
<p>int a</p>
<p>cin >> a</p>
<p>a = a *((12*(a>5))+(7*(a<=5)));</p>
<p>// there is no need for typecasting<br />
//a>5 will return 0 or 1</p>
<p>cout << a;</p>
<p>

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

foaad

طيب نفس السؤال السابق بس بدون استخدام تعليمات cmp, test, je, jne ... Smile
يعني بدون تعليمات شرطية نهائياً (بدون عمليات مقارنة وبدون تحويل من أو إلى bool)

EDIT:
ممكن تعتبر a من النمط unsigned int

rani_walhan

ممكن عمل ذلك باستخدام بوابات المنطق و اوامر الازاحة و الاسناد و القسمة و الضرب و باقي القسمة
وربما لن يتم استخدام كل هؤلاء في الاعلى ولكن هذه كلها اسلحة مشروعة قد تستخدم

سوف اضع حل انت اوحيت ان استخدم لـ unsigned و فضلت ان اقوم بهذا بالـ signed

لن استخدم القسمة ولا باقي القسمة واللتان تاخذان دورات عديدة من المعالج مقارنة بدورة واحدة ل and

ببساطة سوف استخدم الطرح والضرب و الازاحة و بوابة and و not

ان طرحت 6 من الرقم
واذا كانت النتيجة سالبة سوف يكون اول بت في الرقم 1
ويمكن الحصول على 1 عن طريق قناع بوابة and
و ثم ازاحة الى اليسار shl
و ضرب الازاحة بالرقم و ب 7
وضرب عكس نتيجة الازاحة بالرقم و ب 12 و جمع القيمتين

سوف اكتبها بالبسيدو كود

<br />
b = a -6<br />
// >= 5</p>
<p>c = 1 shl 31</p>
<p>d = b and c</p>
<p>e = d shr 31</p>
<p>er = not e</p>
<p>a = (a*7*e)+(a*12*er);</p>
<p>

و سوف اضع لغز جديد قريبا

وتحية

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

foaad

كتب rani_walhan:

واذا كانت النتيجة سالبة سوف يكون اول بت في الرقم 1

فكرة حلوة.
بس يمكن في غلط صغير بالكود...
قيمة e هي إما 1 أو 0 وبالتالي er رح تصير إما -2 أو -1 (أو رقم موجب كبير حسب اللغة) إلا اذا كان المقصود عملية not المنطقية بس بهالحالة منكون استخدمنا التحويل من وإلى bool
بس ممكن نحسب er بغير طريقة:
<br />
er=1-e<br />

الطريقة الي خطرتلي (في حال كانت a من نوع unsigned int) هي الطريقة المعروفة للتخلص من التعليمات الشرطية, استخدام جدول (مصفوفة)

<br />
arr[0]=7<br />
arr[1]=7<br />
...<br />
arr[5]=7<br />
arr[6]=12<br />
arr[7]=12<br />
...<br />

المشكلة هون انو عدد الاحتمالات كبير 2^32
بس ممكن نختصرهم باستخدام تابع تقطيع مناسب
مثلاً باستخدام القسمة الصحيحة والازاحة وعملية AND
<br />
int x = (a / 6);<br />
x = (x & 1)+(x>>1 & 1)+(x>>2 & 1)+...+(x>>31 & 1);</p>
<p>int arr[31] = {7, 12, 12, ..., 12};</p>
<p>cout << a * arr[x];<br />

sh_que_L

عند المقارنة يقوم المعالج بعملية طرح

b = a - 6

الازاحة تكفي لمعرفة الاشارة

e = b shr 31

نستفيد من النتيجة لتوليد الرقم المناسب قبل الجداء

a = (12 - e * 5) * a

لا جديد في حلي، سوى بعض الاختصار
شكرا

rani_walhan

طريقتك ايضا صحيحة

نعم يوجد خطأ باستخدام not
كان يجب استخدام xor

er = 1 xor e

و لكن ايضا استخدام 1- حل جيد

وممكن استخدام المصفوفة كما قلت

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

كتب foaad:
كتب rani_walhan:

واذا كانت النتيجة سالبة سوف يكون اول بت في الرقم 1

فكرة حلوة.
بس يمكن في غلط صغير بالكود...
قيمة e هي إما 1 أو 0 وبالتالي er رح تصير إما -2 أو -1 (أو رقم موجب كبير حسب اللغة) إلا اذا كان المقصود عملية not المنطقية بس بهالحالة منكون استخدمنا التحويل من وإلى bool
بس ممكن نحسب er بغير طريقة:
<br />
er=1-e<br />

الطريقة الي خطرتلي (في حال كانت a من نوع unsigned int) هي الطريقة المعروفة للتخلص من التعليمات الشرطية, استخدام جدول (مصفوفة)

<br />
arr[0]=7<br />
arr[1]=7<br />
...<br />
arr[5]=7<br />
arr[6]=12<br />
arr[7]=12<br />
...<br />

المشكلة هون انو عدد الاحتمالات كبير 2^32
بس ممكن نختصرهم باستخدام تابع تقطيع مناسب
مثلاً باستخدام القسمة الصحيحة والازاحة وعملية AND
<br />
int x = (a / 6);<br />
x = (x & 1)+(x>>1 & 1)+(x>>2 & 1)+...+(x>>31 & 1);</p>
<p>int arr[31] = {7, 12, 12, ..., 12};</p>
<p>cout << a * arr[x];<br />

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

rani_walhan

الحل صحيح مئة بالمئة ومختصر

قمت بالاستفادة من الجمع الرقمين واولوية الضرب على الطرح

الحل مثالي

تقبل تحيتي

كتب sh_que_L:
عند المقارنة يقوم المعالج بعملية طرح

b = a - 6

الازاحة تكفي لمعرفة الاشارة

e = b shr 31

نستفيد من النتيجة لتوليد الرقم المناسب قبل الجداء

a = (12 - e * 5) * a

لا جديد في حلي، سوى بعض الاختصار
شكرا

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

rani_walhan

اللغز الجديد

كيفية فحص رقم معين لاكتشاف ان كان عبارة عن 2 مرفوعة لاس

اي 2^x

او

a ?? = 2^x

قيمة x غير مطلوبة و لكن اعادة true او flase

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

sh_que_L

نتيجة رفع ٢ لأس ما، بحسب نظام العد الثنائي
هو عدد يحوي أصفارا في جميع خاناته باستثناء خانة واحدة
(وهي الخانة المقابلة لقيمة الأس!!).

وبالتالي يكفي التأكد من أن هناك خانة واحدة، وواحدة فقط، تحوي ١

برمجيا
معالجات Intel مثلا
نجد تعليمتين لمسح خانات رقم ما، وهي تًرجع موقع أول خانة تحوي ١
التعليمة الأولى تبدآ من الخانة الأصغر، وتدعي BSF (مسح خانة أمامي Bit Scan Forward)
والثانية تبدأ من الخانة الأكبر، وتدعى BSR (مسح خانة عكسي Bit Scan Reverse)
فيكفي أن تتساوى قيمة المسح الأمامي مع قيمة المسح الخلفي.

أما باستخدام لغة ما كـ Pascal، فربما تصلح العبارات التالية

//Scan bits one by one
iBit := 2;
i := 1;	//0..31
cnt := 0;
while (cnt<2) and (i<32) do
begin
	if ((Num1 and iBit)>1) then
		cnt:=cnt+1;

	//Next bit
	iBit:=iBit*2;
	inc(i);
end;
Result := (cnt=1);
rani_walhan

الباسكال لغة قوية جدا و تحول بالكامل الى لغة الآلة

الحل صحيح مئة بالمئة

ولانه يوجد اكثر من طريقة دائما لحل مسالة برمجية فانتظر حلول اخرى

وسوف اضع الحل المثالي يوم الاثنين

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

rani_walhan

صباح الخير

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

لذلك ارتأيت ان اضع الحل

<br />
int powr2(int a)<br />
{<br />
if (a=) 0 a++;</p>
<p>return ! (a && (a-1));<br />
}<br />

جميع الارقام التي هي عبارة عن 2 مرفوع الى رقم في حال عمل بوابة و
and بينها وبين نفسها -1 سوف تكون النتيجة صفر
غير ذلك سوف تكون النتيجه اكبر من صفر

وتحية

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

عروة ديرانية

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

أما بالنسبة لشفرة بلغة سي تنفذ العمل بكفاءة معقولة فأنا أقترح هذه الشفرات فهي لا تستخدم الطرح، فالطرح عملية مكلفة مقارنة بالإزاحة البتية:

for (int i = 0; number; number >>= 1, ++i) ;

أو:

int i = 0; while (number) {
      number >>= 1; ++i;
}

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

mrabooode

كتب عروة ديرانية:
أعجبني من المشارك السابق معرفته بطقم تعليمات المعالج

ممكن تقلي وين استعملون ؟

mrabooode

كتب عروة ديرانية:

for (int i = 0; number; number >>= 1, ++i) ;

.

و ممكن بعد اذنك تقول ليش هيك شكل حلقة ال FOR??

en.karam1989

يمكن ما قصدو عن آخر مشاركة Smile
قصدو عن اللي قبلون

sh_que_L

عذرا لعدم ذكر الحل سابقا, اكتفيت بالإشارة إلى الامكانية فقط واعتبرته حلا غير مناسب لخصوصيته. عموما إليكم الحل باستخدام لغة الآلة ضمن بيئة Delphi و النتيجة قيمة منطقية Boolean

function pwr2(num:cardinal):boolean;
label returnFalse, returnTrue, done;
asm
bsf ebx, num
bsr ecx, num
cmp ebx, ecx
je returnTrue
returnFalse:
mov @Result, 0
jmp done
returnTrue:
mov @Result, 1
done:
end;

برأيي, الحل المثالي لهذا اللغز هو حل rani_walhan, مع تمنياتي له بالتوفيق. لنقم بتعديل بسيط على هذا اللغز. ماذا لو أردنا معرفة قيمة الأس؟ بتعديل بسيط على حلي السابق نحصل على حل مناسب لمعالجات Intel و النتيجة رقم من 0 إلى 31 إذا كان العدد محققا لشرط المسألة, وفي خلاف ذلك, فالنتيجة ستكون ناقص واحد.

function pwr2value(num:cardinal):integer;
label returnFalse, returnValue, done;
asm
bsf ebx, num
bsr ecx, num
cmp ebx, ecx
je returnValue
returnFalse:
mov @Result, -1
jmp done
returnValue:
mov @Result, ebx
done:
end;

أنا غير راض عن هذا الحل. ما هي حلولكم؟

عروة ديرانية

أتمنى أنا أيضًا التوفيق لصاحبنا راني وأود أن أضيف بعض الأشياء.

فأما عن شفرتي غريبة الشكل:

for (int i = 0; number; number >>= 1, ++i) ;

فتشرحها الشفرة الثانية. وباختصار فعبارة for عبارة من ثلاث مقاطع، المقطع الأول هو شفرة التمهيد (int i = 0) والمقطع الثاني هو شرط المتابعة (number) والمقطع الثالث هو العبارة التكرارية (number >>= 1, ++i) وفي حالتنا هنا ففي المقطع الأول نعرف متغيرًا ونمهده إلى الصفر وهو المتغير الذي سيحوي في نهاية المطاف رقم الأس الثنائي. وفي المقطع الثاني لا تجد إلا اسم متغير هو number وهذا في لغة السي والسي المحسنة يترجم إلى "number لا تساوي الصفر"، أما في العبارة التكرارية فنفعل شيئين وهما أننا نأكل بتًا من ذيل الرقم number (ندفشه إلى اليمين بمقدار بت واحد فيخسر بتًا من ذيله، فإن كان في السابق واحدًا فإنه يصير صفرًا) ونزيد قيمة العداد i. أما جسم الحلقة فهو فارغ وهو سبب ما تراه من أن عبارة for متلوة بفاصلة منقوطة لا يسبقها أي شيء.

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

	unsigned int index, range;
	__asm {
		push eax         ; save values of eax and ebx in memory by pushing them on
		push ebx         ;  the stack. The point here is not to disturb their values
		                 ;  since they may be already in usage by the compiler.
		
		; actual code begins
		
		bsf eax, number  ; store the position of least and the most significant 1's
		bsr ebx, number  ;  of 'number' in eax and ebx, respectively.
		
		sub ebx, eax     ; subtract indeces (zero if equal)
		
		mov range, ebx   ; store the non-negative difference in 'range'.
		mov index, eax   ; store the position of the least-significant bit in 'index'.
		
		; actual code ends
		
		pop ebx          ; restore the original values of eax and ebx, note that since
		pop eax          ;  the stack is a first-in-first-out structure that they are
		                 ;  popped in reversed order.
	}

ما تفعله هذه الشفرة هو أنها تخزن رقم البت المعلم (الذي قيمته واحد) الأول الذي تصادفه تعليمة المسح البتي في index وتخزن عرض البتات المعلمة (الفرق بين مكان أول واحد ومكان آخر واحد) في range. وكل ما يلزمه الأمر بعد هذا المقطع هو أن نتأكد من أن هذا العرض صفر (أي أنه يوجد بت واحد معلم في الرقم number) وحينها تكون النتيجة جاهزة في index، والذي هو الآن ليس رقم البت المعلم الأول بل رقم البت المعلم الوحيد في الرقم number.

عروة ديرانية

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

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

عروة ديرانية

عفوًا على كثرة الردود لكن لا يبدو أنني أفهم كيف تعمل شفرة الأخ راني، فكل ما تصنعه هو أنها تجمع بين الرقم والرقم السابق له بعلاقة AND. طيب لو كان عندنا الرقم ١٠١٠٠ فطرحنا منه واحد يصير عندنا ١٠٠١١ فإن جمعنا هذا المقدار مع أخيه لا يكون عندنا إلا صفر، نعكس ذلك فيعطينا "صحيح" لكن الرقم ليس قوة صافية للرقم اثنين. إن أمكن أن تشرحوا لي شكرتكم فلا يبدو أنني أفهم الشفرة.

foaad

بظن استخدام BSF و BSR أسرع من استخدام الطرح
بس بما انو أغلب لغات البرمجة ما بتأمن تعليمات مقابلة وفرق الأداء كتير بسيط فالحل المثالي هو استخدام الطرح

كتب عروة ديرانية:
عفوًا على كثرة الردود لكن لا يبدو أنني أفهم كيف تعمل شفرة الأخ راني، فكل ما تصنعه هو أنها تجمع بين الرقم والرقم السابق له بعلاقة AND. طيب لو كان عندنا الرقم ١٠١٠٠ فطرحنا منه واحد يصير عندنا ١٠٠١١ فإن جمعنا هذا المقدار مع أخيه لا يكون عندنا إلا صفر، نعكس ذلك فيعطينا "صحيح" لكن الرقم ليس قوة صافية للرقم اثنين. إن أمكن أن تشرحوا لي شكرتكم فلا يبدو أنني أفهم الشفرة.

في مثالك نتيجة الجمع المنطقي ستكون 10000 وليست 0

1000
0100
1100
0010
1010
0110
1110
0001

لايوجد تقاطع بين واحدات رقم وواحدات الرقم الذي يسبقه فقط في حالة الانتقال إلى خانة جديدة أي الرقم هو قوة لـ 2

عروة ديرانية

صحيح، يبدو أن عقلي متلخبط هذا اليوم.

أما بالنسبة للطرح والتعليمتين فلا، عملية طرح واحدة أسرع من دون نقاش من تعليمتي مسح بتيتين، هذا ويضاف إلى ذلك أنني أستخدم عملية طرح بالإضافة إلى تعليمتي المسح البتيتين. لذا فشفرته أسرع بالطبع إن كان الهدف هو اكتشاف ما إذا كان الرقم الممرر قوة من قوى الاثنين، أما إن رغبنا بمعرفة منزلة الأس فأنا أظن بأن استخدام تعليمات المسح البتية يصير عندها خيارًا جيدًا، لكن بطريقة مختلفة عن الشفرة أعلاه، وتحديدًا باستخدام طريقة الأخ راني لاكتشاف ما إذا كان الرقم واحدًا من قوى الرقم اثنين (number && (number - 1 ثم باستخدام تعليمة مسح بتي واحدة لاكتشاف موضع البت المعلم، بهذه الطريقة نستخدم تعليمتين فيهما حمل (carry-propagating instructions) مقارنة بثلاثة، أما الجمع المنطقي فهو عملية بتية تنفذ في وقت ثابت.

rani_walhan

مساء الخير جميعا

سعيد جدا بالتفاعل الذي لقيه اللغز الاخير

و اشكر الجميع وسوف ارد على جميع المشاركات تباعا

بالنسبة لحل اكتشاف اذا كان الرقم من مضاعفات 2

الارقام هي 2 مرفوع لرقم و الرقم الذي قبلها لا تشترك بوجود 1 في نفس المنزلة مما ينتج 0 عند استخدام بوابة and
بينما يتقاطع الرقم الذي ليس 2 مرفوع لرقم بالضرورة مع الرقم الذي قبله فتكون نتيجة بوابة and رقما اكبر من 0

سوف آخذ امثلة

فرضا كان 16

ان عملية وضع بوابة and بين الرقمين سوف تنتج 0 بالضرورة

لان 16 هو رقم 2 مرفوع الى رقم

مثال آخر رقم 77 الذي قبله هو 76
وهما الرقمين الثنائيين
1001101
1001100

استخدام بوابة and سوف تنتج الرقم 76

و بالنسبة الى اكتشاف العدد المرفوع اليه او log2 للرقم لم افكر فيه بعد

لكن ربما سوف استخدم and و ror في تكرار يبدأ بآخر خانة وينتهي عند اكتشاف 1 لاول مرة

اتصحو من غرورك والجماح = اذا ما الشمس تافل للرواح

mrabooode

شكرا عروة عالشرح Smile

foaad

كتب عروة ديرانية:

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

على ما يبدو تعليمات BSF و BSR بمعالجات x86 هنن مجرد macro (مافي دارة فيزيائية لتنفيذ التعليمة)
حسب ما فهمت من هون:
http://home.comcast.net/~fbui/intel_b.html#bsf
تعليمة BSF بحاجة بين 6 و 42 دورة معالج (في حال التعامل مع المسجلات فقط)
وبالتالي ممكن نستخدم حلقة أو مجموعة تعليمات بلغة برمجة عالية المستوى بدل تعليمة BSF بدون مانخسر كتير من الأداء.
بينما تعليمات SUB و AND (نفس عدد الدورات!)
http://home.comcast.net/~fbui/intel_s.html#sub
http://home.comcast.net/~fbui/intel_a.html#and
بحاجة لدورة معالج وحدة (في حال التعامل مع المسجلات فقط)

عروة ديرانية

لقد اقتنعت وأعجبني حل التنقيص number && (number - 1) لاكتشاف ما إذا كان الرقم قوة من قوى الرقم اثنين، إنه ذكي وسريع.

ولفت انتباهي كثيرًا ما قلته يا فؤاد عن أن تعليمات المسح البتية بطيئة وأنها في الأغلب تنفذ بطريقة ماكروية داخل المعالج، والمعلومات الموجودة في الموقع الذي أشرت إليه تعاضد ذلك. ومع أنني ظننت لسنين بأن شركة إنتل لم تعد تعطي أية معلومات عن الكلفة التقريبية للتعليمات المختلفة في المعالج نتيجة انتقالها في معالجاتها لاستخدام ذواكر تخبئة متطورة ووحدات تنفيذ تعيد ترتيب تعليمات البرنامج لتوظيف موارد المعالج بشكل مكثف (وهو ما ينتج عنه أن عدد الدورات اللازمة لتنفيذ التعليمات يصير حسابه شبه مستحيل) فقد راجعت للتو المجلد الأخير من مستندات المعالج ("Intel® 64 and IA-32 Architectures - Optimization Reference Manual") ووجدت أنه أضيف إليه ملحق ("Appendix C") فيه جدول، وفي هذا الجدول يذكر بأن تعليمة طرح الأعداد الصحيحة SUB يستغرق تنفيذها تقريبًا دورة واحدة في معالجات إنتل الحديثة في حين يستغرق تنفيذ تعليمات المسح البتي ست عشرة دورة! وهذا بنظري يدق المسمار الأخير في نعش طريقة استخدام تعليمة معالج لحل مثل هذا المشكلة (أو بالأحرى في جدوى مثل هذا الصنع).

لقد مررت أثناء دراستي على أن معالجات إنتل لو أنها صممت من الصفر اليوم أنها كانت لتتخلى عن الكثير من التعليمات المعقدة وتنتقل من معمارية CISC(*) إلى معمارية RISC(**)، وأنها في صورتها الحالية صارت معالجاتٍ بقلب RISC ينفذ التعليمات المعقدة مثل تعليمات النصوص (وهي تشبه تعليمات المسح البتي من حيث التعقيد) بطريقة ماكروية.

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

(**) RISC: آلة بطقم تعليمات بسيطة، وهي الآلات التي تلتصق بتعليمات بسيطة جدًا وتعتمد على البرامج في عمل كل ما يتطلب تكرارًا أو ما شابه، والغرض من ذلك تبسيط قلب المعالج وتسهيل تسريعه (بآليات مثل تكرار الدوائر على الرقاقة replication والمعاقبة في الدائرة الواحدةpipelining والتنفيذ غير التسلسلي out-of-order execution of instructions).

النتيجة: طريقة الأخ راني في اكتشاف ما إذا كان الرقم واحدًا من قوى الرقم اثنين number && (number - 1) هي الطريقة الأكفأ والأكثر قابلية للتحويل إلى شفرة لغة عالية المستوى. والدرس الذي استخلصناه اليوم هو عدم التسرع باستخدام تعليمات معالج معقدة والافتراض بأن استخدامها سيكون مجديًا إذ يبدو أن استخدام هذه التعليمات ما عاد منصوحًا به اليوم وصار من الأنسب استخدام التعليمات البسيطة، وهو يوافق ما عليه المصرفات (Compilers) الحديثة.

sh_que_L

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

كتب Pierre le Riche:

> BSF/BSR were very slow on older processors. On P4's and Athlon's they are
> now quite efficient.

I did some digging. Here are the timings (latency in clock cycles first,
followed by the throughput in instructions per clock cycle):

Pentium 3: 2(1) for both bsr and bsf
Pentium 4 Willamette and Northwood: 8(0.5) for both bsr and bsf
Pentium 4 Prescott: 16(0.5) for both bsr and bsf
Athlon/Athlon XP: 9(1/8) for bsr, 7(1/7) for bsf
Athlon 64: 11(1/10) for bsr, 8(1/8) for bsf

ما رأيكم؟

و من هذه الصفحة

كتب Matt:

BSR is 2 cycles on P6-core CPUs for the reg-reg form.

عروة ديرانية

مثير للاهتمام. شو رأيك نعمل تجارب؟ ممكن نشوف كم تعليمة مسح بتي أمامي نستطيع التنفيذ في الدقيقة على كلمات عشوائية من أربعة بايتات وكم حلقة مكافئة نستطيع التنفيذ في الدقيقة على كلمات عشوائية Very Happy. وحتى نتأكد من مزعم السيد مات فمن الممكن أن نجرب تعليمة المسح البتي بين مسجلات المعالج بالإضافة إلى تجريبها بين مسجل وعنوان ذاكرة.

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

عروة ديرانية

الشفرة الاختبارية:
- المقطع الأول:

gettimeofday(&time_start, NULL);
for (i = 0; i < iterations; ++i) {
    number = rand();
    __asm {
        mov edx, number
        bsf edx, edx
    }
}
gettimeofday(&time_end, NULL);

- المقطع الثاني:

gettimeofday(&time_start, NULL);
for (i = 0; i < iterations; ++i) {
    number = rand();
    for (j = 0; number; number >>= 1, ++j) ;
}
gettimeofday(&time_end, NULL);

النتائج:
- تشغيلة واحد:

Using the bit scan instruction it is possible to execute 81.8 million iterations per second.
Using a loop it is possible to execute 9.8 million iterations per second.

- تشغيلة إثنان:

Using the bit scan instruction it is possible to execute 82.8 million iterations per second.
Using a loop it is possible to execute 9.4 million iterations per second.

بعض التعديلات:
١. باستخدام إصدارة المسجل-ذاكرة من تعليمة المسح البتي (bsf edx, number) بدل إصدارة المسجل-مسجل (bsf edx, edx) وإزالة تعليمة النقل (mov edx, number):

Using the bit scan instruction it is possible to execute 82.7 million iterations per second.
Using a loop it is possible to execute 9.9 million iterations per second.

(لا فرق يذكر)

٢. باستخدام رقم ثابت بدل رقم مولد عشوائيًا (number = 0x0080):

Using the bit scan instruction it is possible to execute 325.2 million iterations per second.
Using a loop it is possible to execute 46.3 million iterations per second.

Using the bit scan instruction it is possible to execute 327.9 million iterations per second.
Using a loop it is possible to execute 47.5 million iterations per second.

(ونلاحظ أن الأرقام زادت لأن الوقت المهدر في توليد الرقم العشوائي لم يعد موجودًا لكن النسبة ما تزال قريبة)

foaad

كتب sh_que_L:
لست مقتنعا بصحة امكانية استبدال BSF بحلقة تعطي نفس الأداء تقريبا.

هو أنا قلت:
كتب foaad:

وبالتالي ممكن نستخدم حلقة أو مجموعة تعليمات بلغة برمجة عالية المستوى بدل تعليمة BSF بدون مانخسر كتير من الأداء.

أكيد الحلقة مكلفة من ناحية الأداء بسبب تعليمات القفز (بس مع ذلك أنا بفضل استخدمها اذا كان فرق الأداء بسيط)
وبالنسبة لمجموعة التعليمات قصدي مجموعة تعليمات مكافئة للحلقة (Loop unrolling) بهالحالة بظن ممكن نحصل على أداء مكافئ لتعليمة BSF

جربت الكود الي كتبه عروة ديرانية وطلع معي نتائج غريبة شوي:
على اللابتوب (معالج AMD Turion X2)

<br />
Using the bit scan instruction it is possible to execute 22.73 million iterations per second.<br />
Using a loop it is possible to execute 17.09 million iterations per second.<br />

على الديسكتوب (معالج AMD Athlon X2)
<br />
Using the bit scan instruction it is possible to execute 12.90 million iterations per second.<br />
Using a loop it is possible to execute 10.37 million iterations per second.<br />

فرق الأداء قليل (24%, 33%) مقارنة مع نتائج تجارب عروة (834%)!
شكيت انو السبب هو معالجات AMD فجربت على لابتوب بمعالج Intel C2D
<br />
Using the bit scan instruction it is possible to execute 32.54 million iterations per second.<br />
Using a loop it is possible to execute 19.85 million iterations per second.<br />

فرق الأداء أكبر (64%) بس إلى حد ما مقبول ولسا بعيد عن 800%!
بجوز الاختلاف الكبير بالنتائج هو بسبب الـcompiler
أنا استخدمت Microsoft C++ Compiler (VS 2008)‎ مع تفعيل الـ optimization

آخر شي جربت التخلص من الحلقة:

<br />
for (i = 0; i < iterations; ++i) {<br />
	number = rand();<br />
	number >>= 1;<br />
	j = j + (number>0);<br />
	number >>= 1;<br />
	j = j + (number>0);<br />
	...<br />
	...<br />
	number >>= 1;<br />
	j = j + (number>0);<br />
}<br />

وطلع معي النتائج:
<br />
TurionX2: 26.164 m.i/s<br />
AthlonX2: 13.56 m.i/s<br />
C2D: 33.05 m.i/s<br />

يعني بالحالات التلاتة كان الأداء أفضل! من BSF بنسبة بين 2% و 14%
مابعرف بجوز يكون في شي غلط أو في شي أنا ما انتبهتله هي ملف الكود الي استخدمته.