User Tools

Site Tools


Sidebar

1. บทนำ

2. การวิเคราะห์ Algorithm

3. List, Stack and Queue

4. Tree

5. Hashing

6. Priority Queues

7. การจัดเรียง (Sorting)

8. The Disjoint Set

9. Graph Algorithms

dsa:slinear

7.9. การจัดเรียงข้อมูลที่ใช้เวลาเป็น linear time

ในบางกรณีที่เป็นกรณีพิเศษเราอาจทำการจัดเรียงข้อมูลได้ โดยใช้เวลาเป็น linear time ตัวอย่างอย่างง่ายได้แก่ bucket sort โดยจะต้องประกอบด้วยสารสนเทศเพิ่มเติมเพื่อให้มันทำงานได้

7.9.1 Bucket Sort

ถ้าอินพุต $A_1, A_2, ... , A_n$ เป็นเลขจำนวนเต็มบวกที่มีค่าน้อยกว่า M เราจะกระจายอินพุต เข้าไปในถัง ( bucket ) แล้ว เรียงข้อมูล ในแต่ละถัง โดยอาจใช้การเรียงแบบธรรมดา หรือ Recursive ใช้ Bucket sort ก็ได้ แล้วนำข้อมูลที่เรียงในแต่ละถังมาต่อกัน เป็นการเรียงข้อมูลทั้งหมด ถังอาจใช้ linklist หรือ arraylist ในการเก็บข้อมูล โดยแต่ละถังอยู่ในอาร์เรย์

กำหนดให้ มี k ถังในอาเรย์ เราสามารถนำข้อมูล $A_i$ เข้าไปเก็บในถังที่ $\left\lfloor k\bullet A_i/M\right\rfloor$ โดยการอ่านข้อมูลทั้งหมด จะมีการทำงานเป็น $O(n)$ จากนั้นเรียงข้อมูลในแต่ละถัง ในกรณี worst case ข้อมูลทุกตัว เข้าไปอยู่ในถังเดียวกัน จะทำให้ใช้เวลาเป็น $O(n^2)$ ถ้าข้อมูลกระจายอย่างเท่าๆ กัน จะทำให้ในแต่ละถัง มีข้อมูล $\left\lceil n/k\right\rceil$ ซึ่งใช้เวลารวม $k\bulletΟ(\lceil n/k \rceil ^2)$ จากนั้น นำข้อมูลมาต่อกัน โดยการรวม k ถัง ถ้าต้อง copy ข้อมูลออกมา จะทำงานเป็น $O(n)$ ซึ่งโดยรวม ทั้งกระบวนการ จะใช้เวลา $Ο(n)+k∙Ο(\lceil n/k \rceil ^2)+Ο(n)$

ถ้า $k = O(n)$ เช่น $n/2$ หรือ $n/100$ และข้อมูลมีการกระจายอย่างสม่ำเสมอในแต่ละถังจะมีข้อมูลเฉลี่ยเป็น ค่าคงที่ ทำให้เทอม $\ k\bulletΟ( \lceil n/k \rceil ^2)$ มีค่า $O(n) O(1)$ ดังนั้น โดยรวม bucket sort จะทำงานเป็น $O(n)$

ตัวอย่าง ให้อินพุต เป็น 78, 17 , 39, 26, 72, 94, 21, 12, 23, 68 และกำหนดให้ อินพุต มีค่าไม่เกิน 100 เลือก k =10 นำอินพุตใส่ใน bucket เป็น link list และเรียงข้อมูลใน bucket ได้ตามรูปที 7.19


รูปที่ 7.19 Bucket ของ Bucket sort ในรูป อาร์เรย์ ของ linklist

แม้ว่าจะคล้ายกับ การนำข้อมูลเข้า Hash table แต่ การ mapping เป็น index ของอาร์เรย์ ไม่เหมือนกัน ในกรณีของ Bucket sort function ที่ใช้ จะเรียง bucket ด้วย ข้อมูลใน bucket [ 0 ] จะน้อยกว่าข้อมูล ใน bucket [ 1 ]

