4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 –...

12
Recursion เป็ นการเขียนโปรแกรมวิธีหนึ่งที่ยอมให ้ method เรียกตัวเองในการประมวลผล มัน อาจเป็ นเรื่องประหลาดสาหรับหลาย ๆ คนที่เริ่มเขียนโปรแกรม แตrecursion เป็ นวิธีที่สามารถ นามาใช ้ในการแก ้ปัญหาของการเขียนโปรแกรมให ้ดียิ่งข ึ้น ในบทนี้เราจะมาทาความรู ้จักกับ Recursion หลังจากจบบทเรียนนี้แล ้ว ผู ้อ่านจะได ้ทราบถึง o เทคนิค และวิธีการมองการทางานในรูปแบบของ Recursion o ตัวอย่างปัญหาทางคณิตศาสตร์ ที่ใช ้ Recursion เป็นเครื่องมือในการแก ้ปัญหา o ตัวอย่างการทางานของปัญหาต่าง ๆ ที่ใช ้ Recursion เป็นเครื่องมือ 4.1 การหาผลรวม (Summation) สมมติว่าเราต ้องการหาค่าของผลรวมของเลขตั ้งแต่ 1 ถึง 100 วิธีการที่ง่ายที่สุดในด ้านการเขียน โปรแกรมก็คือ การนาเอา loop เข ้ามาช่วยในการหาผลรวมนีซึ่งเราได ้เคยทามาแล ้วไม่มากก็ น ้อย และมีวิธีการเขียนที่หลากหลาย หนึ่งในนั้นก็อาจเขียนได ้ดังนีint sum = 0; for(int i = 100; i > 0; i--) sum += i; ถ ้าเราสังเกตให ้ดี เราจะเห็นว่าการทางานของ loop มีขั้นตอนในการหาผลรวมในรูปแบบของ 100 + 99 + 98 + 97 + … + 1 และถ ้าเราต ้องการหาค่าของผลรวมใด ๆ ตั ้งแต่ 1 ถึง n เราก็อาจเขียนขั้นตอนของการหาผลรวม นั้นได ้ในรูปแบบของสมการทางคณิตศาสตร์ ดังนีn + (n – 1) + (n – 2) + (n – 3) + … + 1 จากการคานวณที่เห็นเรารู ้ว่า ในการหาค่าของ n ที่อยู่ทางด ้านซ ้ายนั ้นจะต ้องอาศัยค่าของ n ทีอยู่ทางด ้านขวา และ n ที่อยู่ทางด ้านขวาสุดเท่านั ้นที่มีค่า (1) ที่เราจะนาไปประมวลผลได ้ ซึ่ง รูปแบบของการประมวลแบบนี้สามารถที่จะเขียนให ้อยู่ในรูปแบบของ Mathematical induction 1 ได ้ดังนีsum(n) = 1 if n = 1 sum(n) = n + sum(n – 1) if n > 1 ในการออกแบบ method ที่อยู่ในรูปแบบของ Recursion นั้น สิ่งที่สาคัญที่สุดในการออกแบบก็ คือ เงื่อนไขของการจบการทางานของ method ในการเรียกตัวเอง ภาพที4.1 แสดงถึงเงื่อนไข ทีsum() หยุดเรียกตัวเอง เมื่อ n มีค่าเท่ากับ 1 เราเริ่มต ้นด ้วยการหาค่าของ sum ทีn = 5 ซึ่งก็ยังไม่ได ้ผลลัพธ์ใด ๆ เพราะว่าจากเงื่อนไขทีกาหนด sum(5) = 5 + sum(4) ดังนั้นเราจึงจาเป็ นที่จะต ้องหาต่อไปเรื่อย ๆ จนกว่าจะเจอ 1 วิธีการพิสูจน์ทฤษฎีทางคณิตศาสตร์แบบหนึ่งที่ใช ้ตัวเลขเป็ นตัวช่วยในการพิสูจน์ (whole numbers และ zero)

Transcript of 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 –...

