각 크기의 Toeplitz 행렬에 대한 최대 결정 값을 찾습니다


14

고정 된 n의 경우, 항목이 0 또는 1 인 n x n Toeplitz 행렬 을 고려 하십시오. 목표는 그러한 모든 Toeplitz 행렬에 대해 최대 결정자를 찾는 것입니다.

직무

n1 이상에서 각각 0 또는 1의 항목을 갖는 n 개의 Toeplitz 행렬에 의해 최대 n 결정 값을 출력 n하십시오.

점수

n내 컴퓨터에서 2 분 동안 코드가 얻는 최대 점수입니다 . 좀 더 명확히하기 위해 코드는 총 2 분 동안 실행될 수 있지만 이는 2 분이 아닙니다 n.

타이 브레이커

두 항목이 같은 n점수를 얻는 경우 당첨 된 항목은 n내 컴퓨터에서 가장 짧은 시간에 가장 높은 항목이됩니다 . 이 기준에서 두 개의 최고 출품작이 동일한 경우 승자가 먼저 제출됩니다.

언어와 라이브러리

원하는 언어와 라이브러리를 자유롭게 사용할 수 있습니다. 코드를 실행할 수 있어야하므로 가능한 경우 리눅스에서 코드를 실행 / 컴파일하는 방법에 대한 자세한 설명을 포함하십시오.

내 컴퓨터 타이밍이 내 컴퓨터에서 실행됩니다. 이것은 AMD FX-8350 8 코어 프로세서에 표준 우분투 설치입니다. 이것은 또한 코드를 실행할 수 있어야 함을 의미합니다.

작은 답변

n = 1..10의 경우 출력은 1,1,2,3,5,9,32,56,125,315 여야합니다.

이 순서는 OEIS에 있지 않으므로 당첨 된 출품작은 새로운 출품작을 제안하게됩니다.

지금까지의 참가작

  • n=10 n=11파이썬 에서 Vioz에 의해
  • n=9Tyilo의 C
  • n=12J의 Legendre
  • n=10에 의해 Tensibai에 의해 R
  • n=14C ++ 에서 SteelRaven 제작
  • n=14C ++ 에서 RetoKoradi 작성

@AlexA. 당신 말이 맞아서 사과했습니다 운좋게도 두 문제는 매우 유사하므로 코드를 쉽게 수정할 수 있어야합니다.

@Vioz의 솔루션은 1, 1, 2, 3, 5, 9, 32로 시작하는 시퀀스를 제공합니다. 따라서 n = 5의 값은 나열된 것과 다릅니다. 다른 모든 값이 일치하므로 솔루션이 올바른 것처럼 보이며 이것은 오타입니까?
레토 코라디

@RetoKoradi 감사합니다. 결정된.

ghostbin.com/paste/axkpa의 최대 결정자를 가진 가능한 10 진 이진 토플 리츠 행렬은 다음과 같습니다 n = 1..10.
Tyilo

2
다른 사람들에게는 도움이 될 수 있지만 14를 넘어서는 확인할 수없는 관측으로, 그것은 Toeplitz 행렬의 맨 윗줄과 첫 번째 열의 각각의 수단이 최대 결정 요인에 대해 항상 0.4 <= m <= 0.6 인 것처럼 보입니다.
MickyT

답변:


3

pthread가있는 C ++

내 컴퓨터에서 1 분 안에 n = 14에 도달합니다. 그러나 이것은 2 코어 노트북 일 뿐이므로 8 코어 테스트 머신이 2 분 안에 n = 15를 완료 할 수 있기를 바랍니다. 내 컴퓨터에서 약 4:20 분이 걸립니다.

나는 정말로 더 효율적인 것을 제안하기를 바랐다. 이 한 가지고 보다 효율적 이진 행렬의 확정을 계산하는 방법이 될 수 있습니다. 결정적 계산에서 +1 및 -1 항을 계산하는 일종의 동적 프로그래밍 접근법을 생각해 내고 싶었습니다. 그러나 그것은 지금까지 꽤 잘 이루어지지 않았습니다.

