Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursion and stack #159

Merged
merged 24 commits into from
Oct 5, 2021
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
04b8cb2
Translate a part of article
mahdiHash Sep 28, 2021
ed3051b
Translate a part of article
mahdiHash Sep 29, 2021
515ad84
Merge branch 'javascript-tutorial:master' into master
mahdiHash Sep 30, 2021
5fcdd34
Translate a part of article
mahdiHash Sep 30, 2021
17cc7e1
Translate a part of article
mahdiHash Oct 1, 2021
923fc54
Translate a part of article
mahdiHash Oct 1, 2021
5dfd251
Translate a part of article
mahdiHash Oct 2, 2021
b04627a
Translate a part of article
mahdiHash Oct 3, 2021
dccf17d
Translate article
mahdiHash Oct 3, 2021
a0a8673
Translate task of "sum-to"
mahdiHash Oct 4, 2021
43f6242
Translate solution of "sum-to"
mahdiHash Oct 4, 2021
e4568a1
Translate task of "factorial"
mahdiHash Oct 4, 2021
d62d0dc
Translate solution of "factorial"
mahdiHash Oct 4, 2021
a58435c
Translate task of "fibonacci-numbers"
mahdiHash Oct 4, 2021
a453039
Translate solution of "fibonacci-numbers"
mahdiHash Oct 4, 2021
7e5b852
Translate task of "output-single-linked-list"
mahdiHash Oct 4, 2021
a67bd44
Translate solution of "output-single-linked-list"
mahdiHash Oct 4, 2021
aa26bd7
Translate task of "output-single-linked-list-reverse"
mahdiHash Oct 4, 2021
4978f4f
Translate solution of "output-single-linked-list-reverse"
mahdiHash Oct 4, 2021
c4fc445
Apply suggestions from code review
mahdiHash Oct 4, 2021
2ffc9af
Update 1-js/06-advanced-functions/01-recursion/article.md
mahdiHash Oct 4, 2021
c084a25
Change "stack" to "پشته"
mahdiHash Oct 4, 2021
0fe37e7
Change "stack" to "پشته"
mahdiHash Oct 4, 2021
fafd50f
Change "stack" to "پشته"
mahdiHash Oct 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Translate solution of "fibonacci-numbers"
  • Loading branch information
mahdiHash committed Oct 4, 2021
commit a4530390add4702ec8ad972c7702e4752264dd64
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The first solution we could try here is the recursive one.
اولین راه‌حلی که می‌توانیم اینجا امتحان کنیم راه‌حل بازگشتی است.

Fibonacci numbers are recursive by definition:
اعداد فیبوناچی طبق تعریف بازگشتی هستند:

```js run
function fib(n) {
Expand All @@ -9,14 +9,14 @@ function fib(n) {

alert( fib(3) ); // 2
alert( fib(7) ); // 13
// fib(77); // will be extremely slow!
// fib(77); // !خیلی کند خواهد بود
```

...But for big values of `n` it's very slow. For instance, `fib(77)` may hang up the engine for some time eating all CPU resources.
...اما برای مقدارهای بزرگ `n` بسیار کند است. برای مثال، `fib(77)` ممکن است موتور را به دلیل مصرف تمام منابع پردازنده برای مدتی از کار بیاندازد.

That's because the function makes too many subcalls. The same values are re-evaluated again and again.
به دلیل اینکه تابع تعداد زیادی زیرفراخوانی ایجاد می‌کند. مقدارهای یکسان دوباره و دوباره ارزیابی می‌شوند.

For instance, let's see a piece of calculations for `fib(5)`:
برای مثال، بیایید یک قسمت از محاسبات را برای `fib(5)` ببینیم:

```js no-beautify
...
Expand All @@ -25,68 +25,68 @@ fib(4) = fib(3) + fib(2)
...
```

Here we can see that the value of `fib(3)` is needed for both `fib(5)` and `fib(4)`. So `fib(3)` will be called and evaluated two times completely independently.
اینجا می‌توانیم ببینیم که مقدار `fib(3)` هم برای `fib(5)` نیاز است و هم برای `fib(4)`. پس `fib(3)` دو بار به صورت کاملا مستقل فراخوانی خواهد شد.

Here's the full recursion tree:
اینجا درخت بازگشت کامل را داریم:

![fibonacci recursion tree](fibonacci-recursion-tree.svg)

We can clearly notice that `fib(3)` is evaluated two times and `fib(2)` is evaluated three times. The total amount of computations grows much faster than `n`, making it enormous even for `n=77`.
می‌توانیم به وضوح ببینیم که `fib(3)` دو بار و `fib(2)` سه بار ارزیابی می‌شود. کل تعداد محاسبات نسبت به `n` خیلی سریع‌تر رشد می‌کند و آن را برای `n=77` بسیار عظیم می‌کند.

We can optimize that by remembering already-evaluated values: if a value of say `fib(3)` is calculated once, then we can just reuse it in future computations.
ما می‌توانیم با به خاطر سپردن مقدارهایی که از قبل ارزیابی شده‌اند آن را بهینه کنیم: اگر یک مقدار برای مثال `fib(3)` یک بار حساب شد، سپس ما می‌توانیم آن را در محاسبات بعدی دوباره استفاده کنیم.

Another variant would be to give up recursion and use a totally different loop-based algorithm.
یک نوع دیگر می‌تواند این باشد که بازگشت را ول کنیم و از یک الگوریتم متفاوت بر پایه حلقه استفاده کنیم.

Instead of going from `n` down to lower values, we can make a loop that starts from `1` and `2`, then gets `fib(3)` as their sum, then `fib(4)` as the sum of two previous values, then `fib(5)` and goes up and up, till it gets to the needed value. On each step we only need to remember two previous values.
به جای اینکه از `n` به مقدارهای کمتر برویم، می‌توانیم کاری کنیم که حلقه از `1` و `2` شروع کند سپس `fib(3)` را به عنوان مجموع آنها دریافت کند، سپس `fib(4)` به عنوان مجموع دو مقدار قبلی، سپس `fib(5)` و همینطور بالا می‌رود تا به مقدار مورد نیاز برسد. در هر مرحله ما فقط نیاز است که دو مقدار قبلی را به حافظه بسپاریم.

Here are the steps of the new algorithm in details.
اینجا مراحل الگوریتم جدید را با جزئیات داریم.

The start:
شروع:

```js
// a = fib(1), b = fib(2), these values are by definition 1
// a = fib(1) ،b = fib(2) ،این مقدارها طبق تعریف 1 هستند
let a = 1, b = 1;

// get c = fib(3) as their sum
// را به عنوان مجموع آنها دریافت کن c = fib(3)
let c = a + b;

/* we now have fib(1), fib(2), fib(3)
/* fib(1) ،fib(2) ،fib(3) حالا اینها را داریم
a b c
1, 1, 2
*/
```

Now we want to get `fib(4) = fib(2) + fib(3)`.
حالا می‌خواهیم `fib(4) = fib(2) + fib(3)` را دریافت کنیم.

Let's shift the variables: `a,b` will get `fib(2),fib(3)`, and `c` will get their sum:
بیایید متغیرها را تغییر دهیم: `a,b` مقدارهای `fib(2),fib(3)` را دریافت می‌کنند و `c` مجموع آنها را:

```js no-beautify
a = b; // now a = fib(2)
b = c; // now b = fib(3)
c = a + b; // c = fib(4)

/* now we have the sequence:
/* :حالا این دنباله را داریم
a b c
1, 1, 2, 3
*/
```

The next step gives another sequence number:
مرحله بعد یک دنباله عددی دیگر را می‌دهد:

```js no-beautify
a = b; // now a = fib(3)
b = c; // now b = fib(4)
c = a + b; // c = fib(5)

/* now the sequence is (one more number):
/* :حالا دنباله اینگونه است (یک عدد بیشتر)
a b c
1, 1, 2, 3, 5
*/
```

...And so on until we get the needed value. That's much faster than recursion and involves no duplicate computations.
...و همینطور تا زمانی که ما مقدار مورد نیاز را دریافت کنیم ادامه می‌یابد. این حلقه از بازگشتی سریع‌تر است و هیچ محاسبات تکراری را شامل نمی‌شود.

The full code:
کد کامل:

```js run
function fib(n) {
Expand All @@ -105,6 +105,6 @@ alert( fib(7) ); // 13
alert( fib(77) ); // 5527939700884757
```

The loop starts with `i=3`, because the first and the second sequence values are hard-coded into variables `a=1`, `b=1`.
حلقه با `i=3` شروع می‌شود چون اولین و دومین مقدار دنباله در متغیرهای `a=1`، `b=1` از قبل ذخیره شده‌اند.

The approach is called [dynamic programming bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming).
این روش [برنامه نویسی پویا](https://fa.wikipedia.org/wiki/برنامه%E2%80%8Cنویسی_پویا) نامیده می‌شود.