Page 1: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion เปนการเขยนโปรแกรมวธหนงทยอมให method เรยกตวเองในการประมวลผล มนอาจเปนเรองประหลาดส าหรบหลาย ๆ คนทเรมเขยนโปรแกรม แต recursion เปนวธทสามารถน ามาใชในการแกปญหาของการเขยนโปรแกรมใหดย งข น ในบทนเราจะมาท าความรจกกบ Recursion หลงจากจบบทเรยนนแลว ผอานจะไดทราบถง

o เทคนค และวธการมองการท างานในรปแบบของ Recursion o ตวอยางปญหาทางคณตศาสตร ทใช Recursion เปนเครองมอในการแกปญหา o ตวอยางการท างานของปญหาตาง ๆ ทใช Recursion เปนเครองมอ 4.1 การหาผลรวม (Summation) สมมตวาเราตองการหาคาของผลรวมของเลขตงแต 1 ถง 100 วธการทงายทสดในดานการเขยนโปรแกรมกคอ การน าเอา loop เขามาชวยในการหาผลรวมน ซงเราไดเคยท ามาแลวไมมากกนอย และมวธการเขยนทหลากหลาย หนงในนนกอาจเขยนไดดงน int sum = 0;

for(int i = 100; i > 0; i--)

sum += i;

ถาเราสงเกตใหด เราจะเหนวาการท างานของ loop มขนตอนในการหาผลรวมในรปแบบของ 100 + 99 + 98 + 97 + … + 1 และถาเราตองการหาคาของผลรวมใด ๆ ตงแต 1 ถง n เรากอาจเขยนขนตอนของการหาผลรวมนนไดในรปแบบของสมการทางคณตศาสตร ดงน n + (n – 1) + (n – 2) + (n – 3) + … + 1 จากการค านวณทเหนเรารวา ในการหาคาของ n ทอยทางดานซายนนจะตองอาศยคาของ n ทอยทางดานขวา และ n ทอยทางดานขวาสดเทานนทมคา (1) ทเราจะน าไปประมวลผลได ซงรปแบบของการประมวลแบบนสามารถทจะเขยนใหอยในรปแบบของ Mathematical induction1

ไดดงน sum(n) = 1 if n = 1 sum(n) = n + sum(n – 1) if n > 1 ในการออกแบบ method ทอยในรปแบบของ Recursion นน ส งทส าคญทสดในการออกแบบกคอ เงอนไขของการจบการท างานของ method ในการเรยกตวเอง ภาพท 4.1 แสดงถงเงอนไขท sum() หยดเรยกตวเอง เมอ n มคาเทากบ 1 เราเรมตนดวยการหาคาของ sum ท n = 5 ซงกยงไมไดผลลพธใด ๆ เพราะวาจากเงอนไขทก าหนด sum(5) = 5 + sum(4) ดงนนเราจงจ าเปนทจะตองหาตอไปเรอย ๆ จนกวาจะเจอ

1 วธการพสจนทฤษฎทางคณตศาสตรแบบหนงทใชตวเลขเปนตวชวยในการพสจน (whole numbers และ zero)

Page 2: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion บทท 4

102

เงอนไขทท าใหเราไดคา เราจะไดคากตอเมอ n มคาเทากบ 1 ซงท าให sum() หยดเรยกตวเอง และ sum() กจะประมวลผลดวยคา 1 ทไดกบคาของ n ขณะนนซงกคอ 2 ท าใหเราไดคาใหมทเกดข นคอ 3 การประมวลผลจะเปนไปในลกษณะน (เราเรยกการประมวลผลแบบนวา Bubble-up Evaluation) จนกระทงในทสดเรากจะมาถงประโยคทเราเรมตนประมวลในครงแรก คอ sum(5) = 5 + sum(4) ซงเราสามารถหาคาไดเพราะเรารวาคาของ sum(4) คอ 10 ดงนนเราจงไดคาของ sum(5) เปน 15

ภาพท 4.1 การหาคาของ sum(5)

ถาเราจะถายทอดความคดของเราในการสราง method sum() ใหอยในรปแบบของ code ทใช Recursion เรากจะได code ดงน //calculate sum using recursion

public static int sum(int n) {

//base case; stop calling itself and bubble-up

if(n == 1)

return n;

else

return n + sum(n - 1);

}

