อัลกอริทึมของหอคอยแห่งฮานอย: Python, C++ Code

⚡ สรุปอย่างชาญฉลาด

อัลกอริทึมหอคอยฮานอยเป็นปริศนาแบบเรียกซ้ำคลาสสิกที่เคลื่อนย้ายกองแผ่นดิสก์ระหว่างหมุดสามตัวโดยไม่วางแผ่นดิสก์ขนาดใหญ่ไว้บนแผ่นดิสก์ขนาดเล็ก ซึ่งแสดงให้เห็นถึงหลักการแบ่งและพิชิตได้อย่างชัดเจน

  • 🗼 การจัดเตรียมปริศนา: มีหมุด 3 อันและแผ่นดิสก์ n แผ่นเรียงซ้อนกันโดยเรียงขนาดจากใหญ่ไปเล็กอยู่บนหมุดต้นทาง รอที่จะถูกเคลื่อนย้ายไปยังหมุดปลายทางโดยใช้หมุดช่วย
  • 📜 กฎ: แผ่นดิสก์จะเคลื่อนที่ได้ครั้งละหนึ่งแผ่นเท่านั้น และเฉพาะแผ่นดิสก์ด้านบนสุดของหมุดใดๆ เท่านั้นที่สามารถเคลื่อนที่ได้ และแผ่นดิสก์ขนาดใหญ่ไม่สามารถวางทับแผ่นดิสก์ขนาดเล็กกว่าได้
  • 🔁 แนวคิดแบบเรียกซ้ำ: ย้ายแผ่นดิสก์ n-1 แผ่นไปยังหมุดช่วย ย้ายแผ่นดิสก์ที่ใหญ่ที่สุดไปยังหมุดปลายทาง จากนั้นย้ายแผ่นดิสก์ n-1 แผ่นจากหมุดช่วยไปยังหมุดปลายทาง
  • ⏱️ ความซับซ้อนของเวลา: การแก้ปัญหาดิสก์ n แผ่นต้องใช้การเคลื่อนย้าย 2^n – 1 ครั้ง ทำให้มีความซับซ้อนของเวลาแบบเลขชี้กำลัง O(2^n) ซึ่งเพิ่มขึ้นอย่างรวดเร็วเมื่อ n เพิ่มขึ้น
  • 🧠 ความซับซ้อนของอวกาศ: สแต็กการเรียกซ้ำสามารถเก็บเฟรมได้สูงสุด n เฟรมในคราวเดียว ดังนั้นความซับซ้อนของพื้นที่ในการแก้ปัญหาแบบเรียกซ้ำจึงเป็น O(n)
  • 🛠️ การใช้งาน: การสอนเรื่องการเรียกซ้ำ (recursion), แผนการหมุนเวียนข้อมูลสำรอง (backup rotation schemes), การเคลื่อนย้ายข้อมูลแบบใช้สแต็ก (stack-based data movement), การจัดลำดับการทำงานของหุ่นยนต์ (robotics sequencer) และความเข้าใจเกี่ยวกับการออกแบบอัลกอริทึมแบบแบ่งและพิชิต (divide-and-conquer algorithm design)

อัลกอริทึมหอคอยฮานอย

หอคอยแห่งฮานอยคืออะไร?

หอคอยฮานอยเป็นปริศนาทางคณิตศาสตร์ที่ประกอบด้วยแท่งสามแท่งและแผ่นดิสก์ที่มีขนาดลดลงเรียงซ้อนกัน เรียกอีกชื่อหนึ่งว่า หอคอยแห่งพระพรหม หรือ หอคอยลูคัส เนื่องจากนักคณิตศาสตร์ชาวฝรั่งเศส เอ็ดวาร์ด ลูคัส ได้คิดค้นขึ้นในปี 1883 ปริศนานี้มีพื้นฐานมาจากตำนานเกี่ยวกับการเคลื่อนย้ายแผ่นดิสก์ทองคำระหว่างแท่งสามแท่ง

ปริศนานี้ประกอบด้วยแท่งสามแท่งและแผ่นดิสก์ที่ซ้อนกันจำนวนหนึ่ง แท่งเหล่านี้ถูกจัดเรียงเป็นหอคอยแบบวงจร โดยแผ่นดิสก์ขนาดใหญ่จะอยู่ด้านล่างและแผ่นดิสก์ขนาดเล็กจะอยู่ด้านบน

