Encontre o determinante máximo para cada tamanho da matriz Toeplitz


14

Para um n fixo, considere n por n matrizes Toeplitz com entradas 0 ou 1. O objetivo é encontrar o determinante máximo sobre todas essas matrizes Toeplitz.

Tarefa

Para cada um nde 1 em diante, imprima o determinante máximo sobre todas as n por n matrizes Toeplitz com entradas que são 0 ou 1. Deve haver uma saída por nqual deve ter o determinante máximo e também uma matriz de exemplo que a atinja.

Ponto

Sua pontuação é a maior que nseu código atinge em 2 minutos no meu computador. Para esclarecer um pouco, seu código pode ser executado por 2 minutos no total, isso não é de 2 minutos por n.

Desempate

Se duas entradas obtiverem a mesma npontuação, a entrada vencedora será a que atingir a maior pontuação nno menor tempo na minha máquina. Se as duas melhores entradas também forem iguais nesse critério, o vencedor será a resposta enviada primeiro.

Línguas e bibliotecas

Você pode usar qualquer idioma e bibliotecas disponíveis gratuitamente que desejar. Devo ser capaz de executar seu código, portanto, inclua uma explicação completa de como executar / compilar seu código no linux, se possível.

Minha máquina Os horários serão executados na minha máquina. Esta é uma instalação padrão do ubuntu em um processador AMD FX-8350 de oito núcleos. Isso também significa que eu preciso poder executar seu código.

Pequenas respostas

Para n = 1..10, as saídas devem ser 1,1,2,3,5,9,32,56,125,315

Essa sequência não está no OEIS e, portanto, a entrada vencedora também propõe uma nova entrada lá.

Entradas até agora

  • n=10 n=11por Vioz em Python
  • n=9por Tyilo em C
  • n=12por Legendre em J
  • n=10por Tensibai em R
  • n=14por SteelRaven em C ++
  • n=14por RetoKoradi em C ++

@AlexA. Você está certo e eu me desculpei. Felizmente, os dois problemas são muito semelhantes, então ele deve poder modificar facilmente seu código.

A solução de @Vioz vem com uma sequência que começa com 1, 1, 2, 3, 5, 9, 32. Portanto, o valor para n = 5 é diferente do que você lista. Como todos os outros valores correspondem, parece que a solução provavelmente está correta e isso é apenas um erro de digitação na pergunta?
Reto Koradi

@RetoKoradi Obrigado. Fixo.

Aqui estão 10 possíveis matrizes Toeplitz binários com determinantes máximos para n = 1..10: ghostbin.com/paste/axkpa
Tyilo

2
Como uma observação que pode ajudar outras pessoas, mas não posso verificar além de 14. Parece que os respectivos meios da linha superior e a primeira coluna da matriz de Toeplitz são sempre 0,4 <= m <= 0,6 para o determinante máximo.
MickyT

Respostas:


3

C ++ com pthreads

Isso chega a n = 14 em menos de 1 minuto na minha máquina. Mas como esse é apenas um laptop de dois núcleos, espero que a máquina de teste de oito núcleos possa terminar n = 15 em menos de 2 minutos. Demora cerca de 4:20 minutos na minha máquina.

Eu realmente esperava encontrar algo mais eficiente. Tem tem que haver uma maneira de calcular o determinate de uma matriz binária de forma mais eficiente. Eu queria criar algum tipo de abordagem de programação dinâmica que conte os termos +1 e -1 no cálculo determinante. Mas isso ainda não está bem definido até agora.

Como a recompensa está prestes a expirar, implementei a abordagem da força bruta padrão:

  • Faça um loop sobre todas as matrizes possíveis de Toeplitz.
  • Pule um dos dois em cada par de matrizes transpostas. Como a matriz é descrita pelos valores da máscara de bit, isso é simples, ignorando todos os valores em que o reverso da máscara de bit é menor que o próprio máscara de bit.
  • O determinado é calculado com uma decomposição do livro-texto LR. Exceto por alguns ajustes menores no desempenho, a principal melhoria que fiz no algoritmo do livro de métodos numéricos da faculdade é o uso de uma estratégia de pivô mais simples.
  • A paralelização é feita com pthreads. O simples uso do espaçamento regular para os valores processados ​​por cada encadeamento causou um balanceamento de carga muito ruim, por isso introduzi alguns swizzling.

Eu testei isso no Mac OS, mas usei código semelhante no Ubuntu antes, então espero que isso compile e execute sem problemas:

  1. Salve o código em um arquivo com uma .cppextensão, por exemplo optim.cpp.
  2. Compile com gcc -Ofast optim.cpp -lpthread -lstdc++.
  3. Corra com time ./a.out 14 8. O primeiro argumento é o máximo n. 14 deve terminar em menos de 2 minutos, com certeza, mas seria ótimo se você pudesse tentar 15 também. O segundo argumento é o número de threads. Usar o mesmo valor que o número de núcleos da máquina normalmente é um bom começo, mas tentar algumas variações pode potencialmente melhorar os tempos.