ผอานควรสงเกตถงเงอนไขของการยตการเรยกตวเองของ sum() วาเปนไปตามทเราไดก าหนดไวในการออกแบบตงแตตน (เรามกเรยกเงอนไขนวา base case) หลงจากทเราเขยนโปรแกรมทดสอบน

สงคา 4 + 6 กลบออกไป

สงคา 3 + 3 กลบ

ออกไป

สงคา 2 + 1 กลบออกไป

สงคา 1 กลบออกไป

หาคาของ sum(5)

sum(4) 5 +

+ 4 sum(3)

3 + sum(2)

2 + sum(1)

1

สงคา 5 + 10 กลบออกไป 15

Page 3: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

บทท 4 Recursion

103

1: /**

2: Finding summation of a given series

3: */

4:

5: class SumRecursion {

6: public static void main(String[] args) {

7: System.out.println("Sum from 1 - 5 is " + sum(5));

8: }

9:

10: //calculate sum using recursion

11: public static int sum(int n) {

12: //base case

13: if(n == 1)

14: return n;

15: else

16: return n + sum(n - 1);

17: }

18: }

ผลลพธทไดคอ Sum from 1 – 100 is 5050

4.1.1 เกดอะไรขนเมอ method เรยกตวเอง

เพอใหเหนการท างานของ Recursion เราอาจเปลยนแปลง code ดวยการน าเอาประโยคทแสดงผลของขนตอน และขอมลในขณะท sum() ถกเรยกในชวงเวลาตาง ๆ กนมาใชดงตวอยางทเหนน //calculate sum using recursion

public static int sum(int n) {

//base case

space(step++);

System.out.println("Calling sum(" + n + ")");

if(n == 1) {

space(--step);

System.out.println("Returned from sum(" + n + "), result = " + n);

return n;

}

else {

int temp = n + sum(n - 1);

space(--step);

System.out.println("Returned from sum(" + n + "), result = " + temp);

return temp;

}

}

//helper method to print spaces

public static void space(int n) {

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

System.out.print(" ");

}

ผลลพธทไดจากการ run ดวยคา n = 5 คอ Calling sum(5)

Calling sum(4)

Calling sum(3)

Calling sum(2)

Calling sum(1)

Returned from sum(1), result = 1

Returned from sum(2), result = 3

Returned from sum(3), result = 6

Returned from sum(4), result = 10

Returned from sum(5), result = 15

Sum from 1 - 5 is 15

Page 4: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion บทท 4

104

จากผลลพธทไดเราจะเหนถงการเรยกตวเองของ sum() ดวยคาทลดลงทละหนง จนกระทงถงกรณสดทายทคาทสงเขาไปเปนหนง การบวกเลขจงเกดข นตามล าดบจนในทสดกยตเมอผลลพธของการบวกครงสดทายถกสงกลบออกไปยง main() 4.1.2 ข นตอนส าคญส าหรบการออกแบบโปรแกรมทใช Recursion ขนตอนทจ าเปนส าหรบ Recursion คอ

o Base case: เปนสวนส าคญของการเขยนโปรแกรมดวย recursion เพราะฉะนนเราตองมชดค าสงอยางนอย 1 ชดทเปน base case เชน เมอ n == 1 ใน method sum()

o การเรยกใช method ทกครงจะตองน าไปส base case ไมเชนนนกจะท าใหเกดการเรยกตวเองโดยไมหยด

4.2 Factorial เพอใหเรามความเขาใจใน Recursion มากยงข นเรามาลองดตวอยางของ Recursion แบบงาย ๆ อกตวหนง นนกคอการค านวณหาคาของผลคณของเลขทกตวใน range ทก าหนดให หรอทเรยกวา Factorial ซงมสตรทางคณตศาสตร คอ factorial(n) = 1 if n = 0

factorial(n) = n x (n – 1) x (n – 2) x … x 3 x 2 x 1 if n > 0

จากสมการของ Factorial เราสามารถทจะหาคาของ factorial(4) ได ซงกคอ factorial(4) = 4 x 3 x 2 x 1 = 24