ในตอนเริ่มต้น เรามีแท่งหรือแกนสามอัน อันหนึ่ง (แท่ง A ในตัวอย่าง) มีแผ่นดิสก์วางซ้อนกันอยู่ทั้งหมด เป้าหมายคือการย้ายแผ่นดิสก์ทั้งหมดจากแท่งหนึ่ง (A) ไปยังอีกแท่งหนึ่ง (C) โดยต้องปฏิบัติตามกฎเฉพาะบางข้อ

นี่คือการตั้งค่าเริ่มต้นของปริศนา:

ปัญหาหอคอยแห่งฮานอย

ปัญหาหอคอยแห่งฮานอย

และนี่คือเป้าหมายสุดท้าย:

หอคอยฮานอย

กฎของหอคอยฮานอย

ต่อไปนี้คือหลักเกณฑ์สำคัญสำหรับการเล่นเกมหอคอยฮานอย:

  • ในขั้นเริ่มต้นของปริศนา แผ่นดิสก์ทั้งหมดจะวางซ้อนกันอยู่บนแท่งที่หนึ่ง
  • ในขั้นตอนสุดท้าย แผ่นดิสก์ทั้งหมดจากแท่งที่หนึ่งจะถูกวางซ้อนบนแท่งที่สองหรือแท่งที่สาม
  • ในแต่ละครั้งจะมีเพียงแผ่นดิสก์เดียวเท่านั้นที่สามารถเคลื่อนที่จากแท่งหนึ่งไปยังอีกแท่งหนึ่งได้
  • เฉพาะแผ่นดิสก์บนสุดบนแกนเท่านั้นที่สามารถขยับได้
  • ไม่สามารถวางแผ่นดิสก์ขนาดใหญ่ทับแผ่นดิสก์ขนาดเล็กกว่าได้

ตำนานดั้งเดิมกล่าวถึงการเคลื่อนย้ายแผ่นดิสก์ 64 แผ่น โดยนักบวชสามารถเคลื่อนย้ายแผ่นดิสก์ได้ทีละแผ่นตามกฎ ตามตำนานกล่าวว่ามีคำทำนายว่าโลกจะถึงจุดจบหากพวกเขาสามารถทำภารกิจนี้สำเร็จ ในส่วนของความซับซ้อนของเวลา เราจะแสดงให้เห็นว่าการจัดเรียงหอคอยฮานอยที่มีแผ่นดิสก์ n แผ่นนั้นต้องใช้การเคลื่อนย้าย 2^n – 1 ครั้ง

ดังนั้น หากนักบวชต้องใช้เวลา 1 วินาทีในการเคลื่อนแผ่นดิสก์หนึ่งแผ่น เวลาทั้งหมดที่ใช้ในการแก้ปริศนาจะเท่ากับ 2^64 – 1 วินาที หรือประมาณ 584,942,417,356 ปี 26 วัน 7 ชั่วโมง และ 15 วินาที

อัลกอริทึมสำหรับหอคอยแห่งฮานอย

วิธีที่พบได้บ่อยที่สุดในการแก้ปัญหาหอคอยฮานอยคือการใช้อัลกอริธึมแบบเรียกซ้ำ ขั้นแรก เราเลือกแท่งสองแท่งเป็นจุดเริ่มต้นและจุดปลายทาง ส่วนแท่งที่เหลือจะทำหน้าที่เป็นตัวช่วยหรือตัวเสริม

ต่อไปนี้เป็นขั้นตอนในการไขปริศนาหอคอยแห่งฮานอย:

  • ย้ายดิสก์ n-1 ด้านบนจากหมุดต้นทางไปยังหมุดตัวช่วย
  • ย้ายแผ่นดิสก์ที่ n จากหลักต้นทางไปยังหลักปลายทาง
  • ย้ายแผ่นดิสก์ที่เหลือ n-1 แผ่นจากหมุดช่วยไปยังหมุดปลายทาง

หมายเหตุ ถ้าเรามีดิสก์เพียงตัวเดียว เราสามารถย้ายข้อมูลจากต้นทางไปยังปลายทางได้โดยตรง

วิธีแก้ปริศนาหอคอยแห่งฮานอย

เรามาลองยกตัวอย่างอัลกอริธึมนี้สำหรับแผ่นดิสก์สามแผ่นกัน โดยให้หมุด A เป็นแหล่งกำเนิด หมุด B เป็นตัวช่วย และหมุด C เป็นปลายทาง

