کدنویسی با و بدون تابع بازگشتی - شرح - سی و پاسکال

draria

Member
سوال در مورد ساختمان داده

چطوری میشه یه برنامه رو به صورت بازگشتی نوشت :
کد:
var x:integer
function fact (n:integer )
var i,f:integer;
begin
f:=1
for i"=1 to n do
f:=f*i
fact1:=f;
end

بازگشتی
کد:
function fact (n:integer)
begin
if n<=1 then fact:=1
else fact:=n*fact (n-1)
end

begin
readln(x)
writeln(fact1(x));
end


این برنامه رو از کتاب ساختما داده از انتشارات گسترش علوم پایه نوشتم که ماله حمید مقسمی هست
چطوری برنامه اول رو تبدیل به بازگشتی کرده ؟ یعنی دقیق باید چیکار کرد تا برنامه بازگشتی بشه
 

the_king

مدیرکل انجمن
اولین شرط تبدیل به الگوریتم بازگشتی اینه که مقدار خروجی با پارامتر n به مقدار خروجی با پارامتر قبلی وابسته باشه،
مثلا در الگوریتم محاسبه فاکتوریل، می دانیم که مقدار فاکتوریل n از ضرب مقدار n در فاکتوریل n-1 بدست میاد.

بدیهی است که در حالت استثنا باید فاکتوریل 0 را یک در نظر بگیریم و آنرا به فاکتوریل n-1 (عدد 1-) ارتباط ندهیم.


کاری که شما باید انجام بدهید، شبیه اثبات مسائل ریاضی به روش استقراء می باشد و شامل دو مرحله است :

1) در تابع بازگشتی تان یک شرط اولیه قرار دهید که اگر مقدار پارامتر ورودی یک مقدار حداقل ثابت باشد، خروجی تابع
را به شکل از قبل محاسبه شده و ثابت برگردانید.
مثلا در مساله محاسبه فاکتوریل، فاکتوریل عدد 0 برابر عدد یک است و باید بطور ثابت مقدارش برگردانده شود :

کد:
int fact (int n)
{
	if (n==0)
		return 1;

2) در ادامه کد تابع، باید با استفاده از مقدار بازگشتی تابع برای n-1، مقدار را برای n محاسبه کنیم.
مثلا در مساله محاسبه فاکتوریل، محاسبه فاکتوریل n ای که از 0 بزرگتر است را بر حسب فاکتوریل n-1 محاسبه می کنیم :

کد:
	return n * fact(n-1);
}