ถาเราเขยน code เพอค านวณหาคาของ Factorial น เรากเขยนขนงาย ๆ ดวยการใช loop เชน

int n = 5, product = 1;

for(int i = n; i > 0; i--)

product *= i;

การหา Factorial ดวยการใช loop ทเหนดานบนกคลาย ๆ กบการหาผลรวมทเราไดท ามากอนหนาน ถาเราท าการหาคาของ Factorial ของ 4 ดวยมอ เรากจะได factorial(4) = 4 * factorial(3)

= 4 * (3 * factorial(2))

= 4 * (3 * (2 * factorial(1)))

= 4 * (3 * (2 * (1 * factorial(0))))

= 4 * (3 * (2 * (1 * 1)))

= 4 * (3 * (2 * 1))

= 4 * (3 * 2)

= 4 * 6

= 24

ลองมาดโปรแกรมตวอยางการหา Factorial

1: /**

2: Finding factorial

3: */

4:

5: class Factorial {

6: public static void main(String[] args) {

7: int n = 5;

8:

9: System.out.println("factorial(" + n + ") = " + factorial(n));

10: }

11:

12: //finding factorial

13: public static int factorial(int n) {

14: if(n == 1)

15: return 1;

16: else

17: return n * factorial(n - 1);

18: }

Page 5: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

บทท 4 Recursion

105

19: }

ผลลพธทไดคอ factorial(5) = 120

Factorial เปนเลขทเพมจ านวนอยางรวดเรวตามคาของตวเลขทสงเขาไป ผอานควรทดลอง run โปรแกรม Factorial ดวยคาทมขนาดใหญ ๆ เพอใหเหนถงการเพมข นของตวเลขทรวดเรว (อาจตองเปลยนชนดของขอมลจาก int ใหเปน long เพอรองรบผลลพธทมขนาดใหญข น) 4.3 การหาคาของเลขยกก าลง การหาคาของเลขยกก าลงใด ๆ เชน mn นนเราสามารถทจะน า Recursion มาชวยได แตกอนอน

เราตองหา base case ของการหาคาของเลขยกก าลงเสยกอน ซงถาสงเกตใหด เลขยกก าลงก คอ การเอาเลขนน ๆ มาคณกนตามจ านวนของก าลงทก าหนดไว ดงสมการทเหนน Mn = M x M(n – 1) if n > 0 Mn = 1 if n = 0 ตวอยาง เชน

55 = 5 x 54 54 = 5 x 53 53 = 5 x 52 52 = 5 x 51 51 = 5 x 50 50 = 1

ถาเราก าหนดให power(m, n) เปน method ทใชในการหาคาของ mn ถา m = 5 และ n = 5 เรากจะได power(5, 5) เปน power(5,5) = 5 * power(5,4)

= 5 * (5 * power(5,3))

= 5 * (5 * (5 * power(5,2)))

= 5 * (5 * (5 * (5 * power(5,1))))

= 5 * (5 * (5 * (5 * (5 * power(5,0)))))

= 5 * (5 * (5 * (5 * (5 * 1))))

= 5 * (5 * (5 * (5 * 5)))

= 5 * (5 * (5 * 25))

= 5 * (5 * 125)

= 5 * 625

= 3125

และเมอเปลยนใหเปน code ใน Java แลว เรากจะได //finding m to the power of n

public static int power(int m, int n) {

//base case

if(n == 0)

return 1;

else

return m * power(m, n - 1);

}

power(m, n) เปน method ทม parameter สองตว ตวทหนงเปนเลขทตองการยกก าลง สวนตวทสองเปนก าลงของเลขตวแรก ในการเรยกตวเองของ power() นน เราจะลดคาของ n ลงทละหนงสวนคาของ m เราจะทงไวเหมอนเดม โปรแกรม Power.java เปนโปรแกรมทเราเขยนขนมาทดสอบการท างานของ power() ทงนเราไดเปลยนแปลง code บางสวนเพอแสดงขนตอนการท างานของ power() ในชวงตาง ๆ เหมอนทเราท ากบ sum() กอนหนาน

1: /**

2: Calculating M to the power of n

3: */

Page 6: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion บทท 4

106

4:

5: class Power {

6: static int step = 0;

7: public static void main(String[] args) {

8: int m = 5, n = 5;

9:

10: System.out.println("\nPower(" + m + ", " + n +") = " + power(m, n));

11: }

12:

13: //finding m to the power of n

14: public static int power(int m, int n) {

15: //base case

16: space(step++);

17: System.out.println("power(" + m + "," + n + ")");

18: if(n == 0) {

19: space(--step);

20: System.out.println("power(" + m + "," + n + ") = " + 1);

21: return 1;

22: }

23: else {

24: int temp = m * power(m, n - 1);

25: space(--step);

26: System.out.println("power(" + m + "," + n + ") = " + temp);

27: return temp;

28: }

29: }

30:

31: //helper method to print spaces

32: public static void space(int n) {

33: for(int i = 0; i < n; i++)

34: System.out.print(" ");

35: }

36:

37: }

ผลลพธทเราไดจากการ run โปรแกรมคอ power(5,5)

power(5,4)

power(5,3)

power(5,2)

power(5,1)

power(5,0)

power(5,0) = 1

power(5,1) = 5

power(5,2) = 25

power(5,3) = 125

power(5,4) = 625

power(5,5) = 3125

Power(5, 5) = 3125

4.4 Fibonacci Numbers Fibonacci numbers เปนกลมของตวเลขทเกดข นจากการเอาตวเลขกอนหนานนสองตวมารวมกนเปนตวเลขทอยถดไปใน ต าแหนงทสาม เชน 0 1 1 2 3 5 8 13 21 34 … จาก pattern ทมอยใน Fibonacci numbers เราสามารถทจะเขยน method เพอท าการหา Fibonacci numbers ดวยเทคนคของการใช recursion ดง algorithm ทเหนน (ไมใช algorithm ทดของการใช recursion – fib(number – 2) จะไปเรยก fib(number – 1) ซงมการเรยกตางหากแลว ท าใหเกดการซ าซอนของการท างานโดยไมจ าเปน)

Page 7: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

บทท 4 Recursion

107

Algorithm fib(number)

if(number is 0 OR number is 1)

return number

else

return fib(number – 1) + fib(number – 2)

โปรแกรม Fib.java ส าหรบการหา Fibonacci numbers จาก Algorithm ทให

1: /**

2: Finding Fibonacci numbers

3: */

4:

5: class Fib {

6: public static void main(String[] args) {

7: int number = 30;

8:

9: for(int i = 0; i <= number; i++) {

10: if(i % 7 == 0)

11: System.out.println();

12: System.out.printf("%8d", fib(i));

13: }

14: System.out.println();

15: }

16:

17: //findin Fibonacci numbers

18: public static int fib(int num) {

19: //base case

20: if(num == 0 || num == 1)

21: return num;

22: else

23: return fib(num - 1) + fib(num - 2);

24: }

25: }

ผลลพธของการ run

0 1 1 2 3 5 8

13 21 34 55 89 144 233

377 610 987 1597 2584 4181 6765

10946 17711 28657 46368 75025 121393 196418

317811 514229 832040

ผอานควรทดลอง run โปรแกรมดวยคาของ number ทแตกตางกนเพอใหเหนถงเวลาทโปรแกรมใช ในการค านวณวาแตกตางกน มากนอยอยางไร 4.5 การกลบตวอกษร (reverse) ใน string ดวยการใช recursion โปรแกรมตวอยางตอไปนเปนโปรแกรมตวอยาง ทเปลยน string ทผใชสงเขามาใหอยในรปแบบของการกลบหว กลบหาง (reverse)

1: /**

2: Reverse a string with recursion

3: */

4:

5: import java.io.*;

6:

