• 8 min

ตัวหารร่วมมาก (G.C.D.) เขียนด้วยตัวเองทำยังไงดี

ตอนคิด ห.ร.ม. ในกระดาษก็ไม่ยากนะ แต่ในโปรแกรมคอมพิวเตอร์เขาเขียนกันยังไง?

เป็ดไอคอนของเรื่องเล่าชาวอัลกอ Practical Algorithms
Practical Algorithms: เรื่องเล่าชาวอัลกอ
เพจที่อยากให้คนไทยมีเนื้อหาอัลกอริทึมดีๆ ให้ได้อ่านกัน
เป็ดชาวอัลกอกำลังนั่งอ่านนั่งสือท่ามกลางกองหนังสือรอบตัว

โจทย์

ในคณิตศาสตร์ ตัวหารร่วมมาก หรือ ห.ร.ม. (greatest common divisor: gcd) ของจำนวนเต็มสองจำนวนซึ่งไม่เป็นศูนย์พร้อมกัน คือจำนวนเต็มที่มากที่สุดที่หารทั้งสองจำนวนลงตัว

โจทย์ จงเขียนโปรแกรมหา ห.ร.ม. ของจำนวนเต็ม 2 จำนวนที่กำหนดให้


ข้อมูลนำเข้า บรรทัดแรกระบุจำนวนเต็มสามจำนวน aa และ bb มีค่าไม่เกิน 2×1092\times10^9

ข้อมูลส่งออก มีบรรทัดเดียวเป็นห.ร.ม. ของทั้งสองจำนวน


ตัวอย่างข้อมูลนำเข้าและข้อมูลส่งออก

InputOutput
12 142
7 31

วิเคราะห์โจทย์

เชื่อว่าหลายคนที่เขียนโปรแกรมมานิดหน่อยก็คงรู้อยู่แล้วว่าการหา ห.ร.ม. เนี่ยมันมีฟังก์ชัน built-in มาให้ใน C++ หรือ ภาษาอื่นๆอยู่แล้ว (Python นี่ยิ่งง่ายเลย)

แต่ถ้าใช้อันนั้นมันก็ไม่ได้ทำให้เราเข้าใจวิธีการหา ห.ร.ม. อ่ะนะ ดังนั้นรอบนี้เลยจะเอาวิธีอื่นๆมาใช้กัน

แบบ 1: หาตัวประกอบทั้งหมด

ข้อสังเกตแรกคือถ้าสมมติเราดูค่าตัวนึงอยู่ สมมติให้เป็น SS เป็นค่าที่เราดูอยู่ จะได้ว่า

S=p×qS=p\times q

ถ้าเราสมมติให้ pqp\leq q จะได้ว่า pp จะมีค่าไม่เกิน S\sqrt{S} เพราะถ้าเกินไปแล้วจะผิดเงื่อนไขที่สมมติขึ้นมา ทำให้ได้ว่าเราก็แค่ลูปค่า pp ตั้งแต่ 1 ถึง S\sqrt{S}

กลับมาที่โจทย์ดั้งเดิม เราจะได้ว่าการหาตัวประกอบร่วมของ aa และ bb สามารถทำได้ด้วยการลูปค่าถึง a\sqrt{a} เพื่อหาค่าตัวประกอบของ aa แล้วเอาค่าตัวประกอบแต่ละตัวของ aa มาดูค่าว่ามีตัวไหนบ้างที่เป็นตัวประกอบของ bb บ้าง แล้วเอาค่ามากที่สุดมาเป็นคำตอบ

แบบ 2: ใช้ Euclidean Algorithm

สังเกตนิดนึงก่อนโดย สมมติว่าแทนให้ gg เป็น ห.ร.ม. ของ aa และ bb และ a<ba < b

สิง่ที่เราจะได้คือ

b=a×q1+r1b = a \times q_1 + r_1

เรารู้ว่า gg หารทั้ง aa และ bb ลงตัวแปลว่า gg ก็ต้องหาร r1r_1 ลงตัวด้วย นั่นแปลว่า ห.ร.ม. ของ aa กับ bb ก็มีค่าเท่ากับ ห.ร.ม. ของ aa กับ r1r_1 ซึ่งถ้าใช้วิธีเดิมคิดต่อก็จะได้ว่า

a=r1×q2+r2a = r_1 \times q_2 + r_2

ซึ่งแน่นอนว่า gg ก็ต้องหาร r2r_2 ลงตัวด้วย นั่นแปลว่า gg ก็ยังเป็น ห.ร.ม. ของ r1r_1 กับ r2r_2 ด้วย

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

rn=rn+1×qn+0r_n = r_{n+1} \times q_n + 0

ซึ่งแปลว่า gg ก็ต้องเป็น ห.ร.ม ของ rn+1r_{n+1} และ 00 ซึ่งก็คือ rn+1r_{n+1} นั่นแหละ และก็แปลว่าขอแค่เราหา rn+1r_{n+1} ได้ เราก็สามารถหา ห.ร.ม. ของ aa และ bb ได้นั่นเอง

เขียนเป็นโค้ดกัน

เราก็จะใช้หลักการเดียวกับที่อธิบายข้างต้นเลย ก็คือ

  • เราต้องการให้ตัวหลังเป็น 0 เพื่อที่ตัวที่เหลือจะเป็นห.ร.ม. ของ a และ b
  • ถ้าตัวหลังไม่ใช่ 0 แปลว่าเรายังไม่เจอห.ร.ม. เราก็จะค่อยๆหารไปตามสมการข้างต้น
1
uint_8 _gcd(uint_8 a, uint_8 b) {
2
if (b == 0) return a;
3
return _gcd(b, a % b);
4
}

อ้างอิง

ลองทำเองได้ที่ Programming.in.th Wikipedia - Euclidean Algorithm The Euclidean Algorithm by Khan Academy
0

บทความอื่นๆ