ค้นหาคู่ที่มีค่า LCM น้อยที่สุดในอาร์เรย์ที่กำหนด

ฉันเพิ่งเจอคำถามเกี่ยวกับการแข่งขันการเขียนโปรแกรมแบบแข่งขัน เมื่อระบุอาร์เรย์จำนวนเต็ม ให้ค้นหาดัชนีของคู่องค์ประกอบอาร์เรย์ที่มีค่า LCM น้อยที่สุด

ฉันรู้ว่ามีวิธีการแก้ปัญหา O(n^2) แบบ double loop ที่ไร้เดียงสา แต่ตามที่คาดไว้ มันให้ข้อยกเว้นการจำกัดเวลา ฉันได้ยินมาว่าการเขียนโปรแกรมแบบไดนามิกนั้นใช้เพื่อปรับแนวทางการบังคับเดรัจฉานให้เหมาะสม แต่ฉันไม่สามารถแบ่งปัญหานี้ออกเป็นปัญหาย่อยได้เพื่อให้มีโครงสร้างย่อยที่เหมาะสมที่สุด

ฉันสามารถขอแนวทางในการแก้ไขปัญหานี้โดยใช้ DP ได้หรือไม่ หรือมีแนวทางที่ดีกว่านี้? ขอบคุณ.


person Mushi Master    schedule 17.04.2019    source แหล่งที่มา
comment
ดำเนินการแต่ละหมายเลขล่วงหน้าเพื่อให้มีการสลายตัวของจำนวนเฉพาะอาจช่วยได้   -  person Jarod42    schedule 17.04.2019
comment
คุณกำลังประสบปัญหาอะไร?   -  person Syed Mehtab Hassan    schedule 17.04.2019
comment
ฉันคิดว่าในกรณีทั่วไป (เช่น ไม่มีตัวเลขในอาร์เรย์ที่มีปัจจัยเฉพาะร่วมกัน) คุณจะจบลงด้วย O(n²) เสมอ เพราะคุณจะต้องคำนวณผลคูณของแต่ละชุดค่าผสมและเปรียบเทียบขนาด ความเป็นไปได้เดียวที่จะปรับปรุงความเร็วของคุณอาจเป็นในกรณีพิเศษ (ยอมรับว่าเป็นเรื่องธรรมดามาก) ที่องค์ประกอบต่างๆ มีปัจจัยสำคัญหลายอย่างเหมือนกัน   -  person Alfe    schedule 17.04.2019
comment
@Alfe หากตัวเลขทั้งหมดเป็นจำนวนเฉพาะ คุณสามารถรับตัวเลขที่เล็กที่สุด 2 ตัว O(n)   -  person maraca    schedule 17.04.2019
comment
@maraca คุณพูดถูกถ้าคุณรู้ว่าพวกมันล้วนเป็นไพรม์ หากคุณมีสมมติฐานนี้ การทดสอบก็เป็นเพียง O(n) เช่นกัน แต่ฉันถือว่ากรณีปกติที่บางกรณีจะเป็น coprime และบางกรณีไม่เป็น ดังนั้นคุณต้องคิดหาบางสิ่งที่สามารถจัดการกับ สิ่งนี้ กรณี และอัลกอริทึม (ไม่เฉพาะทาง) นี้จะมี O(n²) อยู่ในรายการหมายเลขโคไพรม์ทั้งหมด   -  person Alfe    schedule 17.04.2019


คำตอบ (1)


(สมมุติว่าเป็นจำนวนบวก)

LCM ที่เล็กที่สุดน่าจะมาจากองค์ประกอบที่เล็กที่สุด ดังนั้นเมื่อตัดค่าที่ลองแล้วก็สามารถเรียงลำดับอาร์เรย์ได้

int[] v = { ... };

int minLCM = Integer.MAX_VALUE;
int bestVi = -1;
int bestVj = -1;
Arrays.sort(v);
for (int i = 0; i < v.length; ++i) {
    int vi = v[i];
    //                                  _____________
    for (int j = i + 1; j < v.length && v[j] < minLCM; ++j) {
        int vj = v[j];
        int lcm = lcm(vi, vj);
        if (lcm < minLcm) {
            minLCM = lcm;
            bestVi = vi;
            bestVj = vj;
        }
    }
}

สำหรับการตัดแต่งกิ่ง:

lcm(vi, vj) >= vi
lcm(vi, vj) >= vj
(lcm(vi, vj) <= vi*vj

การตัดแต่งกิ่งนี้สามารถทำได้ใน for-j loop ในรูปแบบ vj >= vi เท่านั้น

การตัดแต่งกิ่งที่ดีขึ้นสามารถทำได้หากคุณมีรายการตัวประกอบเฉพาะ (สูงสุด ²log value) แทน เนื่องจากต้นทุนการแยกตัวประกอบก็อาจลองใช้ (2, 3, 5, 7) เป็นต้น

การวนซ้ำที่ดีขึ้นสามารถทำได้โดยการแทนที่ทั้งสองลูปที่ซ้อนกัน เพื่อให้ลูปที่เล็กที่สุด (vi, vj) มาก่อน ด้านบน (v0, v100) มาก่อน (v1, v2) วนซ้ำ i+j ที่เพิ่มขึ้น

i j
0 1
0 2
0 3
1 2
0 4
1 3
0 5
1 4
2 3
...

(คณิตศาสตร์สำหรับตัวนับลูปโดยใช้เส้นทแยงมุมเป็นปริศนาที่ดี ควรทำจริงๆ)

แม้ว่าจะยังคง O(n²) สิ่งนี้อาจใช้ได้ผล

บางครั้งภาษาการเขียนโปรแกรมก็มีความสำคัญ และเราเห็นว่าในบริบทเหล่านั้นมักจะมีส่วนสนับสนุนของ C ในกรณีเช่นนี้ การใช้ Ruby อาจมีผลเสีย

person Joop Eggen    schedule 17.04.2019