현상금이 만료되기 때문에 표준 무차별 대입 접근 방식을 구현했습니다.

  • 가능한 모든 Toeplitz 행렬을 반복합니다.
  • 바뀐 행렬 쌍에서 둘 중 하나를 건너 뜁니다. 행렬은 비트 마스크 값으로 설명되므로 비트 마스크의 반전이 비트 마스크 자체보다 작은 모든 값을 건너 뛰어 간단하게 수행 할 수 있습니다.
  • 결정은 교과서 LR 분해로 계산됩니다. 약간의 성능 조정을 제외하고 대학 수치 분석법 책에서 알고리즘을 개선 한 주요 개선점은 더 간단한 피벗 전략을 사용한다는 것입니다.
  • 병렬화는 pthread로 수행됩니다. 각 스레드에서 처리 된 값에 대해 일정한 간격을 사용하면로드 밸런싱이 매우 나빠 지므로 몇 가지 스위 즐링을 도입했습니다.

나는 이것을 Mac OS에서 테스트했지만 전에 우분투에서 비슷한 코드를 사용했기 때문에 이것이 문제없이 컴파일되고 실행될 수 있기를 바랍니다.

  1. .cpp확장자 가있는 파일에 코드를 저장하십시오 ( 예 :) optim.cpp.
  2. 로 컴파일하십시오 gcc -Ofast optim.cpp -lpthread -lstdc++.
  3. 로 실행하십시오 time ./a.out 14 8. 첫 번째 인수는 최대 값 n입니다. 14 분은 확실히 2 분 안에 끝나야하지만 15 번도 시도해도 좋을 것입니다. 두 번째 인수는 스레드 수입니다. 기계의 코어 수와 동일한 값을 사용하는 것이 일반적으로 좋은 시작이지만 일부 변형을 시도하면 잠재적으로 시간이 향상 될 수 있습니다.

코드를 작성하거나 실행하는 데 문제가 있으면 알려주십시오.

#include <stdint.h>
#include <pthread.h>
#include <cstdlib>
#include <iostream>

static int NMax = 14;
static int ThreadCount = 4;

static pthread_mutex_t ThreadMutex;
static pthread_cond_t ThreadCond;
static int BarrierCount = 0;

static float* MaxDetA;
static uint32_t* MaxDescrA;

static inline float absVal(float val)
{
    return val < 0.0f ? -val : val;
}

static uint32_t reverse(int n, uint32_t descr)
{
    uint32_t descrRev = 0;
    for (int iBit = 0; iBit < 2 * n - 1; ++iBit)
    {
        descrRev <<= 1;
        descrRev |= descr & 1;
        descr >>= 1;
    }

    return descrRev;
}

static void buildMat(int n, float mat[], uint32_t descr)
{
    int iDiag;
    for (iDiag = 1 - n; iDiag < 0; ++iDiag)
    {
        float val = static_cast<float>(descr & 1);
        descr >>= 1;
        for (int iRow = 0; iRow < n + iDiag; ++iRow)
        {
            mat[iRow * (n + 1) - iDiag] = val;
        }
    }

    for ( ; iDiag < n; ++iDiag)
    {
        float val = static_cast<float>(descr & 1);
        descr >>= 1;
        for (int iCol = 0; iCol < n - iDiag; ++iCol)
        {
            mat[iCol * (n + 1) + iDiag * n] = val;
        }
    }
}

static float determinant(int n, float mat[])
{
    float det = 1.0f;
    for (int k = 0; k < n - 1; ++k)
    {
        float maxVal = 0.0f;
        int pk = 0;
        for (int i = k; i < n; ++i)
        {
            float q = absVal(mat[i * n + k]);
            if (q > maxVal)
            {
                maxVal = q;
                pk = i;
            }
        }

        if (pk != k)
        {
            det = -det;
            for (int j = 0; j < n; ++j)
            {
                float t = mat[k * n + j];
                mat[k * n + j] = mat[pk * n + j];
                mat[pk * n + j] = t;
            }
        }

        float s = mat[k * n + k];
        det *= s;

        s = 1.0f / s;
        for (int i = k + 1; i < n; ++i)
        {
            mat[i * n + k] *= s;
            for (int j = k + 1; j < n; ++j)
            {
                mat[i * n + j] -= mat[i * n + k] * mat[k * n + j];
            }
        }
    }

    det *= mat[n * n - 1];

    return det;
}

