Trova il determinante massimo per ogni matrice Toeplitz di dimensioni


14

Per una n fissa, considera le matrici n per n di Toeplitz con voci che sono 0 o 1. L'obiettivo è trovare il determinante massimo su tutte queste matrici di Toeplitz.

Compito

Per ciascuno nda 1 in su, genera il determinante massimo su tutte le matrici di Toeplitz n per n con voci che sono 0 o 1. Dovrebbe esserci un output per il nquale dovrebbe avere il determinante massimo e anche una matrice di esempio che lo raggiunga.

Punto

Il tuo punteggio è il più grande che il ntuo codice raggiunge in 2 minuti sul mio computer. Per chiarire un po ', il codice può essere eseguito per 2 minuti in totale, questo non è di 2 minuti per n.

Tie breaker

Se due voci ottengono lo stesso npunteggio, quella vincente sarà quella che raggiungerà il punteggio più alto nnel minor tempo possibile sulla mia macchina. Se anche le due migliori iscrizioni sono uguali su questo criterio, il vincitore sarà la risposta inviata per prima.

Lingue e biblioteche

Puoi usare qualsiasi linguaggio e librerie liberamente disponibili che ti piacciono. Devo essere in grado di eseguire il tuo codice, quindi per favore includi una spiegazione completa su come eseguire / compilare il tuo codice in Linux, se possibile.

La mia macchina I tempi verranno eseguiti sulla mia macchina. Questa è un'installazione ubuntu standard su un processore a otto core AMD FX-8350. Questo significa anche che devo essere in grado di eseguire il tuo codice.

Piccole risposte

Per n = 1..10 le uscite dovrebbero essere 1,1,2,3,5,9,32,56,125,315

Questa sequenza non è in OEIS e quindi la voce vincente può anche proporre una nuova voce lì.

Voci finora

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

@AlexA. Hai ragione e mi sono scusato. Fortunatamente i due problemi sono molto simili, quindi dovrebbe essere in grado di modificare facilmente il suo codice.

La soluzione di @Vioz presenta una sequenza che inizia con 1, 1, 2, 3, 5, 9, 32. Quindi il valore per n = 5 è diverso da quello che hai elencato. Poiché tutti gli altri valori corrispondono, sembra che la soluzione sia probabilmente corretta, e questo è solo un errore di battitura nella domanda?
Reto Koradi,

@RetoKoradi Grazie. Fisso.

Ecco 10 possibili matrici binarie di Toeplitz con determinanti massimi per n = 1..10: ghostbin.com/paste/axkpa
Tyilo

2
Come osservazione che può aiutare gli altri ma non posso verificare oltre 14. Sembra che i rispettivi mezzi della riga superiore e la prima colonna della matrice di Toeplitz siano sempre 0,4 <= m <= 0,6 per il determinante massimo.
MickyT,

Risposte:


3

C ++ con pthreads

Questo arriva a n = 14 in poco meno di 1 minuto sulla mia macchina. Ma dal momento che è solo un laptop a 2 core, spero che la macchina di test a 8 core possa finire n = 15 in meno di 2 minuti. Ci vogliono circa 4:20 minuti sulla mia macchina.

Speravo davvero di trovare qualcosa di più efficiente. Ci ha ottenuto di essere un modo per calcolare il determinato di una matrice binaria in modo più efficiente. Volevo trovare una sorta di approccio di programmazione dinamica che conteggi i termini +1 e -1 nel calcolo determinante. Ma finora non si è ancora del tutto riunito.

Poiché la taglia sta per scadere, ho implementato l'approccio standard della forza bruta:

  • Passa sopra tutte le possibili matrici di Toeplitz.
  • Salta uno dei due in ciascuna coppia di matrici trasposta. Poiché la matrice è descritta da valori di maschera di bit, questo è semplice da fare saltando tutti i valori in cui il retro della maschera di bit è più piccolo della maschera di bit stessa.
  • Il determinato viene calcolato con una decomposizione LR del libro di testo. Tranne qualche piccola messa a punto delle prestazioni, il principale miglioramento che ho apportato all'algoritmo dal mio libro sui metodi numerici del college è che utilizzo una strategia pivot più semplice.
  • La parallelizzazione viene eseguita con pthreads. L'utilizzo della spaziatura regolare per i valori elaborati da ciascun thread ha causato un bilanciamento del carico molto scarso, quindi ho introdotto un po 'di frizzanti.

Ho provato questo su Mac OS, ma prima ho usato un codice simile su Ubuntu, quindi spero che questo si compili e funzioni senza intoppi:

  1. Salvare il codice in un file con .cppun'estensione, ad es optim.cpp.
  2. Compila con gcc -Ofast optim.cpp -lpthread -lstdc++.
  3. Corri con time ./a.out 14 8. Il primo argomento è il massimo n. 14 dovrebbe finire sicuramente in meno di 2 minuti, ma sarebbe bello se tu potessi provare anche 15. Il secondo argomento è il numero di thread. L'uso dello stesso valore del numero di core della macchina è normalmente un buon inizio, ma provare alcune variazioni potrebbe potenzialmente migliorare i tempi.