ขั้นตอน 1) ในขั้นต้น แผ่นดิสก์ทั้งหมดจะถูกวางซ้อนกันบนหมุด A

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด A, ปลายทาง = หมุด C, ตัวช่วย = หมุด B

ตอนนี้ เราต้องย้ายดิสก์ n-1 อันดับแรกจากแหล่งที่มาไปยังตัวช่วยเหลือ

หมายเหตุ แม้ว่าเราจะสามารถเคลื่อนย้ายดิสก์ได้ครั้งละหนึ่งแผ่นเท่านั้น แต่ขั้นตอนนี้จะลดปัญหาที่มี 3 ดิสก์ให้เหลือเพียงปัญหาที่มี 2 ดิสก์ ซึ่งสามารถจัดการได้ด้วยการเรียกซ้ำ

ขั้นตอน 2) เนื่องจากเราเรียกฟังก์ชันแบบเรียกซ้ำจากจุด A โดยมีจุด B เป็นปลายทาง เราจึงใช้จุด C เป็นตัวช่วย

โปรดสังเกตว่าเรากลับมาที่ขั้นตอนแรกของปัญหาหอคอยฮานอยอีกครั้ง แต่คราวนี้สำหรับแผ่นดิสก์สองแผ่น เราย้ายแผ่นดิสก์ n-1 (นั่นคือ หนึ่งแผ่น) จากต้นทางไปยังตัวช่วย ซึ่งจะย้ายแผ่นดิสก์ที่เล็กที่สุดจากหลัก A ไปยังหลัก C

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด A, ปลายทาง = หมุด B, ตัวช่วย = หมุด C

ขั้นตอน 3) ตามอัลกอริทึม แผ่นดิสก์ที่ n (แผ่นที่ 2) จะถูกย้ายไปยังปลายทางที่หมุด B แล้ว

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด A, ปลายทาง = หมุด B, ตัวช่วย = หมุด C

ขั้นตอน 4) ต่อไป เราจะย้ายแผ่นดิสก์ n-1 (แผ่นดิสก์ที่หนึ่ง) จากหลักช่วย C ไปยังหลักปลายทาง B ตามขั้นตอนที่สามของอัลกอริทึม

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด A, ปลายทาง = หมุด B, ตัวช่วย = หมุด C

ขั้นตอน 5) หลังจากเสร็จสิ้นการเรียกซ้ำ เราจะกลับไปสู่การตั้งค่าก่อนหน้าในขั้นตอนแรกของอัลกอริธึม

ขั้นตอน 6) ในขั้นตอนที่สอง เราจะย้ายแผ่นดิสก์ 3 จากจุดเชื่อมต่อต้นทาง A ไปยังจุดเชื่อมต่อปลายทาง C

ในขั้นตอนนี้: ต้นทาง = หมุด A, ปลายทาง = หมุด C, ตัวช่วย = หมุด B

ขั้นตอน 7) ขั้นตอนต่อไปคือการย้ายดิสก์ที่เหลือจากตัวช่วย (หมุด B) ไปยังปลายทาง (หมุด C) ครั้งนี้เราจะใช้แหล่งที่มาเดิม (หมุด A) เป็นตัวช่วย

แก้ปริศนาหอคอยแห่งฮานอย

ขั้นตอน 8) เนื่องจากเราไม่สามารถเคลื่อนย้ายดิสก์สองแผ่นพร้อมกันได้ เราจึงเรียกฟังก์ชันแบบเรียกซ้ำสำหรับดิสก์แผ่นที่ 1 ตามที่เรากำหนด ขั้นตอนวิธีจุดหมายปลายทางในขั้นตอนนี้คือหมุด A

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด B, ปลายทาง = หมุด A, ตัวช่วย = หมุด C

ขั้นตอน 9) การเรียกซ้ำของเราเสร็จสมบูรณ์แล้ว ตอนนี้เราจะย้ายดิสก์ 2 จากต้นทางไปยังปลายทาง

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด B, ปลายทาง = หมุด C, ตัวช่วย = หมุด A

ขั้นตอน 10) ขั้นตอนสุดท้ายคือการย้ายดิสก์ที่เหลืออีก n-1 แผ่น (ดิสก์ 1) จากตัวช่วยไปยังปลายทาง

แก้ปริศนาหอคอยแห่งฮานอย

ในขั้นตอนนี้: ต้นทาง = หมุด A, ปลายทาง = หมุด C, ตัวช่วย = หมุด B

ชื่อเล่น Code สำหรับหอคอยฮานอย

