ให้สมมาตรจริงเมทริกซ์มีอัลกอริทึมที่คำนวณผลรวมทั้งหมดมีความซับซ้อนของเวลาดีกว่า ?A = ( a ฉันj ) ∑ i , j , k max ( a ฉันj , a ฉันk , a j k ) 1 ≤ ฉัน< j < k ≤ n O ( n 3 )
ให้สมมาตรจริงเมทริกซ์มีอัลกอริทึมที่คำนวณผลรวมทั้งหมดมีความซับซ้อนของเวลาดีกว่า ?A = ( a ฉันj ) ∑ i , j , k max ( a ฉันj , a ฉันk , a j k ) 1 ≤ ฉัน< j < k ≤ n O ( n 3 )
คำตอบ:
มีวิธีการปฏิบัติที่ค่อนข้างใช้งานได้ในเวลาโดยที่คือจำนวนบิตในคำของโปรเซสเซอร์ แนวคิดหลักคือคุณต้องวนซ้ำองค์ประกอบของเมทริกซ์ทีละตัวตามลำดับที่เพิ่มขึ้น (หยุดความสัมพันธ์โดยพลการ) และ "เปิดใช้งาน" พิจารณาช่วงเวลาที่องค์ประกอบที่ใหญ่ที่สุดในสามเปิดอยู่ สำหรับความเรียบง่ายสมมติว่ากล่าวว่าองค์ประกอบ{IJ} เป็นเรื่องธรรมดาที่จะเพิ่มมูลค่าของคำตอบสามคำตอบทันทีเมื่อองค์ประกอบสุดท้ายถูกเปิด ดังนั้นเราต้องนับจำนวนเป็นไปได้ที่และ a i j O ( n )มีการเปิดใช้งานแล้ว (นั่นคือจำนวนของอเนกประสงค์ที่นี่เป็นองค์ประกอบที่ใหญ่ที่สุดดังนั้นพวกเขาจึงเปิดอย่างสมบูรณ์ในขณะนี้) ที่นี่เราสามารถเพิ่มความเร็วการใช้งานไร้เดียงสาโดยใช้การปรับบิตให้เหมาะสม
สำหรับรายละเอียดคุณสามารถอ้างถึงการใช้งานต่อไปนี้ใน C ++ 11 ที่ควรใช้กับ , (มันไม่ได้รับการปรับให้เหมาะสมมาก แต่มันยังคงเต้นได้อย่างไร้เดียงสาสำหรับด้วยระยะขอบขนาดใหญ่อย่างน้อยในเครื่องของฉัน)
// code is not very elegant,
// but should be understandable
// here the matrix a has dimensions n x n
// a has to be symmetric!
int64_t solve (int n, const vector<vector<int32_t>> &a)
{
std::vector<boost::dynamic_bitset<int64_t>> mat
(n, boost::dynamic_bitset<int64_t>(n));
vector<pair<int, int>> order;
for (int j = 1; j < n; j++)
for (int i = 0; i < j; i++)
order.emplace_back(i, j);
sort(order.begin(), order.end(),
[&] (const pair<int, int> &l, const pair<int, int> &r)
{return a[l.first][l.second] < a[r.first][r.second];});
int64_t ans = 0;
for (const auto &position : order)
{
int i, j;
tie (i, j) = position;
mat[i][j] = mat[j][i] = 1;
// here it is important that conditions
// mat[i][i] = 0 and mat[j][j] = 0 always hold
ans += (mat[i] & mat[j]).count() * int64_t(a[i][j]);
}
return ans;
}
หากคุณพิจารณาใช้การโกงบิต optimisations คุณสามารถใช้วิธี russians สี่วิธีเพื่อผลลัพธ์เดียวกันที่นี่ซึ่งให้อัลกอริทึมซึ่งควรใช้งานได้น้อยลง (เนื่องจากค่อนข้างใหญ่ในฮาร์ดแวร์ที่ทันสมัยที่สุด) แต่ในทางทฤษฎีจะดีกว่า ที่จริงเราเลือกและเก็บแต่ละแถวของเมทริกซ์เป็นอาร์เรย์ของจำนวนเต็มตั้งแต่ถึงโดยที่ -th อาร์เรย์ที่สอดคล้องกับบิตของแถวตั้งแต่รวมถึงพิเศษในO(22bb)ฉันj-indexation เราสามารถ precalculate ผลิตภัณฑ์เกลาของทุกสองช่วงตึกดังกล่าวในเวลา การอัปเดตตำแหน่งในเมทริกซ์นั้นรวดเร็วเนื่องจากเรากำลังเปลี่ยนเลขจำนวนเต็มเพียงหนึ่งค่า ในการค้นหาผลิตภัณฑ์สเกลาร์ของแถวและเพียงวนซ้ำอาร์เรย์ที่สอดคล้องกับแถวนั้นให้ค้นหาผลิตภัณฑ์สเกลาร์ของบล็อกที่เกี่ยวข้องในตารางและสรุปผลรวมของผลิตภัณฑ์ที่ได้รับ
ย่อหน้าข้างต้นสันนิษฐานว่าการดำเนินการกับจำนวนเต็มใช้เวลาเวลา มันเป็นข้อสันนิษฐานทั่วไปเนื่องจากโดยทั่วไปแล้วจะไม่เปลี่ยนความเร็วเปรียบเทียบของอัลกอริทึม (ตัวอย่างเช่นถ้าเราไม่ใช้สมมติฐานนั้นวิธีการเดรัจฉานบังคับใช้งานได้จริงใน เวลา (ที่นี่ เราวัดเวลาในการปฏิบัติการบิต) หากรับค่าจำนวนเต็มด้วยค่าสัมบูรณ์อย่างน้อยสูงสุดสำหรับค่าคงที่ (และเราสามารถแก้ปัญหาด้วยคูณเมทริกซ์อย่างไรก็ตาม) อย่างไรก็ตามวิธีการแบบรัสเซียสี่ข้อที่แนะนำไว้ข้างต้นใช้O ( n 3บันทึกn ) ฉันเจ n ε ε > 0 O ( n ε ) O ( n 3 / log n ) O ( บันทึกn ) O ( n 3 )มีจำนวนขนาดในกรณีนั้น ดังนั้นจึงทำให้การดำเนินงานบิตซึ่งยังดีกว่าแรงเดรัจฉานแม้จะมีการเปลี่ยนแปลงของรูปแบบ)
คำถามเกี่ยวกับการมีอยู่ของวิธียังคงน่าสนใจ
เทคนิค (การปรับบิตให้เหมาะสมและวิธีรัสเซียสี่วิธี) ที่นำเสนอในคำตอบนี้ไม่ได้แปลว่าเป็นต้นฉบับและถูกนำเสนอที่นี่เพื่อความสมบูรณ์ของการแสดงออก อย่างไรก็ตามการหาวิธีที่จะใช้พวกเขานั้นไม่สำคัญ
mat[i]
mat[j]
mat
ว่าสิ่งใดสำคัญ ฉันเข้าใจว่าสามารถนิยามได้อย่างไร แต่ฉันสงสัยว่า(mat[i] & mat[j]).count()
จะทำงานได้ตามที่ต้องการกับคอนเทนเนอร์ STL ใด ๆ หรือไม่
mat
- std::vector<boost::dynamic_bitset<int64_t>>
ผมคิดว่าเราต้องใช้
mat
: ใช่ฉันมีบิตเซ็ตมาตรฐานในใจจริง ๆ แต่boost::dynamic_bitset
จะดีกว่าในกรณีนี้เนื่องจากขนาดไม่จำเป็นต้องมีค่าคงที่เวลาคอมไพล์ จะแก้ไขคำตอบเพื่อเพิ่มรายละเอียดนี้และเพื่อชี้แจงแนวทางรัสเซียทั้งสี่