Fammi sapere se hai problemi a costruire o eseguire il codice.

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

C'è un modo interessante di calcolare il determinante di una matrice di numeri interi usando solo l'aritmetica di numeri interi: decomposizione LU in un campo finito (sostanzialmente mod un grande primo). Non so se questo sarebbe più veloce.
lirtosiast

@ThomasKwa Probabilmente sarebbe ancora O (n ^ 3)? Potrebbe essere utile per matrici più grandi in cui la precisione in virgola mobile diventerebbe altrimenti un problema. Non ho davvero cercato letteratura. Bene, ho fatto una rapida ricerca e ho trovato un documento sul calcolo dei determinanti delle matrici di Toeplitz. Ma c'erano troppe domande aperte per me per dedicare il tempo a cercare di implementarlo.
Reto Koradi,

1
@Lembik Proverò a dargli un'occhiata più tardi oggi. L'ho cambiato per gestire dimensioni più grandi per l'altra sfida correlata ieri. Finora non sono riuscito a battere i punteggi più alti per n = 30, la mia euristica è bloccata al di sotto di 5 * 10 ^ 13.
Reto Koradi,

1
@Lembik Vedi paste.ubuntu.com/11915546 per il codice e paste.ubuntu.com/11915532 per risultati fino a n = 19.
Reto Koradi,

1
@Lembik I risultati fino a n = 20 sono su paste.ubuntu.com/11949738 . Ora elencano tutte le soluzioni legate, compresi gli attributi per vedere rapidamente il valore della diagonale e se sono circolanti. Tutti i massimi per m = 18,19,20 sono matrici circolanti. Per favore ricontrolla i determinanti prima di pubblicarli ovunque.
Reto Koradi,

8

J

Aggiornamento: codice migliorato per cercare oltre la metà dei valori. Ora calcola n=12comodamente entro 120 secondi (da 217 a 60 secondi).

Avrai bisogno dell'ultima versione di J installata.

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