START
Procedure Tower_Of_Hanoi(disk, source, dest, helper)
    IF disk == 1 THEN
        move disk from source to dest
    ELSE
        Tower_Of_Hanoi(disk - 1, source, helper, dest)
        move disk from source to dest
        Tower_Of_Hanoi(disk - 1, helper, dest, source)
    END IF
END Procedure

รหัสโปรแกรมเข้า C++

#include <bits/stdc++.h>
using namespace std;
void tower_of_hanoi(int num, string source, string dest, string helper) {
    if (num == 1) {
        cout << " Move disk 1 from tower " << source << " to tower " << dest << endl;
        return;
    }
    tower_of_hanoi(num - 1, source, helper, dest);
    cout << " Move disk " << num << " from tower " << source << " to tower " << dest << endl;
    tower_of_hanoi(num - 1, helper, dest, source);
}
int main() {
    int num;
    cin >> num;
    printf("The sequence of moves :\n");
    tower_of_hanoi(num, "I", "III", "II");
    return 0;
}

Output:

3
The sequence of moves :
Move disk 1 from tower I to tower III
Move disk 2 from tower I to tower II
Move disk 1 from tower III to tower II
Move disk 3 from tower I to tower III
Move disk 1 from tower II to tower I
Move disk 2 from tower II to tower III
Move disk 1 from tower I to tower III

รหัสโปรแกรมเข้า Python

def tower_of_hanoi(n, source, destination, helper):
    if n == 1:
        print("Move disk 1 from peg", source, "to peg", destination)
        return
    tower_of_hanoi(n - 1, source, helper, destination)
    print("Move disk", n, "from peg", source, "to peg", destination)
    tower_of_hanoi(n - 1, helper, destination, source)
# n = number of disks
n = 3
tower_of_hanoi(n, 'A', 'B', 'C')

Output:

Move disk 1 from peg A to peg B
Move disk 2 from peg A to peg C
Move disk 1 from peg B to peg C
Move disk 3 from peg A to peg B
Move disk 1 from peg C to peg A
Move disk 2 from peg C to peg B
Move disk 1 from peg A to peg B

ความซับซ้อนของหอคอยแห่งฮานอย

ต่อไปนี้คือความซับซ้อนด้านเวลาและพื้นที่ของหอคอยฮานอย:

1) ความซับซ้อนของเวลา:

เมื่อมองย้อนกลับไปที่อัลกอริทึม เราจะเรียกฟังก์ชันแบบเรียกซ้ำสำหรับดิสก์ (n-1) สองครั้งต่อการเรียกแต่ละครั้ง การเรียกซ้ำ (n-1) ครั้งจะแตกออกเป็น ((n-1)-1) ครั้ง และเป็นเช่นนี้ไปเรื่อยๆ จนกว่าเราจะถึงกรณีพื้นฐานที่มีดิสก์เพียงตัวเดียว

สำหรับแผ่นดิสก์สามแผ่น:

  • ดิสก์ 3 เรียกฟังก์ชันแบบเรียกซ้ำของดิสก์ 2 สองครั้ง
  • ดิสก์ 2 เรียกฟังก์ชันแบบเรียกซ้ำของดิสก์ 1 สองครั้ง
  • แผ่นดิสก์ที่ 1 เคลื่อนที่ด้วยเวลาคงที่ ทำให้มีเวลาในการหาคำตอบสำหรับแผ่นดิสก์ทั้งสามแผ่น

แสดงออกมาในรูปของการเกิดซ้ำ:

= 2 × (เวลาที่ใช้ในการแก้ปัญหาสำหรับแผ่นดิสก์สองแผ่น) + เวลาคงที่ในการเคลื่อนย้ายแผ่นดิสก์แผ่นที่ 3

= 2 × (2 × เวลาที่ใช้ในการแก้ปัญหาสำหรับแผ่นดิสก์แผ่นแรก + เวลาคงที่ในการเคลื่อนย้ายแผ่นดิสก์แผ่นที่ 2) + เวลาคงที่ในการเคลื่อนย้ายแผ่นดิสก์แผ่นที่ 3

= (2 × 2) × เวลาคงที่ในการเคลื่อนแผ่นดิสก์ 1 + 2 × เวลาคงที่ในการเคลื่อนแผ่นดิสก์ 2 + เวลาคงที่ในการเคลื่อนแผ่นดิสก์ 3

สำหรับดิสก์ n แผ่น จะได้ดังนี้:

