อัลกอริทึม subcubic มีอยู่สำหรับปัญหาต่อไปนี้หรือไม่?


11

ให้สมมาตรจริงเมทริกซ์มีอัลกอริทึมที่คำนวณผลรวมทั้งหมดมีความซับซ้อนของเวลาดีกว่า ?A = ( a ฉันj ) i , j , k max ( a ฉันj , a ฉันk , a j k ) 1 ฉัน< j < k n O ( n 3 )n×nA=(aij)

i,j,kmax(aij,aik,ajk)
1i<j<knO(n3)

3
โปรดทราบว่านี่เป็นเรื่องยากอย่างน้อยเท่ากับการนับจำนวนสามเหลี่ยมในกราฟที่กำหนด หากเมทริกซ์อินพุตของคุณเข้ารหัสกราฟเช่นว่า "0" หมายถึงขอบและ "1" หมายถึงขอบที่หายไปดังนั้นmax(aij,aik,ajk)=0ถ้าหากมี เป็นรูปสามเหลี่ยมที่เกิดขึ้นจากต่อมน้ำi , jและkและมิฉะนั้นจะเป็น11
Jukka Suomela

1
ฉันคิดว่าอัลกอริทึม subcubic ที่สำคัญเพียงอย่างเดียวที่รู้จักกันดีสำหรับการนับสามเหลี่ยมจะขึ้นอยู่กับการคูณเมทริกซ์ที่รวดเร็ว? อาจเป็นการยากที่จะใช้เทคนิคเหล่านี้กับปัญหานี้ นอกจากนี้หากคุณกำลังมองหาบางสิ่งที่สามารถนำไปใช้ได้จริงอะไรก็ตามที่เป็นผลมาจากการคูณเมทริกซ์ที่รวดเร็วจะไม่เป็นประโยชน์
Jukka Suomela

คำตอบ:


3

มีวิธีการปฏิบัติที่ค่อนข้างใช้งานได้ในเวลาโดยที่คือจำนวนบิตในคำของโปรเซสเซอร์ แนวคิดหลักคือคุณต้องวนซ้ำองค์ประกอบของเมทริกซ์ทีละตัวตามลำดับที่เพิ่มขึ้น (หยุดความสัมพันธ์โดยพลการ) และ "เปิดใช้งาน" พิจารณาช่วงเวลาที่องค์ประกอบที่ใหญ่ที่สุดในสามเปิดอยู่ สำหรับความเรียบง่ายสมมติว่ากล่าวว่าองค์ประกอบ{IJ} เป็นเรื่องธรรมดาที่จะเพิ่มมูลค่าของคำตอบสามคำตอบทันทีเมื่อองค์ประกอบสุดท้ายถูกเปิด ดังนั้นเราต้องนับจำนวนเป็นไปได้ที่และO(n3/w)waij,aik,ajkaijkaikajk a i j O ( n )มีการเปิดใช้งานแล้ว (นั่นคือจำนวนของอเนกประสงค์ที่นี่เป็นองค์ประกอบที่ใหญ่ที่สุดดังนั้นพวกเขาจึงเปิดอย่างสมบูรณ์ในขณะนี้) ที่นี่เราสามารถเพิ่มความเร็วการใช้งานไร้เดียงสาโดยใช้การปรับบิตให้เหมาะสมaijO(n)

สำหรับรายละเอียดคุณสามารถอ้างถึงการใช้งานต่อไปนี้ใน C ++ 11 ที่ควรใช้กับ , (มันไม่ได้รับการปรับให้เหมาะสมมาก แต่มันยังคงเต้นได้อย่างไร้เดียงสาสำหรับด้วยระยะขอบขนาดใหญ่อย่างน้อยในเครื่องของฉัน)n5000|aij|109n=5000

// 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(n3/logn)wblog2nnb02-1ผมผมนาที(n,(ผม+1))0O(22bb)ฉันj-indexation เราสามารถ precalculate ผลิตภัณฑ์เกลาของทุกสองช่วงตึกดังกล่าวในเวลา การอัปเดตตำแหน่งในเมทริกซ์นั้นรวดเร็วเนื่องจากเรากำลังเปลี่ยนเลขจำนวนเต็มเพียงหนึ่งค่า ในการค้นหาผลิตภัณฑ์สเกลาร์ของแถวและเพียงวนซ้ำอาร์เรย์ที่สอดคล้องกับแถวนั้นให้ค้นหาผลิตภัณฑ์สเกลาร์ของบล็อกที่เกี่ยวข้องในตารางและสรุปผลรวมของผลิตภัณฑ์ที่ได้รับO(22)ผมJ