Esegui questo e uccidi quando sono trascorsi due minuti. I miei risultati (MBP 2014 - 16 GB di 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 di esecuzione totale = 61,83 s.


Solo per divertimento

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

Questa operazione ha richiesto circa 210 secondi da sola.


1
Nota per i tester: n = 12richiede circa 18 GiB di memoria.
Dennis,

Questo è un bel miglioramento. L'output è tuttavia leggermente errato. Per me usando j64-804 produce n = 1 due volte, quindi esce da uno per sempre di più.

@Lembik Ah, è vero. Ho appena aggiornato il codice; potresti provare a correre di nuovo? Grazie! (L'ho impostato per calcolare fino a n=13. È possibile modificare il 13nella penultima riga per fare in modo che calcoli tutto ciò che si desidera.)
Legendre

L'ho eseguito di nuovo e arriva ancora a 12.

@Lembik Hmm .. stai dicendo che arriva a 12 entro il limite di tempo e arriva a 13 dopo qualche tempo (che è quello che mi aspetto), o che non arriva mai a 13 (cioè il programma si ferma dopo 12)?
Legendre,

4

Python 2

Questa è una soluzione molto semplice e probabilmente non vincerà il concorso. Ma ehi, funziona!

Darò una rapida panoramica di ciò che sta accadendo esattamente.

  1. In primo luogo ho generato ogni possibile riga iniziale per n. Ad esempio, quando n=2, ciò genererà una lunghezza di matrice 2 n + 1 , in cui ogni riga è lunghezza 2n-1. Si sarebbe simile a questa: [[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. Quindi, per ognuna di quelle possibili righe iniziali, lo ngiro volte e taglio i primi nelementi per generare la matrice appropriata, e uso scipyper calcolare il determinante, il tutto tenendo traccia del valore massimo. Alla fine, stampo semplicemente il massimo, incremento ndi 1 e continuo fino a quando sono trascorsi 10 minuti.

Per eseguire questo, avrai bisogno di scipy installato.

Modifica 1: Modificato il modo in cui sono state costruite le righe iniziali utilizzando invece itertools.product, grazie a Sp3000!

Modifica 2: rimossa la memorizzazione di possibili righe iniziali per un miglioramento minimo della velocità.

Modifica 3: modificato per scipyavere un maggiore controllo sul detfunzionamento.

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

Ecco alcuni esempi di output sul mio computer di casa (i7-4510U, 8 GB di 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

Grazie, ma penso che tu abbia risposto a una vecchia versione della domanda, temo. Ora si tratta delle matrici di Toeplitz e il limite di tempo è di 2 minuti.

4
Vedo così tanto Python golfato su questo sito che spesso dimentico che per scopi generali è in realtà un linguaggio leggibile.
Alex A.

Questo potrebbe probabilmente essere accelerato in modo significativo, perché non sfrutta il fatto che si tratta di una matrice binaria.
Lirtosiast

@ThomasKwa Se sono onesto, non ho idea di come trarne vantaggio: P
Kade,

Citazione dalla documentazione numpy: "Il determinante viene calcolato tramite fattorizzazione LU usando la routine LAPACK z / dgetrf." Ho guardato dgetrf, e dice che usa la doppia precisione; a seconda della GPU dell'OP la precisione singola potrebbe essere più veloce.
lirtosiast,

4

C ++

Bruteforce con l'uso di OpenMP per la parallelizzazione e l'ottimizzazione semplice per evitare la valutazione del determinante per le matrici trasposte.

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

Sembra che potresti presto fare la tua prima iscrizione all'OEIS a meno che qualcuno non abbia un'idea geniale :)

2

C

Compilare con:

$ clang -Ofast 52851.c -o 52851

Corri con:

$ ./52851

Può produrre il determinante massimo per n = 1..10 ~ 115 secondi sul mio computer.

Il programma sta ottenendo al determinante ogni possibile matrice binaria di dimensione di Toeplitz n, tuttavia ogni determinante di matrici di dimensione5x5 o inferiori verrà memorizzato nella cache utilizzando la memorizzazione.

All'inizio ho erroneamente supposto che ogni sottotrix di una matrice di Toeplitz fosse anche una matrice di Toeplitz, quindi dovevo solo 2^(2n-1)memorizzare i valori anziché 2^(n^2)per ciascuno n. Ho fatto il programma prima di rendermi conto del mio errore, quindi questa presentazione è solo una correzione di quel programma.


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

Sembra che tu stia calcolando il determinante usando l'espansione dei minori; questo ha O(n!)complessità, quindi potresti stare meglio usando un algoritmo diverso.
lirtosiast,

@ThomasKwa Non sapevo che esistessero algoritmi più veloci, quindi sì, questa soluzione è piuttosto male.
Tyilo,

Potresti voler esaminare l'utilizzo della decomposizione LU per trovare il determinante di una matrice. È O(n^3), credo, anche se può essere reso più veloce con alcuni algoritmi interessanti. Credo che la maggior parte dei builtin qui usati generalmente utilizzino una variante di decomposizione per eseguire determinanti.
BrainSteel,

@BrainSteel, sì, l'ho guardato, ma potrei anche scegliere un O(n^2)algoritmo se sto aggiornando la mia risposta.
Tyilo,

Secondo una ricerca casuale di Wikipedia, è possibile determinare il determinante di una matrice di Toeplitz O(n^2). Ma penso che il collo di bottiglia del problema sia la ricerca tra i O(4^n)molti 0-1 nper nmatrici.
Legendre,

2

R

Dovrai installare R e i pacchetti elencati con install.packages("package_name")

Non ho ottenuto meno di 2 minuti sul mio computer con questa versione (devo provare con una modifica parallela)

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

Chiamata e uscita:

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

Benchmark sulla mia macchina:

> 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

Per informazioni, per un intervallo 1:11, sono necessari 285 secondi.


1

PARI / GP, n = 11

Questa è forza bruta ma ne approfitta det(A^T) = det(A). Lo sto solo postando per dimostrare quanto sia facile saltare le trasposizioni. Il bit più basso b1contiene la cella in alto a sinistra e gli altri bit contengono il resto della riga in alto. b2contiene il resto della colonna di sinistra. Facciamo semplicemente rispettare 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);
  )
}

Per quanto riguarda il calcolo dei determinanti di Toeplitz nel O(n^2)tempo: nella mia ricerca limitata, ho continuato a incontrare un requisito secondo cui tutti i principali minori minori devono essere diversi da zero per far funzionare gli algoritmi, il che rappresenta un grosso ostacolo per questo compito. Sentiti libero di darmi suggerimenti se ne sai di più di me.


Hai visto questo documento? scienpress.com/upload/JAMB/Vol%201_1_4.pdf . Non mi era chiaro quale fosse la complessità. Sembravano esserci parecchi termini per l'esempio n = 5.
Reto Koradi,

@RetoKoradi Sì, l'ho visto. Sembra che la complessità non sia polinomiale, considerando che ad esempio e_{k+1}ha 4 volte il numero di componenti come e_k. Ci sono molte omissioni nel documento. Una matrice invertibile ha una decomposizione LU se tutti i principali minori minori sono diversi da zero. (Si noti che i denominatori, ad esempio a_0- implicitamente sono garantiti come diversi da zero.) L'unicità deriva dal fatto che L è un'unità triangolare. Inoltre, l'autore non ha menzionato la stabilità numerica. Nel caso in cui il collegamento non fosse disponibile, l'articolo è "Sul calcolo delle determinanti delle matrici di Toeplitz" di Hsuan-Chu Li (2011).
Mitch Schwartz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.