static void threadBarrier()
{
    pthread_mutex_lock(&ThreadMutex);

    ++BarrierCount;
    if (BarrierCount <= ThreadCount)
    {
        pthread_cond_wait(&ThreadCond, &ThreadMutex);
    }
    else
    {
        pthread_cond_broadcast(&ThreadCond);
        BarrierCount = 0;
    }

    pthread_mutex_unlock(&ThreadMutex);
}

static void* threadFunc(void* pData)
{
    int* pThreadIdx = static_cast<int*>(pData);
    int threadIdx = *pThreadIdx;

    float* mat = new float[NMax * NMax];

    for (int n = 1; n <= NMax; ++n)
    {
        uint32_t descrRange(1u << (2 * n - 1));
        float maxDet = 0.0f;
        uint32_t maxDescr = 0;

        uint32_t descrInc = threadIdx;
        for (uint32_t descrBase = 0;
             descrBase + descrInc < descrRange;
             descrBase += ThreadCount)
        {
            uint32_t descr = descrBase + descrInc;
            descrInc = (descrInc + 1) % ThreadCount;

            if (reverse(n, descr) > descr)
            {
                continue;
            }

            buildMat(n, mat, descr);
            float det = determinant(n, mat);
            if (det > maxDet)
            {
                maxDet = det;
                maxDescr = descr;
            }
        }

        MaxDetA[threadIdx] = maxDet;
        MaxDescrA[threadIdx] = maxDescr;

        threadBarrier();
        // Let main thread output results.
        threadBarrier();
    }

    delete[] mat;

    return 0;
}

static void printMat(int n, float mat[])
{
    for (int iRow = 0; iRow < n; ++iRow)
    {
        for (int iCol = 0; iCol < n; ++iCol)
        {
            std::cout << " " << mat[iRow * n + iCol];
        }
        std::cout << std::endl;
    }

    std::cout << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc > 1)
    {
        NMax = atoi(argv[1]);
        if (NMax > 16)
        {
            NMax = 16;
        }
    }

    if (argc > 2)
    {
        ThreadCount = atoi(argv[2]);
    }

    MaxDetA = new float[ThreadCount];
    MaxDescrA = new uint32_t[ThreadCount];

    pthread_mutex_init(&ThreadMutex, 0);
    pthread_cond_init(&ThreadCond, 0);

    int* threadIdxA = new int[ThreadCount];
    pthread_t* threadA = new pthread_t[ThreadCount];

    for (int iThread = 0; iThread < ThreadCount; ++iThread)
    {
        threadIdxA[iThread] = iThread;
        pthread_create(threadA + iThread, 0, threadFunc, threadIdxA + iThread);
    }

    float* mat = new float[NMax * NMax];

    for (int n = 1; n <= NMax; ++n)
    {
        threadBarrier();

        float maxDet = 0.0f;
        uint32_t maxDescr = 0;

        for (int iThread = 0; iThread < ThreadCount; ++iThread)
        {
            if (MaxDetA[iThread] > maxDet)
            {
                maxDet = MaxDetA[iThread];
                maxDescr = MaxDescrA[iThread];
            }
        }

        std::cout << "n = " << n << " det = " << maxDet << std::endl;
        buildMat(n, mat, maxDescr);
        printMat(n, mat);

        threadBarrier();
    }

    delete[] mat;

    delete[] MaxDetA;
    delete[] MaxDescrA;

    delete[] threadIdxA;
    delete[] threadA;

    return 0;
}

정수 산술 만 사용하여 정수 행렬의 결정자를 계산하는 흥미로운 방법이 있습니다. 일부 유한 필드의 LU 분해 (기본적으로 큰 소수). 이것이 더 빠를 지 모르겠습니다.
lirtosiast

@ThomasKwa 아마 O (n ^ 3)일까요? 부동 소수점 정밀도가 문제가되는 더 큰 행렬에 도움이 될 수 있습니다. 나는 실제로 문학을 찾지 않았다. 글쎄, 나는 빠른 검색을했고 Toeplitz 행렬의 행렬식을 계산하는 것에 관한 논문을 발견했습니다. 그러나 시도하고 구현할 시간을 약속하기에는 너무 많은 공개 질문이있었습니다.
레토 코라디

1
@Lembik 나는 오늘 나중에 그것을 시도하고 살펴볼 것입니다. 어제 다른 관련 문제에 대해 더 큰 크기를 처리하도록 변경했습니다. 지금까지 n = 30에서 최고 점수를 달성 할 수 없었으며, 휴리스틱은 5 * 10 ^ 13 이하로 멈췄습니다.
Reto Koradi

1
@Lembik 참조 paste.ubuntu.com/11915546 코드에 대한 paste.ubuntu.com/11915532 최대 N = 19 결과.
Reto Koradi

1
@Lembik 최대 n = 20의 결과는 paste.ubuntu.com/11949738에 있습니다. 이제 대각선의 가치와 회귀 여부를 빠르게 확인할 수있는 속성을 포함하여 모든 관련 솔루션을 나열합니다. m = 18,19,20에 대한 최대 값은 모두 순환 행렬입니다. 어디서나 게시하기 전에 결정자를 다시 확인하십시오.
Reto Koradi

8

제이

업데이트 : 값의 절반 이상을 검색하도록 코드가 개선되었습니다. 이제 n=12120 초 이내에 217 초에서 60 초로 편안하게 계산 됩니다.

최신 버전의 J가 설치되어 있어야합니다.

#!/usr/bin/jconsole

dim =: -:@>:@#
take =: i.@dim
rotstack =: |."0 1~ take
toep =: (dim (|."1 @: {."1) rotstack)"1
det =: -/ . * @: toep
ps =: 3 : ',/(0 1 ,"0 1/ ,.y)'
canonical =: #. >: [: #. |. " 1

lss =: 3 : 0
  shape =. (2^y), y
  shape $ ,>{;/(y,2)$0 1
)

ls =: (canonical@:lss) # lss
ans =: >./ @: det @: ls @: <: @: +:

display =: 3 : 0
echo 'n = ';y;'the answer is';ans y
)
display"0 (1 + i.13)
exit''

이것을 실행하고 2 분이되면 죽입니다. 내 결과 (MBP 2014-16GB RAM) :

┌────┬─┬─────────────┬─┐
│n = │1│the answer is│1│
└────┴─┴─────────────┴─┘
┌────┬─┬─────────────┬─┐
│n = │2│the answer is│1│
└────┴─┴─────────────┴─┘
┌────┬─┬─────────────┬─┐
│n = │3│the answer is│2│
└────┴─┴─────────────┴─┘
┌────┬─┬─────────────┬─┐
│n = │4│the answer is│3│
└────┴─┴─────────────┴─┘
┌────┬─┬─────────────┬─┐
│n = │5│the answer is│5│
└────┴─┴─────────────┴─┘
┌────┬─┬─────────────┬─┐
│n = │6│the answer is│9│
└────┴─┴─────────────┴─┘
┌────┬─┬─────────────┬──┐
│n = │7│the answer is│32│
└────┴─┴─────────────┴──┘
┌────┬─┬─────────────┬──┐
│n = │8│the answer is│56│
└────┴─┴─────────────┴──┘
┌────┬─┬─────────────┬───┐
│n = │9│the answer is│125│
└────┴─┴─────────────┴───┘
┌────┬──┬─────────────┬───┐
│n = │10│the answer is│315│
└────┴──┴─────────────┴───┘
┌────┬──┬─────────────┬────┐
│n = │11│the answer is│1458│
└────┴──┴─────────────┴────┘
┌────┬──┬─────────────┬────┐
│n = │12│the answer is│2673│
└────┴──┴─────────────┴────┘

총 런타임 = 61.83 초


재미로

┌────┬──┬─────────────┬────┐
│n = │13│the answer is│8118│
└────┴──┴─────────────┴────┘

약 210 초가 걸렸습니다.


1
테스터 참고 사항 : n = 12약 18GiB의 메모리가 필요합니다.
Dennis

이것은 매우 좋은 개선입니다. 그러나 출력은 약간 버그가 있습니다. j64-804를 사용하는 경우 n = 1을 두 번 출력하므로 더 많이 하나씩 출력됩니다.

@Lembik 아 맞아. 방금 코드를 업데이트했습니다. 다시 뛸 수 있습니까? 감사! (나는 그것을 계산하도록 설정했다 n=13. 당신은 13당신이 원하는 것을 계산하도록 마지막에서 두 번째 줄로 변경할 수있다 .)
Legendre

다시 실행했는데 여전히 12에 도달합니다.

@Lembik Hmm .. 당신은 시간 제한 내에서 12에 도달하고 그 이후 어느 시점에서 13에 도달한다고 말합니까 (예상 한 것입니다), 또는 13에 도달하지 못합니다 (즉, 12 이후에 프로그램이 멈춤)?
Legendre

4

파이썬 2

이것은 매우 간단한 해결책이며 아마도 대회에서 이기지 못할 것입니다. 그러나 이봐, 작동한다!

나는 정확히 무슨 일이 일어나고 있는지 간략하게 설명 할 것이다.

  1. 먼저에 가능한 모든 시작 행을 생성합니다 n. 예를 들어 일 때 n=2, 이것은 배열 길이 2 n + 1 을 생성하며 , 여기서 각 행은 길이 2n-1입니다. 다음과 같이 보일 것입니다 : [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]].
  2. 그런 다음 가능한 각 시작 행에 대해 n시간을 회전 하고 첫 번째 n항목을 잘라서 적절한 행렬을 생성 scipy하고 최대 값을 추적하면서 결정자를 계산하는 데 사용 합니다. 이것의 끝에서, 나는 단순히 최대 값을 출력하고, n1 씩 증가 시키고, 10 분이 지날 때까지 계속 진행합니다.

이를 실행하려면 scipy가 설치되어 있어야합니다.

편집 1 : sp3000 덕분에 itertools.product를 대신 사용하여 초기 행이 작성되는 방식이 변경되었습니다!

편집 2 : 속도를 최소화하기 위해 가능한 시작 행의 저장 공간을 제거했습니다.

편집 3 : 작동 scipy방식을보다 잘 제어 할 수 있도록 변경되었습니다 det.

from scipy import linalg
from collections import deque
from time import time
from itertools import product

c=1
t=time()
while 1:
    m=0
    for d in product(range(2),repeat=2*c-1):
        a=deque(d)
        l=[d[0:c]]
        for x in xrange(c-1):
            a.rotate(1)
            l+=[list(a)[0:c]]
        m=max(m,linalg.det(l,overwrite_a=True,check_finite=False))
    print m,'in',time()-t,'s'
    c+=1

가정용 컴퓨터 (i7-4510U, 8GB RAM)의 샘플 출력은 다음과 같습니다.

1.0 in 0.0460000038147 s
1.0 in 0.0520000457764 s
2.0 in 0.0579998493195 s
3.0 in 0.0659999847412 s
5.0 in 0.0829999446869 s
9.0 in 0.134999990463 s
32.0 in 0.362999916077 s
56.0 in 1.28399991989 s
125.0 in 5.34999990463 s
315.0 in 27.6089999676 s
1458.0 in 117.513000011 s

고맙지 만 나는 당신이 내가 두려워하는 질문의 이전 버전에 대답했다고 생각합니다. 이제는 Toeplitz 행렬에 관한 것이며 시간 제한은 2 분입니다.

4
나는이 사이트에서 너무 많은 골프 파이썬을 보았 기 때문에 일반적인 목적으로 실제로 읽을 수있는 언어라는 것을 종종 잊어 버립니다.
Alex A.

이것은 이진 행렬이라는 사실을 이용하지 않기 때문에 크게 가속화 될 수 있습니다.
lirtosiast

@ThomasKwa 내가 정직하다면, 그것을 활용하는 방법을 모른다. : P
Kade

numpy 문서에서 인용 : "결정자는 LAPACK 루틴 z / dgetrf를 사용하여 LU 분해를 통해 계산됩니다." 나는 dgetrf를보고 배정도를 사용한다고 말한다. OP의 GPU에 따라 단 정밀도가 더 빠를 수 있습니다.
lirtosiast

4

C ++

병렬화 및 단순 최적화를 위해 OpenMP를 사용하는 Bruteforce는 전치 행렬의 행렬식을 평가하지 않습니다.

$ lscpu
...
Model name:            Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz
...
$ g++ -O2 toepl.cpp -fopenmp
$ timeout 2m ./a.out 
1 1
2 1
3 2
4 3
5 5
6 9
7 32
8 56
9 125
10 315
11 1458
12 2673
13 8118
14 22386
#include <cmath>

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

void updateReverses(vector < int > & reverses) {
  int reversesCnt = reverses.size();
  for(int i = 0; i < reversesCnt; ++i){
    reverses[i] <<= 1;
    reverses.push_back(reverses[i] | 1);
  }
}

const double eps = 1e-9;

double determinant(vector < vector < double > > & matrix) {
  int n = matrix.size();
  double det = 1;
  if(n == 1) return matrix[0][0];
  for(int i = 0; i < n; ++i){
    int p = i;
    for(int j = i + 1; j < n; ++j)
      if(fabs(matrix[j][i]) > fabs(matrix[p][i]))
        p = j;
    if(fabs(matrix[p][i]) < eps)
      return 0;
    matrix[i].swap(matrix[p]);
    if(i != p) det *= -1;
    det *= matrix[i][i];
    matrix[i][i] = 1. / matrix[i][i];
    for(int j = i + 1; j < n; ++j)
      matrix[i][j] *= matrix[i][i];
    for(int j = i + 1; j < n; ++j){
      if(fabs(matrix[j][i]) < eps) continue;
      for(int k = i + 1; k < n; ++k)
        matrix[j][k] -= matrix[i][k] * matrix[j][i];
    }
  }
  return det;
}

int main() {
  vector < int > reverses(1, 0);
  reverses.reserve(1 << 30);
  updateReverses(reverses);
  for(int n = 1;; ++n){
    double res = 0;
    int topMask = 1 << (2 * n - 1);
    vector < vector < double > > matrix(n, vector < double > (n));
#pragma omp parallel for reduction(max:res) firstprivate(matrix) schedule(dynamic,1<<10)
    for(int mask = 0; mask < topMask; ++mask){
      if(mask < reverses[mask]) continue;
      for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
          matrix[i][j] = (mask >> (i - j + n - 1)) & 1;
      res = max(res, determinant(matrix));
    }
    cout << n << ' ' << res << endl;
    updateReverses(reverses);
    updateReverses(reverses);
  }
}

누군가가 영리한 아이디어를 제시하지 않으면 곧 첫 번째 OEIS 항목을 만들 것 같습니다 :)

2

다음과 같이 컴파일하십시오.

$ clang -Ofast 52851.c -o 52851

로 실행 :

$ ./52851

n = 1..10내 컴퓨터에서 최대 결정자를 ~ 115 초 동안 출력 할 수 있습니다.

이 프로그램은 가능한 모든 크기의 이진 Toeplitz 행렬을 n결정하는 데 사용되지만 크기 5x5이하 의 행렬을 결정하는 것은 메모리를 사용하여 캐시됩니다.

처음에는 Toeplitz 행렬의 모든 하위 행렬이 Toeplitz 행렬이 될 것이라고 잘못 가정했기 때문에 각 2^(2n-1)2^(n^2)대한 대신 값 을 메모해야했습니다 n. 내 실수를 깨닫기 전에 프로그램을 만들었 으므로이 제출은 해당 프로그램의 수정 일뿐입니다.


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <string.h>

#define ELEMENTS(x) (sizeof(x) / sizeof(*x))

int *dets[6];

void print_matrix(int n, int c) {
    for(int row = 0; row < n; row++) {
        for(int col = 0; col < n; col++) {
            int j = n - 1 - row + col;
            int val = !!(c & (1 << j));
            printf("%d ", val);
        }
        puts("");
    }
}

int det(int n, uint8_t *m) {
    if(n == 1) {
        return m[0];
    }

    int i = 0;

    if(n < ELEMENTS(dets)) {
        for(int j = 0; j < n * n; j++) {
            i *= 2;
            i += m[j];
        }

        int v = dets[n][i];
        if(v != INT_MIN) {
            return v;
        }
    }

    int v = 0;

    uint8_t *sub = malloc((n - 1) * (n - 1));

    for(int removed = 0; removed < n; removed++) {
        if(m[removed]) {
            uint8_t *p = sub;
            for(int row = 1; row < n; row++) {
                for(int col = 0; col < n; col++) {
                    if(col == removed) {
                        continue;
                    }

                    *p = m[col + row * n];

                    p++;
                }
            }

            v += (removed % 2 == 0? 1: -1) * det(n - 1, sub);
        }
    }

    free(sub);

    if(n < ELEMENTS(dets)) {
        dets[n][i] = v;
    }
    return v;
}

int main(void) {
    for(int i = 2; i < ELEMENTS(dets); i++) {
        int combinations = 1 << (i * i);
        dets[i] = malloc(combinations * sizeof(**dets));
        for(int j = 0; j < combinations; j++) {
            dets[i][j] = INT_MIN;
        }
    }

    puts("1: 1");

    for(int n = 2; n < 65; n++) {
        int vars = 2 * n - 1;
        size_t combinations = 1 << vars;

        int best = -1;
        int max = -1;

        uint8_t *sub = malloc((n - 1) * (n - 1));

        for(int c = 0; c < combinations; c++) {
            int d = 0;
            for(int i = 0; i < n; i++) {
                if(c & (1 << (n - 1 + i))) {
                    uint8_t *p = sub;
                    for(int row = 1; row < n; row++) {
                        for(int col = 0; col < n; col++) {
                            if(col == i) {
                                continue;
                            }

                            int j = n - 1 - row + col;
                            *p = !!(c & (1 << j));

                            p++;
                        }
                    }
                    d += (i % 2 == 0? 1: -1) * det(n - 1, sub);
                }
            }

            if(d > max) {
                max = d;
                best = c;
            }
        }

        free(sub);

        printf("%d: %d\n", n, max);
        //print_matrix(n, best);
    }

    return 0;
}

미성년자에 의한 확장을 사용하여 결정자를 계산하는 것처럼 보입니다. 이것은 O(n!)복잡하기 때문에 다른 알고리즘을 사용하는 것이 좋습니다.
lirtosiast

@ThomasKwa 더 빠른 알고리즘이 있다는 것을 몰랐 으므로이 솔루션은 꽤 나쁩니다.
Tyilo

LU 분해 를 사용하여 행렬의 행렬식을 찾는 것이 좋습니다. 그건 O(n^3)그래도 난 빨리 흥미로운 알고리즘을 할 수있다, 믿는다. 여기에 사용 된 대부분의 내장은 일반적으로 결정 변형을 수행하기 위해 변형 변형을 사용한다고 생각합니다.
BrainSteel

@BrainSteel, 예, 살펴 보았지만 O(n^2)대답을 업데이트하는 경우 알고리즘을 사용할 수도 있습니다 .
Tyilo

일반적인 Wikipedia 검색에 따르면 Toeplitz 행렬의 행렬식은에서 확인할 수 있습니다 O(n^2). 하지만 문제의 병목이 중 검색 생각 O(4^n)많은 0-1 n으로 n행렬.
Legendre

2

아르 자형

R과 함께 나열된 패키지를 설치해야합니다. install.packages("package_name")

이 버전의 컴퓨터에서 2 분 미만이되지 않았습니다 (병렬 수정을 시도했습니다).

library(pracma)
library(stringr)
library(R.utils)
library(microbenchmark)

f <- function(n) {
  #If n is 1, return 1 to avoid code complexity on this special case
  if(n==1) { return(1) }
  # Generate matrices and get their determinants
  dets <- sapply(strsplit(intToBin( 0:(2^n - 1)), ""), function(x) {
              sapply( strsplit( intToBin( 0:(2^(n-1) - 1) ), ""), 
                    function(y) { 
                      det(Toeplitz(x,c(x[1],y))) 
                    })

              })
  #Get the maximum determinant and return it
  res <- max(abs(dets))
  return(res)
}

호출 및 출력 :

> sapply(1:10,f)
 [1]   1   1   2   3   5   9  32  56 125 315

내 컴퓨터의 벤치 마크 :

> microbenchmark(sapply(1:10,f),times=1L)
Unit: seconds
            expr      min       lq     mean   median       uq      max neval
 sapply(1:10, f) 66.35315 66.35315 66.35315 66.35315 66.35315 66.35315     1

정보를 얻으려면 1:11 범위의 경우 285 초가 걸립니다.


1

PARI / GP, n = 11

이것은 무차별적인 힘이지만 활용 det(A^T) = det(A)합니다. 전치사를 건너 뛰는 것이 얼마나 쉬운 지 보여주기 위해 게시하고 있습니다. 가장 낮은 비트는 b1왼쪽 상단 셀을 유지하고 다른 비트는 나머지 맨 위 행을 유지합니다. b2왼쪽 열의 나머지 부분을 보유합니다. 우리는 단순히 시행 b2 <= (b1>>1)합니다.

{ for(n=1,11,
    res=0;
    for(b1=0,2^n-1,
      for(b2=0,b1>>1,
        res=max(res,matdet(matrix(n,n,i,j,bittest(if(i>j,b2>>(i-j-1),b1>>(j-i)),0))));
      )
    );
    print(n" "res);
  )
}

O(n^2)시간 에 따른 Toeplitz 결정 요인 계산 관련 : 제한적인 연구에서 알고리즘이 작동하기 위해서는 모든 주요 주요 미성년자가 0이 아니어야한다는 요구 사항이 계속 발생했습니다. 이는이 작업의 주요 장애물입니다. 나보다 이것에 대해 더 많이 알고 있다면 언제든지 알려주십시오.


이 종이 보셨어요? scienpress.com/upload/JAMB/Vol%201_1_4.pdf . 복잡성이 무엇인지는 분명하지 않았습니다. n = 5 예제에는 꽤 많은 용어가있는 것 같습니다.
Reto Koradi

@RetoKoradi 예 나는 그것을 보았다. 복잡도는 다항식이 아닌 것 같습니다. 예를 들어 e_{k+1}구성 요소 수의 4 배를가집니다 e_k. 이 논문에는 많은 누락이 있습니다. 모든 주요 주항이 0이 아닌 경우, 비가역 행렬에는 LU 분해가 있습니다. (분모에 주목하십시오. 예를 들어 a_0, 암시 적으로 그것들은 0이 아닌 것으로 보장됩니다.) 고유성은 L이 단위 삼각형이라는 점에서 비롯됩니다. 저자는 또한 수치 안정성에 대해서는 언급하지 않았습니다. 링크를 사용할 수없는 경우 Hsuan-Chu Li (2011)의 논문은 "Toeplitz 행렬의 결정 요인 계산 중"입니다.
Mitch Schwartz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.