ย่อหน้าข้างต้นสันนิษฐานว่าการดำเนินการกับจำนวนเต็มใช้เวลาเวลา มันเป็นข้อสันนิษฐานทั่วไปเนื่องจากโดยทั่วไปแล้วจะไม่เปลี่ยนความเร็วเปรียบเทียบของอัลกอริทึม (ตัวอย่างเช่นถ้าเราไม่ใช้สมมติฐานนั้นวิธีการเดรัจฉานบังคับใช้งานได้จริงใน เวลา (ที่นี่ เราวัดเวลาในการปฏิบัติการบิต) หากรับค่าจำนวนเต็มด้วยค่าสัมบูรณ์อย่างน้อยสูงสุดสำหรับค่าคงที่ (และเราสามารถแก้ปัญหาด้วยคูณเมทริกซ์อย่างไรก็ตาม) อย่างไรก็ตามวิธีการแบบรัสเซียสี่ข้อที่แนะนำไว้ข้างต้นใช้nO(1)O ( n 3บันทึกn ) ฉันเจ n ε ε > 0 O ( n ε ) O ( n 3 / log n ) O ( บันทึกn ) O ( n 3 )O(n3เข้าสู่ระบบn)aผมJnεε>0O(nε)O(n3/เข้าสู่ระบบn)มีจำนวนขนาดในกรณีนั้น ดังนั้นจึงทำให้การดำเนินงานบิตซึ่งยังดีกว่าแรงเดรัจฉานแม้จะมีการเปลี่ยนแปลงของรูปแบบ)O(เข้าสู่ระบบn)O(n3)

คำถามเกี่ยวกับการมีอยู่ของวิธียังคงน่าสนใจO(n3-ε)

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


ประการแรกคำแนะนำของคุณดูเหมือนจะมีประโยชน์ในแง่การปฏิบัติฉันอาจลองใช้ในกรณีที่ใช้งาน ขอบคุณ! ประการที่สองความซับซ้อนของอัลกอริธึมการคำนวณของคุณยังคงเป็นสำหรับชนิดตัวเลขที่มีความกว้างคงที่ คุณช่วยอธิบายเกี่ยวกับวิธีการไหม? ฉันไม่ได้รับวิธีที่เราสามารถหาผลิตภัณฑ์สเกลาร์และเร็วกว่า (ซึ่งจะต้องใช้ถ้าเราเข้าถึงองค์ประกอบทั้งหมดของพวกเขา) O ( n 3 /บันทึกn ) O ( n )O(n3)O(n3/เข้าสู่ระบบn)mat[i]mat[j]O(n)
user89217

นอกจากนี้โค้ดของคุณไม่ได้กำหนดmatว่าสิ่งใดสำคัญ ฉันเข้าใจว่าสามารถนิยามได้อย่างไร แต่ฉันสงสัยว่า(mat[i] & mat[j]).count()จะทำงานได้ตามที่ต้องการกับคอนเทนเนอร์ STL ใด ๆ หรือไม่
user89217

1
เกี่ยวกับmat- std::vector<boost::dynamic_bitset<int64_t>>ผมคิดว่าเราต้องใช้
user89217

เกี่ยวกับmat: ใช่ฉันมีบิตเซ็ตมาตรฐานในใจจริง ๆ แต่boost::dynamic_bitsetจะดีกว่าในกรณีนี้เนื่องจากขนาดไม่จำเป็นต้องมีค่าคงที่เวลาคอมไพล์ จะแก้ไขคำตอบเพื่อเพิ่มรายละเอียดนี้และเพื่อชี้แจงแนวทางรัสเซียทั้งสี่
Kaban-5

1
เยี่ยมมากนี่ดูแข็งแกร่งสำหรับฉัน ประเด็นย่อย: เนื่องจากโมเดล transdichotomous สมมติว่าเราสามารถดำเนินการกับคำของเครื่องจักรในจึงไม่จำเป็นต้องคำนวณผลิตภัณฑ์สเกลาร์ล่วงหน้าอีกต่อไป อันที่จริงแบบจำลองจะถือว่าดังนั้นอย่างน้อยก็ดีเท่ากับw log 2 n O ( n 3 / w ) O ( n 3 / log n )O(1)Wเข้าสู่ระบบ2nO(n3/W)O(n3/เข้าสู่ระบบn)n) และอย่างที่คุณพูดผลิตภัณฑ์สเกลาร์ที่ผ่านการปรับสภาพไม่เหมาะสม (การค้นหาอาร์เรย์จะช้ากว่าไบนารีออปชั่น)
user89217
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.