دقت کنید که ما فرض می کنیم که مقدار (fact(n-1 به درستی محاسبه شده و مقدارش را در اختیار داریم.
بدیهی است که اگر صورت الگوریتم به نحوی باشد که مقدار برای پارامتر n ربطی به مقدار پارامتر n-1 نداشته باشد،
نمی توان از روش بازگشتی استفاده کرد.

البته در مثال ما و الگوریتم فاکتوریل مقدار قبلی n-1 است ، ولی همیشه این مقدار قبلی n-1 نیست،
در بعضی الگوریتم ها n/2 یا (log(n یا هر مقدار دیگری ممکن است مورد استفاده قرار گیرد.
 

saalek110

Well-Known Member
پست اول به زبان پاسکال است و کدهای استاد عزیز the_king به زبان سی.

جهت رفع ابهام این پست را زدم.

====================
تفاوتها:

شرط:
سی:
کد:
if (n==0)
DoSomething
پاسکال:
کد:
if X = 20 then 
  DoSomething

تابع:
سی:
کد:
int fact (int n)
پاسکال:
کد:
function fact (n:integer ) : integer

بازگشت دادن از تابع:
در سی با return
در پاسکال با ریختن در کلمه ای هم نام تابع.

به نقل از:
http://www.hkbu.edu.hk/~bba_ism/ISM2110/pas046.htm
کد:
function  ADD_TWO ( value1, value2 : integer ) : integer;
	begin
	     ADD_TWO := value1 + value2
	end;
به تفاوت مهم زیر توجه کنید. چون می تواند نوشتن تابع بازگشتی در سی و پاسکال را متفاوت کند:

در سی با آمدن کلمه return بلافاصله تابع بازگشت می کند و کدهای بعد آن اجرا نمی شود ولی در پاسکال کدهای بعد کد ارسال خروجی اجرا می شود.
 

draria

Member
در کل ممنون اگر روشنم کنید که چطوری هر ساختمان داده ای رو باید باز گشتی نوشت . البته منظورم پاسکال هست . من هرچی تلاش کردم نفهمیدم یه برنامه چطوری بازگشتی میبشه
 

the_king

مدیرکل انجمن
در کل ممنون اگر روشنم کنید که چطوری هر ساختمان داده ای رو باید باز گشتی نوشت . البته منظورم پاسکال هست . من هرچی تلاش کردم نفهمیدم یه برنامه چطوری بازگشتی میبشه

اتفاقا هر ساختمان داده ای رو نمیشه بازگشتی نوشت. کلا مساله باید یک ویژگی اساسی رو داشته باشه که بتونید
بصورت بازگشتی بنویسید :

باید قابل تقسیم کردن باشه، مثلا وقتی شما می خواهید میانگین 10 عنصر رو پیدا کنید، می توانید مساله را
به دو لیست 5 عنصری بشکنید، میانگین را در هر دو لیست بدست آورید و بعد میانگین بین دو مقدار را محاسبه کنید :

کد:
(a + b + c + d + e + f + g + h + i + j) / 10

((a + b + c + d + e) / 5 + (f + g + h + i + j) / 5) / 2
 

najmeh_67_2007

New Member
سوال درباره توابع بازگشتی

چطور می تونم برنامه جستجو در یک آرایه رو به صورت بازگشتی در C++ بنویسم؟:sad:
 
آخرین ویرایش:

saalek110

Well-Known Member
جناب najmeh تاپیک شما با تاپیک فوق ادغام شد.
از توضیحات بالا استفاده کنید و در گوگل جستجو کنید. اگر پیدا نکردید سئوال خود را تکرار کنید. من در پستهای بعدی میان بحث شما چند پست می زنم.
 

saalek110

Well-Known Member
اولا من فکر می کنم در خیلی از موارد نیازی به تابع بازگشتی نباشد.
چون خیلی محاسبات تابع بازگشتی به ذهن فشار می آورد به نظرم بهتر است ازش استفاده نشه.
البته قضیه دانشگاه فرق دارد و باید با روال آنها پیش رفت.

میشه از حلقه به راحتی جایگزین تابع بازگشتی استفاده کرد. و یا یک تابع ساده هم داخل حلقه حداکثر صدا زده بشه. هر کس هم که نظر مخالف داره بگه. من ذهنی این نظر را دادم و نه با امتحان با کد.

----------------------------------------------
اما تابع بازگشتی:
برای فهم ساده تر ساختار تابع بازگشتی من پیشنهاد می کنم تابع به 3 قسمت تقسیم بشه.

قسمت اول قسمت بالایی.
قسمت میانی.
قسمت سوم قسمت پایینی.

در قسمت اول کدها موقع رفت تابع اجرا می شوند.
در قسمت میانی تابع خودش را صدا می زند و همچنین شرط صدا زدن در این قسمت قرار می گیرد. اگر شرط متغیری را بررسی می کند. کاهش یا افزایش آن متغیر هم جزو قسمت میانی حساب میشه.
در قسمت سوم کدها موقع بازگشت اجرا می شوند.

نکته مهم : باید حواسمان باشد که تابع تا ابد خود را صدا نزند. پس شرط صدا زدن خود تابع باید به دقت تنظیم بشود.


==================================
من عبارت زیر را در گوگل سرچ کردم:
کد:
"c programming"+"recursive function"

و اولین سایتی که جلب توجه می کنه همان ویکپدیا است:
http://en.wikipedia.org/wiki/Recursion_(computer_science)

با عناوین زیر:
کد:
1 Recursive algorithms
2 Recursive programming 
2.1 Examples of recursively defined procedures (generative recursion) 
2.1.1 Factorial
2.1.2 Fibonacci
2.1.3 Greatest common divisor
2.1.4 Towers of Hanoi
2.1.5 Binary search
2.2 Recursive data structures (structural recursion) 
2.2.1 Linked lists
2.2.2 Binary trees
2.3 Recursion versus iteration
3 Tail-recursive functions
4 Order of function calling 
4.1 Function 1
4.2 Function 2 with swapped lines
5 Direct and indirect recursion
6 See also
7 External links
8 References

===========================
و بعد به سایت زیر می رسیم:
http://www.cprogramming.com/tutorial/lesson16.html

که با کد شرح داده.
اولش این مقدمه را نوشته:
Recursion is a programming technique that allows the programmer to express operations in terms of themselves. In C++, this takes the form of a function that calls itself. A useful way to think of recursive functions is to imagine them as a process being performed where one of the instructions is to "repeat the process". This makes it sound very similar to a loop because it repeats the same code, and in some ways it is similar to looping. On the other hand, recursion makes it easier to express ideas in which the result of the recursive call is necessary to complete the task. Of course, it must be possible for the "process" to sometimes be completed without the recursive call. One simple example is the idea of building a wall that is ten feet high; if I want to build a ten foot high wall, then I will first build a 9 foot high wall, and then add an extra foot of bricks. Conceptually, this is like saying the "build wall" function takes a height and if that height is greater than one, first calls itself to build a lower wall, and then adds one a foot of bricks.

ساده ترین حالت اینه:
کد:
void recurse()
{
  recurse(); //Function calls itself
}

int main()
{
  recurse(); //Sets off the recursion
}
یعنی تابع با مین صدا شده و بعد تابع خودش را صدا زده.
البته برنامه بالا کراش می کنه. و قابل استفاده نیست.

برنامه بعدی اینه:
کد:
void doll ( int size )
{
  if ( size == 0 )   // No doll can be smaller than 1 atom (10^0==1) so doesn't call itself
    return;          // Return does not have to return something, it can be used
                     //  to exit a function
  doll ( size - 1 ); // Decrements the size variable so the next doll will be smaller.
}
int main()
{
  doll ( 10 ); //Starts off with a large doll (its a logarithmic scale)
}
اینجا شرط توقف صدا زدن گویا هست و تابع بازگشتی هم وقتی size - 1 را ارسال می کنه به بعدی نوعی تفریق است.

=========================

شرح تابع فاکتوریل با تابع بازگشتی در اینجا:
http://cprogramminglanguage.net/c-recursive-function.aspx
 

saalek110

Well-Known Member
منبع:
http://forum.persiantools.com/t32403-page19.html

ساده ترين تابع بازگشتي كه فكر كنم بشه نوشت اين باشه.
کد:
#include <iostream.h>
#include <conio.h>

void my_function(int a);

void main(void)
{
  clrscr();
	    my_function(7);  //ehzare tabeye_baz gashti
     getch();
}

void my_function(int a)  
{
		  cout<<"a="<<a<<endl;
	    a=a-1;
		 if (a>0)  my_function(a);
}
تابع بازگشتي ، تابعي است كه خود را صدا مي كند. طبيعي است كه بايستي شرطي بگذاريم كه تا ابد ، اين صدا كردن ادامه نداشته باشه.
 

saalek110

Well-Known Member
باز هم از :
http://forum.persiantools.com/t32403-page19.html

در برنامه زير چگونگي رفت و برگشت تابع بازگشتي را نشان داده ام.
کد:
#include <conio.h>

void my_function(int a);
void main(void)
{
  clrscr();
	    my_function(7);  //ehzare tabeye baz_gashti
     getch();
}

void my_function(int a)
{
	cout<<"a="<<a<<endl;
	    a=a-1;
		 if (a>0)  my_function(a);
	 cout<<"a+10="<<a+10<<endl;
}
البته عکسی همراه پست بوده که از بین رفته.
خودتان خروجی را نگاه کنید و مسیر کنترل روی کدها را تحلیل کنید.
 

saalek110

Well-Known Member
سلام.
برای تعین اعداد اول یک روش اینه که مثلا اگر عدد 20 باشد از 19 تا 1 بررسی بشه که آیا عدد 20 بر اون عدد قابل بخش هست یا نه.
یک نفر قبلا همین را با تابع بازگشتی خواسته بود.
تابع بازگشتی اون این طور میشه که داخل صدا کردنهای تو در تو مدام از عدد کم میشه و در هر مرحله هم بخش پذیری تست میشه.

یعنی شرط صدا زدن میشه نرسیدن به عدد 1 . و در تابع هم بخش پذیری تست میشه. و اگر بخش پذیر بود چاپ میشه. یعنی که این عدد چاپ شده مقسوم علیه است. اگر هم برنامه ای می خواهید که مقسوم علیه ها را نخواهد و فقط می خواهید تابعی بسازید که بگوید عدد اول هست یا نه. میشه با return ها این قضیه را انجام داد. یعنی هر گاه در مرحله ای مقسوم علیه ئی پیدا شد ریترن خاصی بدهد و کار صدا زدن هم دیگه ادامه پیدا نمی کند. و در درونی ترین استپ هم مقدار خاصی ریترن میشه که یعنی عدد اول است.

کلا این قضیه که قسمت بالایی تابع بازگشتی موقع رفت اجرا میشه و قسمت پایینی موقع برگشت را باید در نظر گرفت.

============================
پست زیر در جواب یکی از دوستان که کد خود را گذاشته بود تا اصلاح بشود گذاشته می شود:
====================================================
ببین این درست کار می کنه یا نه.

کد:
#include <stdio.h>
#include <conio.h>

 int z=0;
int first(int a,int i)
{
 z=a%i;
 if (  z==0   )
  return 1;
		else
			{
			if (i>2)
			first(a,i-1);
		  else
		  return 0;
		  }
}
void main()
{
 int a,i,x;
 tried :
 clrscr();
 printf("Enter Number : ");
 scanf("%d",&a);
  i=a-1;
 x=first(a,i);
 if (x==0)
  printf("First");
 else
  printf("Not First");
 printf("\nDo You Want Try Again ?");
 if (getch()=='y')
  goto tried;
}

سعی کردم زیاد کد شما را دست کاری نکنم و فقط جاهایی که لازم بود را تغییر دادم.

برای عددهای 1 و 2 برنامه کار نمی کنه که باید باز اصلاح بشه.

نکته مهم:
هر وقت دیدید برنامه شما طبق انتظار شما عمل نمی کند به ذهن خود ابدا فشار نیاورید بلکه:

در مکان های پروسس print بگیرید از همه متغیرها.
بعد نگاه کنید به رفتار برنامه با نگاه به اعداد چاپ شده.

من دستورات چاپ را بعد رفع اشکال برنامه پاک کردم. ولی باید بدانید قبلش من حوالی z=a%i دستور چاپ متغیرها را گذاشتم و فورا فهمیدم چرا برنامه رفتار غیر عادی دارد.
 

najmeh_67_2007

New Member
سوال درباره توابع بازگشتی در سی پلاس پلاس

چطور می تونم سرچ خطی یک آرایه رو به روش بازگشتی بنویسم؟ لطفا راهنمایی کنید؟
متشکرم.
 

جدیدترین ارسال ها

بالا