다음 문제에 대해 서브 큐빅 알고리즘이 있습니까?


11

n×nA=(aij)

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

3
이것은 주어진 그래프에서 삼각형의 수를 세는 것만 큼 어렵습니다 . 경우 입력 행렬 코딩하는 그래프와 같은 "0"에지를 나타내고, "1"누락 된 에지 다음 나타낸다고 의 경우에만 존재를 는 노드 , 및 의해 형성된 삼각형 이며, 그렇지 않으면 입니다. max(aij,aik,ajk)=0ijk1
Jukka Suomela

1
삼각형 계산을 위해 유일하게 알려진 서브 큐빅 알고리즘은 빠른 행렬 곱셈을 기반으로한다고 생각합니까? 이 문제에서 이러한 기술을 적용하는 것은 까다로울 수 있습니다. 또한 실용적인 것을 찾고 있다면 빠른 행렬 곱셈을 기반으로하는 것은 도움이되지 않습니다.
Jukka Suomela

답변:


3

시간에 작동하는 실질적인 접근 방식이 있습니다. 여기서 는 프로세서 워드의 비트 수입니다. 주요 아이디어는 증가하는 순서 (임의로 연결 해제)로 매트릭스의 요소를 하나씩 반복하여 "전환"하는 것입니다. 트리플 의 가장 큰 요소 가 켜진 순간을 고려하십시오 . 간단히하기 위해, 상기 요소가 라고 가정 해 봅시다 . 마지막 요소가 켜졌을 때 트리플 값을 답에 추가하는 것이 당연합니다. 우리가 할 수의 수를 계산해야한다 그래서 '등이야 그 및O(n3/w)wI , J , I (K)은 , j는 k는 I J의 K를 케이 J의 K를 I J O ( N )aij,aik,ajkaijkaikajk이미 켜져 있습니다 (즉, 트리플의 수입니다. 여기에서 가 가장 큰 요소이므로 지금은 완전히 켜져 있습니다). 여기서 비트 최적화를 사용하여 순진한 구현 속도를 높일 수 있습니다 .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;
}

당신이 비트 최적화가 부정 사용을 고려하는 경우, 당신은 항복, 여기에 같은 결과에 네 러시아 방법을 사용할 수 있습니다 (때문에 실용성해야 알고리즘, 가장 현대적인 하드웨어에 꽤 큰) 이론적으로 더 좋습니다. 실제로 선택 하고 행렬의 각 행을 정수를 에서 의 배열로 유지합니다 . 여기서 번째 숫자는 이르기까지 열의 비트 배열에 대응 포함하는 단독으로O(n3/logn)wblog2nnb02b1iibmin(n,(i+1)b)0색인. 시간에 이러한 두 블록의 스칼라 곱을 미리 계산할 수 있습니다 . 하나의 정수만 변경하기 때문에 행렬에서 위치를 업데이트하는 것이 빠릅니다. 행 및 의 스칼라 곱을 찾으려면 해당 행에 해당하는 배열을 반복하고 테이블에서 해당 블록의 스칼라 곱을 찾아서 얻은 곱을 요약하십시오.O(22bb)ij

위의 단락은 정수 을 갖는 연산 이 시간이 가정합니다 . 그것은 일반적으로 알고리즘의 비교 속도를 실제로 변경하지 않기 때문에 매우 일반적인 가정입니다 (예를 들어, 우리가 그 가정을 사용하지 않으면 무차별 강제 방법은 실제로 시간에 작동합니다 (여기서 가 상수 대해 적어도 까지의 절대 값을 갖는 정수 값을 사용하는 경우 비트 연산으로 시간을 측정합니다 (그렇지 않으면 행렬 곱셈)); 그러나 위에서 제안한 네 가지 러시아 방법은nO(1)O(n3logn)aijnεε>0O(nε)O(n3/logn)이 경우 크기 숫자를 가진 연산 ; 따라서 비트 연산을 수행하므로 모델 변경에도 불구하고 무차별 대입보다 여전히 우수합니다.O(logn)O(n3)

그러나 접근법의 존재에 대한 질문 은 여전히 ​​흥미 롭습니다.O(n3ε)

이 답변에 제시된 기법 (비트 최적화 및 4 명의 러시아인 방법)은 결코 독창적이지 않으며 박람회의 완성을 위해 여기에 제시됩니다. 그러나 그것들을 적용하는 방법을 찾는 것은 사소한 것이 아닙니다.


첫째, 귀하의 제안은 실제로 실용적인 측면에서 도움이되는 것처럼 보입니다. 내 유스 케이스에서 시도해 볼 수도 있습니다. 감사! 둘째, 알고리즘 계산 ​​복잡도는 고정 너비 숫자 유형에 대해 여전히 입니다. 접근 방식 에 대해 자세히 설명해 주 시겠습니까? 나는 우리의 스칼라 제품을 찾을 수있는 방법을 얻을하지 않습니다 및 보다 빠르게 (우리는 모든 요소에 액세스하는 경우 필요한 것입니다). O(n3)O(n3/logn)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그 크기는 컴파일 타임 상수가 될 필요가 없기 때문에, 더 나은이 경우입니다. 이 세부 사항을 추가하고 4 명의 러시아인 접근 방식을 명확히하기 위해 답변을 편집합니다.
Kaban-5

1
좋아, 이것은 나에게 견고하게 보인다. 작은 점 : 이분법 적 모델은 에서 기계어에 대한 연산을 수행 할 수 있다고 가정하기 때문에 스칼라 곱을 미리 계산할 필요가 없습니다. 실제로이 모델에서는 이라고 가정 하므로 는 좋습니다. 그리고 당신이 말했듯이, 스칼라 제품을 사전 계산하는 것은 실질적으로 의미가 없습니다 (배열 조회는 이진 연산보다 느릴 것입니다). O(1)wlog2nO(n3/w)O(n3/logn)
user89217
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.