7.9.2 Counting Sort

ใช้แนวความคิดเดียวกันกับ bucket sort ในการ mapping ข้อมูล แต่ใช้การนับแทนการนำข้อมูลเข้าไปใน bucket ทั้งนี้การนับทำให้เป็นการเรียงข้อมูลเพียงบางส่วน จึงใช้เป็นส่วนหนึ่งในการเรียงข้อมูลใน Radix Sort ที่จะกล่าวต่อไป

Counting Sort ใช้เรียงข้อมูลจำนวนเต็ม ที่มีค่าไม่เกิน M โดยการสร้างอาร์เรย์ ขนาด M (count[M]) เพื่อใช้นับข้อมูล โดยการนับข้อมูล $A_i$ ในตำแหน่งค่าของ $A_i$ ( count[$A_i$]++ ) เมื่อนับข้อมูลทั้งหมดแล้ว จะคำนวณตำแหน่งของข้อมูลที่ได้จากการนับ แล้วนำข้อมูลไปใส่ใน อาร์เรย์ใหม่ตามตำแหน่ง ถ้าข้อมูลเป็น จำนวนเต็มอย่างเดียวไม่ใช่ key ของ Object สามารถพิมพ์ข้อมูลได้ เมื่อนับข้อมูลเสร็จ

ตัวอย่าง ให้อินพุต เป็นข้อมูลจำนวนเต็ม มีค่าไม่เกิน 5 ตามรูปด้านล่าง

1 2 3 4 5 6 7 8
A 2 5 3 0 2 3 0 3

สร้างอาร์เรย์ Count และนับจำนวนข้อมูลใน A ตามรูปด้านล่าง

0 1 2 3 4 5
Count 2 0 2 3 0 1

ถ้าข้อมูลเป็นจำนวนเต็มอย่างเดียว จะพิมพ์ผลลัพธ์ได้ เป็น 0 0 2 2 3 3 3 5 โดยดูจากการนับ

ถ้าข้อมูลเป็น object โดยมีจำนวนเต็มเป็นค่า key เพื่อเรียงข้อมูลจะคำนวณหาค่า Offset โดย บวกจำนวนสะสมจากตัวหน้า จะได้ตามรูปด้านล่าง

0 1 2 3 4 5
Offset 2 2 4 7 7 8

ทั้งนี้ อาจคำนวณในอาร์เรย์ Count เป็นค่า Offset เลยก็ได้ เนื่องจากไม่ได้ใช้ค่า count อีกแล้ว อีกทั้งจะมีขนาดเท่ากัน ค่า Offset จะเป็นค่าตำแหน่งท้ายสุดที่ข้อมูลค่าตาม index ของอาร์เรย์ จะอยู่

สร้างอาร์เรย์ B ขนาดเท่ากับ อาร์เรย์ A เพื่อเก็บค่าที่เรียงกัน อ่านค่าอาร์เรย์ A จากตัวสุดท้าย ไปหาตัวแรก ดูค่า offset โดยการ mapping ค่ามายัง index ของ offset จะได้ตำแหน่ง ย้ายข้อมูลจาก อาร์เรย์ A มายัง อาร์เรย์ B ที่ตำแหน่งดังกล่าว และ ลดค่า offset ตัวอย่างตามรูปที่ 7.20

รูปที่ 7.20 แสดงการย้ายข้อมูล ของ Counting Sort

การที่อ่าน อาร์เรย์ A จากด้านหลังมาด้านหน้า เพื่อให้เป็น Stable sort คือข้อมูลที่เท่ากัน จะเรียงเหมือนเดิม

จะเห็นได้ว่า การทำงาน จะนับข้อมูลทั้งหมด 1 รอบ ปรับปรุงข้อมูลใน Count อาร์เรย์ และ ย้ายข้อมูลทั้งหมด อีก 1 รอบ ดังนั้น การทำงานจึง มี Runtime เป็น $O(n) + O(M) + O(n)$ โดย M เป็นขนาดของ Count อาร์เรย์ และ ต้องการเนื้อที่เก็บข้อมูลเพิ่ม เท่ากับ จำนวนอินพุต