2n-1 เวลาคงที่ในการเคลื่อนแผ่นดิสก์ 1 + 2n-2 × เวลาคงที่ในการเคลื่อนแผ่นดิสก์ 2 + ….

ลำดับเรขาคณิตนี้มีผลรวมเป็น O(2n – 1) ซึ่งทำให้ง่ายขึ้นเป็น โอ(2n)ซึ่งมีความซับซ้อนเชิงเวลาแบบเลขชี้กำลัง

2) ความซับซ้อนของพื้นที่:

ความซับซ้อนของพื้นที่จัดเก็บข้อมูลของ Tower of Hanoi คือ O(n) เนื่องจากการเรียกซ้ำใช้สแต็กการเรียก และความลึกสูงสุดของสแต็กเท่ากับ n ซึ่งเป็นจำนวนของดิสก์ ดังนั้นความซับซ้อนของพื้นที่จัดเก็บข้อมูลจึงเป็น O(n)

คำถามที่พบบ่อย

อัลกอริทึมหอคอยฮานอยเป็นกระบวนการแบบเรียกซ้ำที่เคลื่อนย้ายแผ่นดิสก์ n แผ่นจากหลักต้นทางไปยังหลักปลายทางโดยใช้หลักช่วยหนึ่งอัน โดยจะไม่วางแผ่นดิสก์ขนาดใหญ่ไว้บนแผ่นดิสก์ขนาดเล็กกว่าเด็ดขาด

จำนวนการเคลื่อนย้ายขั้นต่ำสำหรับแผ่นดิสก์ n แผ่น คือ 2^n – 1 แผ่นดิสก์สามแผ่นต้องใช้ 7 การเคลื่อนย้าย แผ่นดิสก์สี่แผ่นต้องใช้ 15 การเคลื่อนย้าย และแผ่นดิสก์สิบแผ่นต้องใช้ 1,023 การเคลื่อนย้าย

ความซับซ้อนของเวลาคือ O(2^n) เนื่องจากดิสก์แต่ละแผ่นที่เพิ่มเข้ามาจะทำให้งานเพิ่มขึ้นเป็นสองเท่า สมการเวียนเกิด T(n) = 2T(n-1) + 1 จะได้ผลลัพธ์เป็น 2^n – 1 ซึ่งเป็นการคูณแบบเลขชี้กำลัง

ความซับซ้อนของพื้นที่จัดเก็บข้อมูลคือ O(n) เนื่องจากสแต็กการเรียกซ้ำจะเก็บเฟรมหนึ่งเฟรมสำหรับแต่ละดิสก์ที่กำลังประมวลผล ความลึกของการเรียกซ้ำสูงสุดจะถึง n ดังนั้นหน่วยความจำเสริมที่ต้องการจึงเป็นเชิงเส้นตามจำนวนดิสก์

ใช่แล้ว วิธีการแก้ปัญหาแบบวนซ้ำจะใช้ลูปที่มีรูปแบบคงที่: ในตาเดินคี่ ให้สลับแผ่นดิสก์ที่เล็กที่สุดระหว่างหมุดแบบวนซ้ำ และในตาเดินคู่ ให้ทำการเคลื่อนไหวที่ไม่ใช่การเคลื่อนไหวที่เล็กที่สุดที่ถูกต้องเพียงอย่างเดียว

อัลกอริทึมนี้สอนเรื่องการเรียกซ้ำ สร้างแบบจำลองแผนการหมุนเวียนสำรองข้อมูลสำหรับการจัดเก็บข้อมูล กำหนดลำดับการทำงานของแขนหุ่นยนต์ และปรากฏในแบบทดสอบทางประสาทวิทยาที่วัดความสามารถในการวางแผน

เอージェนต์การเรียนรู้แบบเสริมแรงแก้ปัญหาหอคอยฮานอยโดยถือว่าการจัดเรียงแผ่นดิสก์แต่ละแบบเป็นสถานะ และการเคลื่อนไหวแต่ละครั้งเป็นการกระทำ ซึ่งเป็นเกณฑ์มาตรฐานทั่วไปสำหรับการวางแผนและการเรียนรู้เชิงนโยบายแบบลำดับชั้น

ใช่ครับ GitHub Copilot, ChatGPT และ Gemini สร้างโซลูชันหอคอยฮานอยแบบเรียกซ้ำใน Python, C++และ Javaนักพัฒนาควรตรวจสอบกรณีพื้นฐานและลำดับของอาร์กิวเมนต์อยู่เสมอ

สรุปโพสต์นี้ด้วย: