198232 DATA STRUCTURES AND ALGORITHMS
1. INTRODUCTION
ความสำคัญของโปรแกรมที่ใช้งานข้อมูล inputจำนวนมาก 1 INTRODUCTION เนื้อหาในบทนี้ ความสำคัญของโปรแกรมที่ใช้งานข้อมูล inputจำนวนมาก สรุปคณิตศาสตร์ที่จำเป็น ทบทวน Recursion
1.1. ว่าด้วยเนื้อหาวิชา การเขียนโปรแกรมที่สามารถใช้งานได้ยังไม่เป็นการเพียงพอ ถ้าโปรแกรมดังกล่าวต้องนำไปใช้งานกับจำนวนข้อมูลมาก ๆ แล้ว ประเด็นเรื่องเวลาที่ต้องใช้ในการทำงานของโปรแกรมก็จะเป็นสิ่งที่ต้องนำมาพิจารณา
1.1. ว่าด้วยเนื้อหาวิชา เรียนรู้การประเมิน running time ของโปรแกรมสำหรับ input ขนาดใหญ่ และที่สำคัญ คือ เราสามารถเปรียบเทียบ running times ของโปรแกรมสองโปรแกรมโดยที่ไม่ต้องลงมือเขียนโปรแกรมจริงขึ้นมาก่อน เรียนรู้เทคนิคที่สามารถใช้ในการปรับปรุงความเร็วในการทำงานของโปรแกรมและตรวจหาจุดคอขวดของโปรแกรม
1.1. ว่าด้วยเนื้อหาวิชา เมื่อเราใช้คอมพิวเตอร์ในการแก้ปัญหา เราจะพบว่ามีวิธีการหลายวิธีการที่ใช้ในการแก้ปัญหานั้น ๆ ได้ กล่าวสำหรับปัญหาขนาดเล็กแล้ว ไม่ว่าเราจะใช้วิธีการใดที่สามารถหาคำตอบได้ก็เป็นอันใช้ได้ แต่สำหรับปัญหาขนาดใหญ่แล้ว (หรือปัญหาที่ประกอบด้วยปัญหาเล็ก ๆ ในปริมาณมาก ๆ) เราพบว่าเราจำเป็นต้องใช้วิธีการแก้ปัญหาที่ต้องใช้เวลาและทรัพยากรต่าง ๆ อย่างมีประสิทธิภาพมากที่สุดเท่าที่จะทำได้
1.1. ว่าด้วยเนื้อหาวิชา ปัญหาการเลือก (selection problem): หาค่าที่มีค่ามากเป็นอันดับ k จากกลุ่มตัวเลข n ตัว วิธีแรก: อ่านค่าทั้งหมดมาเก็บใน array แล้วทำการจัดเรียงจากค่ามากไปค่าน้อย ค่าที่ต้องการคือค่าที่อยู่ในตำแหน่ง k
1.1. ว่าด้วยเนื้อหาวิชา อัลกอริทึมที่ดีกว่า: อ่านค่ามา k ตัวลงใน array แล้วทำการจัดเรียงจากค่ามากไปหาค่าน้อย จากนั้น อ่านค่าที่เหลือมาทีละค่า ถ้าค่าที่อ่านเข้ามานี้มีค่าน้อยกว่าค่าที่อยู่ที่ตำแหน่ง k ใน array ก็ละทิ้งค่านั้นไป แต่ถ้ามันมีค่ามากกว่าค่าที่ตำแหน่ง k ใน array ก็บรรจุค่านั้นลงในตำแหน่งที่ถูกต้องตามลำดับของมันแล้วเอาค่าเดิมที่อยู่ในตำแหน่ง k ทิ้งไป เมื่อจบสิ้นการทำงานของอัลกอริทึมแล้ว ค่าที่อยู่ที่ตำแหน่ง k ก็คือคำตอบ
Algorithm ใดดีกว่ากัน และ Alorithm ดังกล่าวนี้ดีพอหรือไม่ 1.1. ว่าด้วยเนื้อหาวิชา Algorithm ใดดีกว่ากัน และ Alorithm ดังกล่าวนี้ดีพอหรือไม่
ปัญหา word search puzzle: ตารางของตัวอักษรและรายการของคำ 1.1. ว่าด้วยเนื้อหาวิชา ปัญหา word search puzzle: ตารางของตัวอักษรและรายการของคำ ACCELERATION ACOUSTICS AMPLITUDE ATOM BLACK HOLE BUOYANCY CENTRIPETAL COLD FUSION CONVECTION DENSITY DISPLACEMENT DOPPLER EINSTEIN ELECTRON ENERGY ENTROPY EXPERIMENT FARADAY FREEZING POINT FULCRUM GRAVITY HAWKING IMPEDANCE ISOTOPE KELVIN LAWS LENGTH LEVER MASS MECHANICS MOMENTUM NEWTON OPTICS PASCAL PENDULUM POWER PRINCIPLES QUARK REFRACTION SOUND STRING THEORY TESLA THERMOMETER TRAJECTORY VECTOR VISCOSITY WAVE WEDGE WORK A second problem is to solve a popular word puzzle. The input consists of a two dimensional array of letters and a list of words. The object is to find the words in the puzzle. These words may be horizontal, vertical, or diagonal in any direction. As an example, the puzzle shown in Figure 1.1 contains the words this, two, fat, and that. The word this begins at row 1, column 1, or (1,1), and extends to (1,4); two goes from (1,1) to (3,1); fat goes from (4,1) to (2,3); and that goes from (4,4) to (1,1). Again, there are at least two straightforward algorithms that solve the problem. For each word in the word list, we check each ordered triple (row, column, orientation) for the presence of the word. This amounts to lots of nested for loops but is basically straightforward. Alternatively, for each ordered quadruple (row, column, orientation, number of characters) that doesn’t run off an end of the puzzle, we can test whether the word indicated is in the word list. Again, this amounts to lots of nested for loops. It is possible to save some time if the maximum number of characters in any word is known. It is relatively easy to code up either method of solution and solve many of the real-life puzzles commonly published in magazines. These typically have 16 rows, 16 columns, and 40 or so words. Suppose, however, we consider the variation where only the puzzle board is given and the word list is essentially an English dictionary. Both of the solutions proposed require considerable time to solve this problem and therefore are not acceptable. However, it is possible, even with a large word list, to solve the problem in a matter of seconds.
1. หาว่า คำในรายการคำ อยู่ในตารางหรือไม่ 2 1. หาว่า คำในรายการคำ อยู่ในตารางหรือไม่ 2. หาว่า ในตาราง เป็นคำอยู่ในรายการคำหรือไม่
1.1. ว่าด้วยเนื้อหาวิชา ประเด็นที่สำคัญ คือ ในหลายปัญหา การเขียนโปรแกรมที่เพียงแต่สามารถแก้ปัญหาเหล่านั้นได้ยังไม่เป็นการเพียงพอ ถ้าโปรแกรมนั้นจะต้องใช้ทำงานกับข้อมูลจำนวนมาก ๆ แล้วจะต้องคำนึงถึงเรื่องของ running time ด้วย
1.1. ว่าด้วยเนื้อหาวิชา ดังนั้น เราจะต้อง: เรียนรู้การประเมิน running time ของโปรแกรมสำหรับอินพุตขนาดใหญ่และที่สำคัญกว่า คือ เราสามารถเปรียบเทียบ running time ของโปรแกรมสองโปรแกรมโดยที่ไม่ต้องลงมือเขียนโปรแกรมจริงขึ้นมาก่อน
1.2 ทบทวนคณิตศาสตร์ที่จำเป็น 1.2.1 Exponents xA xB = xA+B xA / xB = xA-B (xA)B = xAB xN + xN = 2xN x2N 2N + 2N = 2N+1
1.2 ทบทวนคณิตศาสตร์ที่จำเป็น 1.2.2 Logarithms ส่วนใหญ่ที่ใช้เป็น logarithms ฐาน 2 นิยาม: XA = B if and only if logX B = A log ab = log a + log b log a/b = log a - log b log(ab) = b log a log x < x for all x > 0 log A B= log c B log c A ;A,B,C>0,A≠1 log 1 = 0, log 2 = 1, log 1,024 = 10, log 1,048,576 = 20
1.2.2. Logarithms THEOREM log (AB) = log A + log B; A, B > 0 PROOF: ให้ X = log A, Y = log B, Z = log AB และถ้าฐานเป็น 2 จะได้ 2X = A, 2Y = B, 2Z = AB รวมทั้งสามสมการจะได้ 2X2Y = 2Z = AB ดังนั้น X + Y = Z
1.2 ทบทวนคณิตศาสตร์ที่จำเป็น THEOREM log 𝐴 𝐵= log 𝑐 𝐵 log 𝑐 𝐴 ;A,B,C>0,A≠1 PROOF: ให้ X = logC B, Y = logC A, และ Z = logA B. ดังนั้น จากนิยามของ logarithms จะได้ CX = B, CY = A, และ AZ = B รวมทั้งสามสมการจะได้ (CY)Z = CX = B ดังนั้น X = YZ, ซึ่งก็คือ Z = X/Y
1.2 ทบทวนคณิตศาสตร์ที่จำเป็น 1.2.3 อนุกรม (Series) 𝑖=0 𝑁 2 𝑖 = 2 𝑁+1 −1 𝑖=0 𝑁 𝐴 𝑖 = 𝐴 𝑁+1 −1 𝐴−1 ถ้า 0 < A < 1, จะได้ 𝑖=0 𝑁 𝐴 𝑖 ≤ 1 1−𝐴 ถ้า N เข้าหา , ผลบวกจะเข้าหา 1 1−𝐴
ให้ S เป็นผลบวก ดังนั้น S = 1 + A + A2 + A3 + A4 + A5 + . . และ 1.2.3 อนุกรม (Series) การ derive สูตร 𝑖=0 ∞ 𝐴 𝑖 (0<𝐴<1) ให้ S เป็นผลบวก ดังนั้น S = 1 + A + A2 + A3 + A4 + A5 + . . และ AS = A + A2 + A3 + A4 + A5 + . . จะได้ S – AS = 1 -> S(1-A)=1 -> S = 1 / (1 – A)
วิธีเดียวกันเพื่อหาค่าของ 1.2.3 อนุกรม (Series) วิธีเดียวกันเพื่อหาค่าของ 𝑆= 𝑖=0 ∞ 𝑖 2 𝑖 𝑠= 1 2 + 2 2 2 + 3 2 3 + 4 2 4 +… 2𝑠=1+ 2 2 + 3 2 2 + 4 2 3 + 5 2 4 +… 2𝑠−𝑠=1+ 1 2 + 1 2 2 + 1 2 3 + 1 2 4 +… 𝑠=1+ 1 1− 1 2 = 3
อนุกรมเลขคณิตสามารถหาได้ด้วยการใช้สูตรพื้นฐาน ดังนี้ 1.2.3 อนุกรม (Series) อนุกรมเลขคณิตสามารถหาได้ด้วยการใช้สูตรพื้นฐาน ดังนี้ 𝑖=1 𝑁 𝑖= 𝑁(𝑁+1) 2 ≈ 𝑁 2 2 เช่น 1+2+3…100= 100×(100+1) 2
อนุกรมอื่น ๆ ที่อาจพบได้ 1.2.3 อนุกรม (Series) อนุกรมอื่น ๆ ที่อาจพบได้ 𝑖=1 𝑁 𝑖 2 = 𝑁(𝑁+1)(2𝑁+1) 6 ≈ 𝑁 3 3 𝑖=1 𝑁 𝑖 𝑘 ≈ 𝑁 𝑘+1 𝑘+1 ;𝑘≠−1 𝐻 𝑛 = 𝑖=1 𝑁 1 𝑖 ≈ log 𝑒 𝑁
โดยทั่วไปแล้ว: 𝑖=1 𝑁 𝑓(𝑛) =𝑁𝑓(𝑁) 1.2.3 อนุกรม (Series) โดยทั่วไปแล้ว: 𝑖=1 𝑁 𝑓(𝑛) =𝑁𝑓(𝑁) 𝑖= 𝑛 0 𝑁 𝑓 𝑖 = 𝑖=1 𝑁 𝑓(𝑖) − 𝑖=1 𝑛 0 −1 𝑓(𝑖)
1.2 ทบทวนคณิตศาสตร์ที่จำเป็น 1.2.4 Modular Arithmetic เรียก a ว่าเป็น congruent to b modulo n, ซึ่งเขียนเป็น a b(mod n), ถ้า n หาร a – b ลงตัว เศษที่ได้จากการนำ n ไปหาร a หรือหาร b มีค่าเท่ากัน ดังนั้น, 81 61 1(mod 10) และถ้า a b (mod n), จะได้ว่า a + c b + c (mod n) และ ad bd (mod n).
Floors and ceilings For any real number x, we denote the greatest integer less than or equal to x by 𝑥 (read “the floor of x”) and the least integer greater than or equal to x by 𝑥 (read “the ceiling of x”). For all real x, 𝑥−1< 𝑥 ≤𝑥≤ 𝑥 <𝑥+1 For any integer n, 𝑛/2 + 𝑛/2 =𝑛
1.2 ทบทวนคณิตศาสตร์ที่จำเป็น 1.2.5 The P Word Proof by Induction Proof by Counterexample Proof by Contradiction
Proof by Induction ประกอบด้วย 3 ขั้นตอน คือ พิสูจน์กรณีฐาน (base case) inductive hypothesis is assumed. (เป็นจริงทุกกรณีจนถึงค่า k) แสดงให้เห็นว่าทฤษฎีเป็นจริงกรณีค่าถัดไป (ปกติคือกรณีค่า k+1)
proof by Counterexample A proof by counterexample is not technically a proof. It is merely a way of showing that a given statement cannot possibly be correct by showing an instance that contradicts a universal statement. A counterexample to the statement "all prime numbers are odd numbers" is the number 2, as it is a prime number but is not an odd number.
1.2.5 The P Word Proof by contradiction: เริ่มต้นทำด้วยการสมมติให้ทฤษฎีที่เราต้องการพิสูจน์นั้นว่าไม่จริง (คือ เป็น false) และจะทำการแสดงให้เห็นว่าข้อสมมติของเราไม่เป็นความจริง ดังนั้นคำกล่าวที่เราสมมติให้ทฤษฎีที่เราต้องการพิสูจน์นั้นว่าไม่จริงนั้นจึงไม่ถูกต้อง ตัวอย่างที่นิยมยกมาแสดงกันบ่อย ๆ คือการพิสูจน์ว่าจำนวนของค่า prime number มีจำนวนเป็นอนันต์ (infinite)
If P is the proposition to be proved: 1 If P is the proposition to be proved: 1. P is assumed to be false, that is ¬P is true. 2. It is shown that ¬ P implies two mutually contradictory assertions, Q and ¬ Q. 3. Since Q and ¬ Q cannot both be true, the assumption that P is false must be wrong, and P must be true.
Infinitely Many Primes One of the first proofs by contradiction is the following gem attributed to Euclid (300BC). Theorem. There are infinitely many prime numbers. Proof. Assume to the contrary that there are only finitely many prime numbers, and all of them are listed as follows: p1, p2 ..., pn. Consider the number q = p1p2... pn + 1. The number q is either prime or composite. If we divided any of the listed primes pi into q, there would result a remainder of 1 for each i = 1, 2, ..., n. Thus, q cannot be composite. We conclude that q is a prime number, not among the primes listed above, contradicting our assumption that all primes are in the list p1, p2 ..., pn.
1.3. ทบทวน Recursion ฟังก์ชันที่นิยามในเทอมของมันเองเรียกว่า recursive n! = n * (n – 1)! หรือ Fn = Fn-1 + Fn-2
Line 3 เป็น recursive call 1.3. ทบทวน Recursion 𝑓 𝑥 =2𝑓 𝑥−1 + 𝑥 2 public class Fig01_02 { public static int f( int x ) { /*1*/ if ( x == 0 ) /*2*/ return 0; else /*3*/ return 2*f(x-1)+x*x; } public static void main( String [ ] args ) { System.out.println( "f(5) = "+f(5)); Lines 1 และ 2 จัดการกับ base case ซึ่งฟังก์ชันรู้ค่าได้โดยไม่ต้องเรียกใช้ recursion Line 3 เป็น recursive call จะมีการทำงาน recursive calls ไปเรื่อย ๆ จนกว่าจะถึงจุด base case
Nonterminating recursive method 1.3. ทบทวน Recursion Nonterminating recursive method public class Fig01_03 { public static int bad( int n ) /* 1*/ if( n == 0 ) /* 2*/ return 0; else /* 3*/ return bad( n / 3 + 1 ) + n - 1; } public static void main(String [ ]args) System.out.println( "Bad is infinite recursion" ); bad(0); bad(3); โปรแกรมนี้ใช้งานไม่ได้ทุกค่า n ยกเว้นค่า 0 เท่านั้น สำหรับโปรแกรม recursive ไม่มีสิ่งที่เรียกว่า "special case."
1.3. ทบทวน Recursion กฎพื้นฐานของ recursion: 1. Base cases. ต้องมี base cases ซึ่งหาผลลัพธ์ได้โดยไม่ต้องใช้ recursion 2. Making progress. สำหรับกรณีที่ต้องหาผลลัพธ์แบบ recursively, จะเป็น recursive call สำหรับในกรณีที่ทำให้เข้าสู่ base case 3. Design rule. ถือว่า recursive calls ทั้งหมดทำงานได้ ข้อนี้หมายความว่าโปรแกรมแบบ recursive ทำให้เราไม่ต้องสนใจกับรายละเอียดการทำงานภายในของโปรแกรม 4. Compound interest rule. อย่าทำงานหาค่าตัวเดียวกันซ้ำ ๆ โดยการเรียก recursive calls แยกจากกัน
การพิมพ์ตัวเลข public class Fig01_04 { 1.3. ทบทวน Recursion การพิมพ์ตัวเลข public class Fig01_04 { public static void printDigit( int n ) { System.out.print( n ); } public static void printOut( int n ) /* Print nonnegative n */ { if ( n >= 10 ) printOut( n / 10 ); printDigit( n % 10 ); public static void main( String [ ] args ) { printOut( 1369 ); System.out.println( );
ถ้าต้องการพิมพ์ 76234 เราต้องพิมพ์ 7623 ก่อนจากนั้นจึงตามด้วย 4 การพิมพ์ตัวเลข ถ้าต้องการพิมพ์ 76234 เราต้องพิมพ์ 7623 ก่อนจากนั้นจึงตามด้วย 4 ในขั้นตอนที่สองข้างบนทำได้ง่ายด้วยคำสั่ง print_digit(n%10) อย่างไรก็ตามการทำงานขั้นตอนแรก (คือพิมพ์ 7623) นั้นเป็นปัญหาเดียวกับปัญหาเดิม (คือ พิมพ์ 76234) กรณี base case คือ print_digit(n) ถ้า 0 n 10 ฟังก์ชัน print_out(n) ใช้สำหรับทุกค่าตัวเลขจาก 0 ถึง 9, และค่าที่มากกว่านี้ก็จะมีนิยามในเทอมของตัวเลขบวกที่น้อยกว่ามัน
Recursion และ Induction THEOREM 1.4: The recursive number-printing algorithm is correct for n 0. PROOF: ขั้นแรก, ถ้า n มีเพียง 1 digit, โปรแกรมทำงานได้ถูกต้อง เนื่องจากเพียงแต่เรียกใช้ print_digit เท่านั้น สมมติให้ print_out ทำงานได้ถูกต้องทุกค่า k digit หรือที่น้อยกว่า ตัวเลขที่มี k + 1 digit เขียนได้ด้วย k digit แรกแล้วตามด้วย least significant digit. แต่ตัวเลขที่เกิดจาก k digit แรกนั้น คือ 𝑛/10 , ซึ่งจาก hypothesis ถือว่าถูกพิมพ์ได้ถูกต้อง และ digit สุดท้าย คือ n mod 10, ดังนั้นโปรแกรมจึงพิมพ์ค่า (k + 1) digit ได้ถูกต้อง นั่นคือ ด้วย induction, จึงพิมพ์ตัวเลขได้ถูกต้อง
ตัวอย่าง Recursion: Factorial public class FactorialCalculator { public long factorial( long number ) { if ( number <= 1 ) // base case return 1; else // recursion step return number * factorial( number - 1 ); } // end method factorial public void displayFactorials() { for (int counter = 0;counter <= 10;counter++ ) System.out.printf("%d! = %d\n",counter, factorial(counter) ); } } // end class FactorialCalculator
public class FactorialTest { public static void main( String args[] ) { FactorialCalculator factorialCalculator = new FactorialCalculator(); factorialCalculator.displayFactorials(); } // end method main } // end class FactorialTest
Recursive evaluation of 5! 5*4! 4*3! 3*2! 2*1! 1 5! 5*4! 4*3! 3*2! 2*1! 1 Sequence of recursive call Values returned from recursive call