Entre em contato se tiver algum problema ao criar ou executar o código.

#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;
}

Existe uma maneira interessante de calcular o determinante de uma matriz inteira usando apenas aritmética inteira: decomposição da LU em algum campo finito (modifique basicamente um primo grande). Não sei se isso seria mais rápido.
lirtosiast

@ThomasKwa Isso provavelmente ainda seria O (n ^ 3)? Pode ser útil para matrizes maiores, nas quais a precisão do ponto flutuante se tornaria um problema. Eu realmente não procurei literatura. Bem, fiz uma pesquisa rápida e encontrei um artigo sobre o cálculo de determinantes das matrizes de Toeplitz. Mas havia muitas perguntas em aberto para eu dedicar tempo para tentar implementá-lo.
Reto Koradi 19/07/2015

1
@Embembik Vou tentar dar uma olhada hoje mais tarde. Eu mudei para lidar com tamanhos maiores para seu outro desafio relacionado ontem. Não foi possível superar as pontuações mais altas de n = 30 até agora, minhas heurísticas estão paradas abaixo de 5 * 10 ^ 13.
Reto Koradi

1
@Lembik Consulte paste.ubuntu.com/11915546 para obter o código e paste.ubuntu.com/11915532 para obter resultados até n = 19.
Reto Koradi

1
@Lembik Os resultados até n = 20 estão em paste.ubuntu.com/11949738 . Agora eles listam todas as soluções vinculadas, incluindo atributos para ver rapidamente o valor da diagonal e se elas estão circulantes. Todos os máximos para m = 18,19,20 são matrizes circulantes. Verifique os determinantes antes de publicá-los em qualquer lugar.
Reto Koradi 27/07/2015

8

J

Atualização: código aprimorado para pesquisar mais da metade dos valores. Agora calcula n=12confortavelmente em 120 segundos (de 217 para 60).

Você precisará da versão mais recente do J instalada.

#!/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''

Execute isso e mate quando dois minutos terminarem. Meus resultados (MBP 2014 - 16GB de 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│
└────┴──┴─────────────┴────┘

Tempo total de execução = 61,83s.


Apenas por diversão

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

Isso levou aproximadamente 210 segundos por conta própria.


1
Nota para os testadores: n = 12requer aproximadamente 18 GiB de memória.
1111 Dennis

Esta é uma melhoria muito agradável. A saída é ligeiramente incorreta. Para mim, usando o j64-804, ele gera n = 1 duas vezes, e é eliminado por um para sempre.

@Lembik Ah, isso mesmo. Acabei de atualizar o código; você poderia tentar correr de novo? Obrigado! (Eu configurá-lo para calcular até n=13É possível alterar o. 13Na segunda-to-última linha para tê-lo calcular-se o que quiser.)
Legendre

Corri-lo novamente e ele ainda fica a 12.

@Lembik Hmm .. você está dizendo que chega a 12 dentro do prazo e chega a 13 algum tempo depois disso (que é o que eu espero), ou que nunca chega a 13 (ou seja, o programa pára após 12)?
Legendre

4

Python 2

Esta é uma solução muito simples e provavelmente não vencerá o concurso. Mas ei, isso funciona!

Vou dar uma rápida visão geral do que exatamente está acontecendo.

  1. Eu primeiro giro todas as linhas iniciais possíveis para n. Por exemplo, quando n=2, isso gerará um comprimento de matriz 2 n + 1 , em que cada linha terá o comprimento 2n-1. Ele ficaria assim: [[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. Em seguida, para cada uma dessas possíveis linhas iniciais, eu giro o ntempo e corto os primeiros nitens para gerar a matriz apropriada e uso scipypara calcular o determinante, mantendo o controle do valor máximo. No final, simplesmente imprimo o máximo, incremento nde 1 e continuo até 10 minutos.

Para executar isso, você precisará do scipy instalado.

Editar 1: Alterou como as linhas iniciais foram criadas usando itertools.product, graças ao Sp3000!

Edição 2: Armazenamento removido de possíveis linhas iniciais para uma melhoria mínima na velocidade.

Editar 3: mudou para scipyter mais controle sobre como detfuncionou.

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

Aqui estão alguns exemplos de saída na minha máquina doméstica (i7-4510U, 8 GB de 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

Obrigado, mas acho que você respondeu a uma versão antiga da pergunta que receio. Agora é sobre matrizes de Toeplitz e o prazo é de 2 minutos.

4
Eu vejo tanto Python jogado neste site que muitas vezes esqueço que, para fins gerais, é realmente uma linguagem legível.
9789 Alex A.

Provavelmente, isso pode ser acelerado significativamente, porque não tira vantagem do fato de ser uma matriz binária.
lirtosiast

@ThomasKwa Se eu sou honesto, eu não tenho nenhuma idéia de como tirar proveito disso: P
Kade

Cite a documentação numpy: "O determinante é calculado via fatoração LU usando a rotina LAPACK z / dgetrf." Eu olhei para o dgetrf e ele diz que usa precisão dupla; dependendo da precisão única da GPU do OP, pode ser mais rápido.
lirtosiast

4

C ++

Bruteforce com o uso do OpenMP para paralelização e otimização simples para evitar a avaliação de determinantes para matrizes transpostas.

$ 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);
  }
}

Parece que você pode em breve estar fazendo a sua primeira entrada OEIS a menos que alguém surge com uma ideia inteligente :)