M อาจจะมีขนาดใหญ่ตามค่าของอินพุตที่เป็นไปได้ โดยมากแล้ว ใช้การแบ่งค่า ตามหลักของค่าอินพุตออกเป็นส่วนๆ เพื่อให้ M มีขนาดเล็กลง จำนวนเต็มอาจจะมองในฐาน 10 เป็นหลักหน่วย หลักสิบ แต่ละหลักมี 10 ตัว หรือ อาจจะมองในฐาน 2 เป็น byte แรก, byte ที่ 2 แต่ละ byte มี 256 ตัว แล้วทำงานหลายรอบตาม Radix sort

7.9.3 Radix Sort

เป็นการเรียงข้อมูล ที่ใช้กับ เครื่องเรียงการ์ด (ก่อนคอมพิวเตอร์) โดยการเรียงการ์ด ทีละหลัก โดยให้เรียงจากหลักที่สำคัญน้อย ไปหา หลักที่สำคัญมาก โดยเรียงแบบ Stable sort เมื่อเรียงครบทุกหลัก ข้อมูลจะเรียงกัน ได้นำหลักการนี้มาใช้ในโปรแกรมในลักษณะเดียวกัน

ตัวอย่าง มีอินพุตเป็นจำนวนเต็ม 3 หลัก ทำการ เรียงด้วย Counting sort ในแต่ละหลัก 3 ครั้ง โดยแต่ละหลัก Count อาร์เรย์ มี่ขนาด 10 เพราะใช้เลขฐาน 10 ซึ่งเป็นเลขได้ 0-9 ตามรูปที่ 7.21

5 0 3 1 7 0 5 0 3 0 6 1
0 8 7 0 6 1 7 0 3 0 8 7
5 1 2 5 1 2 9 0 8 1 5 4
0 6 1 6 1 2 5 0 9 1 7 0
9 0 8 5 0 3 5 1 2 2 7 5
1 7 0 6 5 3 6 1 2 4 2 6
8 9 7 7 0 3 4 2 6 5 0 3
2 7 5 1 5 4 6 5 3 5 0 9
6 5 3 2 7 5 1 5 4 5 1 2
4 2 6 7 6 5 0 6 1 6 1 2
1 5 4 4 2 6 7 6 5 6 5 3
5 0 9 0 8 7 1 7 0 6 7 7
6 1 2 8 9 7 2 7 5 7 0 3
6 7 7 6 7 7 6 7 7 7 6 5
7 6 5 9 0 8 0 8 7 8 9 7
7 0 3 5 0 9 8 9 7 9 0 8
รอบที่ 1 รอบที่ 2 รอบที่ 3 ผลลัพธ์
หลักหน่วย หลักสิบ หลักร้อย

รูปที่ 7.21 Radix sort

ถ้าอินพุตมีจำนวนหลักมากกว่านี้ เช่นเป็นตัวเลข 13 หลัก (รหัสประจำตัวประชาชน) การใช้ Counting sort อาจทำทีละ 4 หลักก็ได้ ซึ่งจะลดการทำงาน จาก 13 รอบเหลือเพียง 4 รอบ แต่ Count อาร์เรย์ จะมีขนาด เป็น 10,000 ช่อง การเรียงข้อมูลแต่ละหลัก อาจใช้วิธีการอื่นที่ไม่ใช่ Counting sort ก็ได้ แต่ต้องเป็น Stable sort กล่าวคือ ข้อมูลที่มีค่าเท่ากัน ตัวที่มาก่อนในอินพุต จะมาก่อนใน ผลลัพธ์

จะเห็นได้ว่า Runtime เท่ากับ จำนวนรอบ คูณกับ Runtime ของการ Sort แต่ละรอบ

dsa/slinear.txt · Last modified: 2021/09/26 22:21 by wasu

Page Tools