7: class ReverseString {

8: public static void main(String[] args) {

9: String str = "";

10: BufferedReader reader;

11: InputStreamReader in;

12:

13: //getting input from keyboard

14: in = new InputStreamReader(System.in);

15: reader = new BufferedReader(in);

16: try {

17: System.out.print("Enter a string: ");

18: str = reader.readLine();

19: }

20: catch(IOException err) {

Page 8: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion บทท 4

108

21: System.err.println("I/O error!");

22: System.exit(1);

23: }

24: //calling reverse() to reverse a string

25: reverse(str);

26: }

27:

28: //reversing a string

29: public static void reverse(String s) {

30: reverse(s, s.length()-1);

31: }

32:

33: //main method to print String in reverse order

34: public static void reverse(String s, int n) {

35: //base case

36: if(n < 0)

37: return ;

38: else {

39: //print character at n

40: System.out.print(s.charAt(n));

41: reverse(s, n - 1);

42: }

43: }

44: }

เราใชความยาวของ string เปนตวก าหนดการยตการท างานของ method reverse() ดวยเหตนเราจงตองใช helper method เปนตวท างานใหเรา เราเรยก method reverse(s, n) ครงแรกดวยความยาวของ string ลบ 1 ทงนกเนองจากวาเราตองการทจะ print ตวอกษรทต าแหนงสดทายของ string เราใช method charAt() เปนตวดงเอาคาของตวอกษรจากต าแหนงทก าหนดไว (n) หลงจากนนเรากเรยก reverse() ใหมดวยคาของ n – 1 และในทสดคาของ n กจะมคานอยกวา 0 (คาทเปนจรงคอ -1) ซงคา -1 นแหละทเปนตวก าหนดการหยดการท างานของ reverse() ผลลพธทไดจากการ run โปรแกรม Enter a string: I am in Chiang Mai

iaM gnaihC ni ma I

จะเหนวาการท าให string กลบหว กลบหางของเรานนเปนการกลบแบบตรง ๆ ซงท าใหเราไมสามารถทจะอานขอความได ตวอยางตอไปนเปนการกลบ string ทยงรกษาความเปนค านน ๆ ไว 4.5.1 การ reverse string ทรกษารปแบบของค าไว

1: /**

2: Reverse a string which preserve readability of words

3: */

4:

5: import java.io.*;

6:

7: class StringReversed {

8: public static void main(String[] args) {

9: String str = "";

10: BufferedReader reader;

11: InputStreamReader in;

12:

13: //getting input from keyboard

14: in = new InputStreamReader(System.in);

15: reader = new BufferedReader(in);

16: try {

17: System.out.print("Enter a string: ");

18: str = reader.readLine();

19: }

20: catch(IOException err) {

21: System.err.println("I/O error!");

22: System.exit(1);

23: }

24:

25: //calling reverse() to reverse a string

26: System.out.println("String reversed: " + reverse(str));

Page 9: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

บทท 4 Recursion

109

27: }

28:

29: /**

30: routine to reverse a string by

31: 1. call flip() to reverse all characters

32: 2. find each word and call flip() to reverse it back

33: 3. replace the old word with the new one just flipped

34: 4. return a new string

35: */

36: private static String reverse(String string) {

37: String SPACE = " ";

38: //reverse a given string

39: StringBuffer s = new StringBuffer(flip(string));

40: int i = s.indexOf(SPACE);

41: int j = 0;

42: String oldWord = null, newWord = null;

43: //find each word and reverse it, then replace

44: //the old word with the new one

45: while(i >= 0) {

46: oldWord = s.substring(j, i);

47: newWord = flip(oldWord);

48: s.replace(j, i, newWord);

49: for(j = i+1; s.charAt(j) == ' '; j++) {}

50: i = s.indexOf(SPACE, j);

51: }

52: //take care of last word

53: oldWord = s.substring(j);

54: newWord = flip(oldWord);

55: s.replace(j, s.length(), newWord);

56:

57: return s.toString();

58: }

59:

60: //flip all characters in a string

61: private static String flip(String s) {

62: //base case

63: if(s.length() < 2)

64: return s;

65: else

66: return flip(s.substring(1)) + s.charAt(0);

67: }

68: }

ผลลพธทไดจากการ run คอ Enter a string: I love Chiang Mai

String reversed: Mai Chiang love I

4.6 Binary search กบ Recursion Binary search ทเราไดพดถงในบทท 1 ใช loop เปนตวจดการกบขอมล ถาเราจะน าเอา Recursion เขามาใชเราตองยกเลกการใช loop มาด Binary search ทเราไดเขยนขนกอนหนาน public boolean binarySearch(Comparable key) {

int lower = 0;

int upper = elems - 1;

int mid;

while(true) {

mid = (lower + upper) / 2;

if(arr[mid].equals(key))

return true; //found it

else if(lower > upper)

return false; //not found

else {

//divide array into halves

if((arr[mid].compareTo(key)) < 0)

lower = mid + 1; //in upper half

else

upper = mid - 1; //in lower half

}

Page 10: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion บทท 4

110

}

}

binarySearch() ทเหนดานบนน เปลยนการคนหาในสวนตาง ๆ ดวยการค านวณหา lower และ upper ทกครงใน loop จนกวาจะท างานเสรจ เพราะฉะนนถาจะเปลยนมาใช Recursion เราตองสงคาของ lower และ upper ทหาไดไปให method ทท าการคนหาขอมลให เราเขยน method 2 ตวคอ search() และ recBinSearch() โดยผใชจะเรยกใช search() ในการคนหา สวน recBinSearch() เปน method ภายในทเปนกลไกหลกของการคนหาดวยการใช Recursion เขามาชวย public boolean search(Comparable key) {

return recBinSearch(key, 0, elems - 1);

}

ภายใน search() เราเรยก recBinSearch() ดวย parameter 3 ตวคอ

o key ทตองการคนหา o index เรมตน (lower) ของ array o index สดทาย (upper) ของ array

และ code ของการคนหาดวย Binary Search แบบทใช recursion มดงน //recursive Binary search

private boolean recBinSearch(Comparable k,

int lower, int upper) {

int mid;

mid = (lower + upper) / 2;

if(arr[mid].equals(k))

return true;

else if(lower > upper)

return false;

else {

//search on the right half

if((arr[mid].compareTo(k)) < 0)

return recBinSearch(k, mid + 1, upper);

//search on the left half

else

return recBinSearch(k, lower, mid - 1);

}

}

การท างานหลก ๆ ของการคนหาเกดภายใน method recBinSearch() ซงมการเปลยนแปลงเพยงแค การเพม parameter ใหสองตว พรอมทงเปลยนการค านวณหา lower และ upper ให เปนการเรยกใช recBinSearch() แทนพรอมทงการเลกใช loop ผอานควรเขยนโปรแกรมทดสอบเพอท าความเขาใจกบการคนหาดวย recBinSearch() ใหดย งข น

สรป Recursion เปนวธการทสามารถน ามาใชในการเขยนโปรแกรมวธการหนง โดยทวไปการใช recursion มกจะมคา overhead สงกวาการใช loop เขามาชวยในการเขยน เพราะฉะนน ถาหลกเลยงการใช recursion ไดกควรทจะหลกเลยง แตถาจ าเปนและการใช recursion ท าให code ดงายขน บางครงเรากควรทจะใช recursion ทงนและทงนนกข นอยกบการออกแบบ recursion และ ลกษณะของปญหาทมอย เชนการใช recursion ในภาษา lisp และ prolog ซง

เปนภาษาทมการใช recursion อยางมาก และจะใช recursion ในการค านวณและประมวลอยางสม าเสมอ โดยสรปแลวเราไดพดถง การแกปญหาดวย Recursion การหาเงอนไขในการยตการเรยกตวเองของ method การประมวลผลของ method วามขนตอนอยางไร ตวอยางการเขยนโปรแกรมดวยการใช Recursion

Page 11: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

บทท 4 Recursion

111

แบบฝกหด 1. การหา square root ของเลขใด ๆ นนมวธการหลายแบบ มวธการแบบหนงเรยกวา วธการ

ของ Newton ซงมการค านวณ หาดงน

Algorithm squareRoot(num, ans, tol)

If(|ans2 – num| <= tol)

squareRoot(num, ans, tol) = ans

else

squareRoot(num, (ans2 + num) / (2 x ans), tol)

จงเขยน recursive method เพอหา square root ของตวเลขใด ๆ ทปอนเขามาจาก keyboard ทดสอบดวยการเรยก squareRoot(5, 2, 0.01) และ squareRoot(4, 2, 0.01)

2. การหาตวหารรวม (Greatest Common Divisor) ของ integer 2 ตว หาไดดวยวธการของ

Euclid ดงน

gcd(x, y) = gcd(y, x) if x < y gcd(x, y) = x if y = 0; gcd9x, y) = gcd(y, x mod y) ถาไมอยใน 2 case แรก จงเขยน recursive method เพอหา gcd ของ integer ทปอนเขามาจาก keyboard

3. จงเขยน recursive method ทค านวณหาความยาวของ linked-list

4. จงเขยน recursive method ทค านวณหาผลรวม n ครงของ series

1 +1/2 + 1/3 +1/4 + 1/5 + … + 1/n 5. Palindrome เปน string ทอานแลวไดความหมายเหมอนกนทง จากทางดานหนา และทาง

ดานหลง เชน

Anna Go dog Madam, I’m Adam จงเขยน recursive method ทตรวจสอบวา string ทก าหนดใหเปน palindrome หรอไม

6. Perfect number คอตวเลขทเปนผลรวมของ factor ของมนเองเชน 6 = 1 + 2 + 3 จง

เขยน recursive method ทค านวณหา perfect number ทม factor นอยกวาตวมนเอง (1 < 6, 2 < 6, 3 < 6 etc.)

7. จงเขยน recursive method ท convert string ของตวเลขใหเปน integer เชน string “12345” จะถกเปลยนใหเปน 12345

8. จงเขยน recursive method ทค านวณหาจ านวนครงของ character ทปรากฏอยใน string

ทก าหนดให 9. จงเขยน recursive method ทเปลยนเลขฐานสบใหเปนเลขฐานสอง 10. จงอธบายถงการท างาน และหาผลลพธของโปรแกรมทใหน

class WhereAmI {

public static void main(String[] args) {

String[] str = {"Am ", "in ", "main()"};

System.out.println(str[0] + str[1] + str[2]);

Page 12: 4.1 (Summation) - sci.feu.ac.thsci.feu.ac.th/faa/dsa/bookPDFs/chap4-Recursion.pdf · Sum from 1 – 100 is 5050 4.1.1 เกิดอะไรขึ้นเมื่อ method เรียกตัวเอง

Recursion บทท 4

112

main(str);

}

}

11. จงเขยน method ทหาคาของ function ทใหน พรอมทงเขยนโปรแกรมทดสอบ

f(n) = 0 if n = 0 f(n) = f(½ n) if n = เลขค และ n > 0 f(n) = 1 + f(n – 1) if n = เลขค และ n > 0

12. จงเขยน recursive method ทท าการหาจ านวนของ 1 ทอยในเลขฐานสอง เชนสมมตวา

เลขฐานสองทวามคาเปน 5 = 1012 ดงนน จ านวนของ 1 คอ 2 ตว

หมายเหต: จ านวนของ 1 ในเลขฐานสองใด ๆ (n) เทากบจ านวนของ 1 ทอยใน n/2 บวกอกหนงถาเลขตวนนเปนเลขค

13. จงเขยนโปรแกรมทท าการหา permutation ของ String ก าหนดใหดวยการใช Recursion

เชน ถา String มคาเปน "abc" เราจะไดคาทงหมดคอ abc, acb, bac, bca, cab, cba 14. Binomial coefficients C(N, k) สามารถเขยนใหอยรปแบบของ Recursion ไดดงน

C(N, 0) = 1, C(N, N) = 1, C(N, k) = C(N – 1, k) + C(N – 1, k – 1) ถา 0 < k < N จงหาผลลพธของ Binomial coefficients ดงกลาวดวยการใช Recursion

15. จงเขยนโปรแกรมทหาคาสงสดทมอยใน array ดวยการใช Recursion

แนะน า: ควรใชแนวทางของ binary search ทใช recursion int max(int[] array, int left, int right);

โดยทตวแปร left และ right เปนจดเรมตนและจดสดทายของขอมลทอยใน array

ตามล าดบ