2

C

Ajuntar com:

$ clang -Ofast 52851.c -o 52851

Correr com:

$ ./52851

Pode emitir o determinante máximo por n = 1..10~ 115 segundos no meu computador.

O programa está apenas obtendo o determinante de todas as matrizes binárias possíveis de tamanho de Toeplitz n, porém todos os determinantes de matrizes de tamanho 5x5ou menores serão armazenados em cache usando a memorização.

No começo, assumi erroneamente que todas as submatrizes de uma matriz de Toeplitz também serão uma matriz de Toeplitz; portanto, eu só precisava memorizar 2^(2n-1)valores em vez de 2^(n^2)para cada uma n. Eu criei o programa antes de perceber meu erro, então esse envio é apenas uma correção desse programa.


#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;
}

Parece que você está calculando o determinante usando a expansão por menores; isso tem O(n!)complexidade, então é melhor usar um algoritmo diferente.
lirtosiast

@ThomasKwa Eu não sabia que havia algoritmos mais rápidos, então sim, essa solução é muito ruim.
Tyilo

Você pode querer usar a Decomposição da LU para encontrar o determinante de uma matriz. É O(n^3), creio eu, que pode ser feito mais rápido com alguns algoritmos interessantes. Acredito que a maioria dos componentes internos usados ​​aqui geralmente usa uma variante de decomposição para executar determinantes.
BrainSteel

@BrainSteel, sim, olhei para ele, mas é melhor usar um O(n^2)algoritmo se estiver atualizando minha resposta.
Tyilo 11/07

De acordo com uma pesquisa casual da Wikipedia, o determinante de uma matriz de Toeplitz pode ser determinado em O(n^2). Mas acho que o gargalo do problema está pesquisando entre os O(4^n)muitos 0-1 npor nmatrizes.
Legendre

2

R

Você precisará instalar o R ​​e os pacotes listados em install.packages("package_name")

Não recebi menos de 2 minutos na minha máquina com esta versão (tenho que tentar com uma modificação paralela)

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)
}

Chamada e saída:

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

Referência na minha máquina:

> 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

Para obter informações, para um intervalo de 1:11, são necessários 285 segundos.


1

PARI / GP, n = 11

Isso é força bruta, mas aproveitando det(A^T) = det(A). Só estou postando para demonstrar como é fácil pular transposições. O bit mais baixo b1contém a célula superior esquerda e os outros bits mantêm o restante da linha superior. b2mantém o restante da coluna esquerda. Nós simplesmente aplicamos 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);
  )
}

Com relação à computação dos determinantes de Toeplitz no O(n^2)tempo: Na minha pesquisa limitada, continuei exigindo que todos os principais menores principais não fossem zero para que os algoritmos funcionassem, o que é um grande obstáculo para essa tarefa. Sinta-se livre para me dar sugestões, se você souber mais sobre isso do que eu.


Você viu este artigo? scienpress.com/upload/JAMB/Vol%201_1_4.pdf . Não estava claro para mim qual é a complexidade. Parecia haver muitos termos para o exemplo n = 5.
Reto Koradi 19/07/2015

@RetoKoradi Sim, eu vi isso. Parece que a complexidade não é polinomial, considerando que, por exemplo, e_{k+1}possui 4 vezes o número de componentes como e_k. Existem muitas omissões no artigo. Uma matriz invertível possui uma decomposição da LU se todos os principais menores principais não forem zero. (Observe os denominadores, por exemplo a_0- implicitamente, eles são garantidos como diferentes de zero). A singularidade vem de L ser unidade triangular. O autor também não mencionou estabilidade numérica. Caso o link fique indisponível, o artigo é "Sobre o cálculo dos determinantes das matrizes de Toeplitz", de Hsuan-Chu Li (2011).
21715 Mitch
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.