Finden Sie die maximale Determinante für jede Größe der Toeplitz-Matrix

14

Betrachten Sie für ein festes n die n mal n Toeplitz-Matrizen mit Einträgen, die entweder 0 oder 1 sind. Ziel ist es, die maximale Determinante über alle diese Toeplitz-Matrizen zu finden.

Aufgabe

Geben Sie für jede nvon 1 aufwärts die maximale Determinante über alle n mal n Toeplitz-Matrizen mit Einträgen von entweder 0 oder 1 aus. Es sollte eine Ausgabe geben n, für die die maximale Determinante und eine Beispielmatrix vorhanden sein sollte, die sie erreicht.

Ergebnis

Ihre Punktzahl ist die höchste, die nIhr Code in 2 Minuten auf meinem Computer erreicht. Um ein wenig zu verdeutlichen, Ihr Code kann insgesamt 2 Minuten lang ausgeführt werden, das sind nicht 2 Minuten pro n.

Kabelbinder

Wenn zwei Einträge die gleiche nPunktzahl erzielen, ist der Gewinner derjenige, der nin kürzester Zeit auf meinem Computer den höchsten Wert erreicht . Stimmen auch bei diesem Kriterium die beiden besten Einsendungen überein, ist der Gewinner die zuerst eingereichte Antwort.

Sprachen und Bibliotheken

Sie können jede frei verfügbare Sprache und Bibliothek verwenden, die Sie mögen. Ich muss in der Lage sein, Ihren Code auszuführen. Fügen Sie daher bitte eine vollständige Erklärung dazu bei, wie Sie Ihren Code unter Linux ausführen / kompilieren, wenn dies möglich ist.

Mein Computer Die Timings werden auf meinem Computer ausgeführt. Dies ist eine Ubuntu-Standardinstallation auf einem AMD FX-8350 Eight-Core-Prozessor. Dies bedeutet auch, dass ich in der Lage sein muss, Ihren Code auszuführen.

Kleine Antworten

Für n = 1..10 sollten die Ausgänge 1,1,2,3,5,9,32,56,125,315 sein

Diese Sequenz ist nicht in OEIS und daher kann der Gewinner auch dort einen neuen Eintrag vorschlagen.

Einträge bisher

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

quelle
@AlexA. Du hast recht und ich habe mich entschuldigt. Glücklicherweise sind die beiden Probleme sehr ähnlich, so dass er seinen Code leicht ändern kann.
Die Lösung von @Vioz liefert eine Sequenz, die mit 1, 1, 2, 3, 5, 9, 32 beginnt. Daher unterscheidet sich der Wert für n = 5 von Ihrer Liste. Da alle anderen Werte übereinstimmen, scheint die Lösung wahrscheinlich korrekt zu sein, und dies ist nur ein Tippfehler in der Frage?
Reto Koradi
@RetoKoradi Vielen Dank. Fest.
Hier sind 10 mögliche binäre Toeplitz-Matrizen mit maximalen Determinanten für n = 1..10: ghostbin.com/paste/axkpa
Tyilo
2
Als eine Beobachtung, die anderen helfen kann, die ich aber nicht über 14 hinaus überprüfen kann. Es scheint, dass die jeweiligen Mittelwerte der oberen Reihe und der ersten Spalte der Toeplitz-Matrix für die maximale Determinante immer 0,4 <= m <= 0,6 sind.
MickyT

Antworten:

3

C ++ mit pthreads

Dies wird auf meinem Computer in knapp 1 Minute zu n = 14. Da es sich aber nur um einen 2-Core-Laptop handelt, hoffe ich, dass der 8-Core-Testcomputer n = 15 in weniger als 2 Minuten fertigstellen kann. Auf meinem Computer dauert es ungefähr 4:20 Minuten.

Ich hatte wirklich gehofft, etwas effizienteres zu finden. Es hat bekam einen Weg , um die bestimmten einer binären Matrix effizienter zu berechnen. Ich wollte eine Art dynamischen Programmieransatz entwickeln, bei dem die Terme +1 und -1 in der Determinantenberechnung berücksichtigt werden. Aber es ist einfach noch nicht so weit zusammengekommen.

Da das Kopfgeld bald abläuft, habe ich den Standard-Brute-Force-Ansatz implementiert:

  • Durchlaufen Sie alle möglichen Toeplitz-Matrizen.
  • Überspringen Sie eines der beiden in jedem transponierten Matrixpaar. Da die Matrix durch Bitmaskenwerte beschrieben wird, ist dies einfach, indem alle Werte übersprungen werden, bei denen die Umkehrung der Bitmaske kleiner als die Bitmaske selbst ist.
  • Die Bestimmung wird mit einer Lehrbuch-LR-Zerlegung berechnet. Abgesehen von einigen geringfügigen Leistungsverbesserungen habe ich den Algorithmus aus meinem Buch über numerische Methoden am College hauptsächlich dadurch verbessert, dass ich eine einfachere Pivot-Strategie verwende.
  • Die Parallelisierung erfolgt mit pthreads. Die Verwendung von regulären Abständen für die von jedem Thread verarbeiteten Werte führte zu einem sehr schlechten Lastenausgleich, weshalb ich ein gewisses Swizzling einführte.

Ich habe dies unter Mac OS getestet, habe aber zuvor unter Ubuntu ähnlichen Code verwendet. Ich hoffe, dass dies ohne Probleme kompiliert und ausgeführt werden kann:

  1. Speichern Sie den Code in einer Datei mit einer .cppErweiterung, z optim.cpp.
  2. Kompilieren mit gcc -Ofast optim.cpp -lpthread -lstdc++.
  3. Laufen Sie mit time ./a.out 14 8. Das erste Argument ist das Maximum n. 14 sollte sicher in weniger als 2 Minuten fertig sein, aber es wäre großartig, wenn Sie auch 15 versuchen könnten. Das zweite Argument ist die Anzahl der Threads. Die Verwendung des gleichen Werts wie die Anzahl der Kerne der Maschine ist normalerweise ein guter Anfang, aber das Ausprobieren einiger Variationen könnte möglicherweise die Zeiten verbessern.

Lassen Sie mich wissen, wenn Sie Probleme beim Erstellen oder Ausführen des Codes haben.

#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;
}
Reto Koradi
quelle
Es gibt eine interessante Möglichkeit, die Determinante einer Ganzzahlmatrix nur mit Ganzzahlarithmetik zu berechnen: LU-Zerlegung in einem endlichen Feld (im Grunde genommen modifiziert man eine große Primzahl). Ich weiß nicht, ob das schneller gehen würde.
Lirtosiast
@ThomasKwa Das wäre wohl noch O (n ^ 3)? Dies kann bei größeren Matrizen hilfreich sein, bei denen die Gleitkommapräzision ansonsten ein Problem darstellt. Ich habe nicht wirklich nach Literatur gesucht. Nun, ich machte eine schnelle Suche und fand eine Arbeit über die Berechnung der Determinanten von Toeplitz-Matrizen. Es gab jedoch zu viele offene Fragen, als dass ich mir die Zeit nehmen könnte, sie umzusetzen.
Reto Koradi
1
@Lembik Ich versuche es später noch einmal. Ich habe es gestern geändert, um größere Formate für Ihre andere verwandte Herausforderung zu handhaben. Die höchsten Punktzahlen für n = 30 konnten bisher nicht erreicht werden, meine Heuristiken stecken unter 5 * 10 ^ 13.
Reto Koradi
1
@Lembik Siehe paste.ubuntu.com/11915546 für Code und paste.ubuntu.com/11915532 für Ergebnisse bis n = 19.
Reto Koradi
1
@Lembik Die Ergebnisse bis n = 20 finden Sie unter paste.ubuntu.com/11949738 . Sie listen jetzt alle verknüpften Lösungen auf, einschließlich der Attribute, um den Wert der Diagonale schnell zu erkennen und festzustellen, ob sie zirkulierend sind. Alle Maxima für m = 18,19,20 sind Zirkulationsmatrizen. Bitte überprüfen Sie die Determinanten, bevor Sie sie irgendwo veröffentlichen.
Reto Koradi
8

J

Update: Verbesserter Code für die Suche nach mehr als der Hälfte der Werte. Berechnet jetzt n=12bequem innerhalb von 120 Sekunden (von 217 auf 60 Sekunden).

Sie müssen die neueste Version von J installiert haben.

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

Führen Sie dies aus und töten Sie, wenn zwei Minuten vergangen sind. Meine Ergebnisse (MBP 2014 - 16 GB 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│
└────┴──┴─────────────┴────┘

Gesamtlaufzeit = 61,83 s.


Nur zum Spaß

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

Dies dauerte allein ungefähr 210 Sekunden.

Legendre
quelle
1
Hinweis für Tester: n = 12Benötigt ca. 18 GB Speicher.
Dennis
Das ist eine sehr schöne Verbesserung. Die Ausgabe ist jedoch leicht fehlerhaft. Für mich mit j64-804 gibt es zweimal n = 1 aus, so dass es für immer mehr um eins ausfällt.
@ Lembik Ah das ist richtig. Ich habe gerade den Code aktualisiert. Könntest du es nochmal versuchen? Vielen Dank! (Ich habe es eingestellt, um bis zu berechnen n=13. Sie können das 13in der vorletzten Zeile ändern , um es berechnen zu lassen, was auch immer Sie wünschen.)
Legendre
Ich habe es erneut ausgeführt und es wird immer noch bis 12.
@Lembik Hmm .. sagst du, dass es innerhalb des Zeitlimits auf 12 und einige Zeit danach auf 13 kommt (was ich erwarte) oder dass es nie auf 13 kommt (dh das Programm stoppt nach 12)?
Legendre
4

Python 2

Dies ist eine sehr einfache Lösung und wird den Wettbewerb wahrscheinlich nicht gewinnen. Aber hey, es funktioniert!

Ich gebe einen kurzen Überblick darüber, was genau passiert.

  1. Ich erstelle jede mögliche Startreihe für n . In n=2diesem Fall wird beispielsweise eine Arraylänge von 2 n + 1 generiert , wobei jede Zeile die Länge 2 n -1 hat. Es würde so aussehen: [[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. Dann drehe ich sie für jede dieser möglichen Startreihen n mal und schneide die ersten nElemente ab, um die entsprechende Matrix zu generieren, und scipyberechne die Determinante, wobei den Maximalwert verfolge. Am Ende drucke ich einfach das Maximum aus, inkrementiere es num 1 und fahre fort, bis 10 Minuten vergangen sind.

Um dies auszuführen, muss scipy installiert sein.

Edit 1: Die Erstellung der ersten Zeilen wurde geändert, indem stattdessen itertools.product verwendet wurde, danke Sp3000!

Bearbeiten 2: Die Speicherung möglicher Startreihen wurde entfernt, um die Geschwindigkeit minimal zu verbessern.

Edit 3: Geändert zu scipy um mehr Kontrolle über die Funktionsweise zu haben 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

Hier einige Beispiele für die Ausgabe auf meinem Heimcomputer (i7-4510U, 8 GB 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
Kade
quelle
Vielen Dank, aber ich glaube, Sie haben eine alte Version der Frage beantwortet, fürchte ich. Es geht jetzt um Toeplitz-Matrizen und die Frist beträgt 2 Minuten.
4
Ich sehe so viel Golf-Python auf dieser Seite, dass ich oft vergesse, dass es für allgemeine Zwecke tatsächlich eine lesbare Sprache ist.
Alex A.
Dies könnte wahrscheinlich erheblich beschleunigt werden, da die Tatsache, dass es sich um eine binäre Matrix handelt, nicht ausgenutzt wird.
Lirtosiast
@ThomasKwa Wenn ich ehrlich bin, habe ich keine Ahnung, wie ich das ausnutzen kann: P
Kade
Zitat aus der Numpy-Dokumentation: "Die Determinante wird über die LU-Faktorisierung mit der LAPACK-Routine z / dgetrf berechnet." Ich habe dgetrf angesehen und es heißt, es wird doppelte Präzision verwendet. Abhängig von der GPU des OP kann die Genauigkeit einer einzelnen Karte schneller sein.
Lirtosiast
4

C ++

Bruteforce mit Verwendung von OpenMP zur Parallelisierung und einfachen Optimierung, um die Bewertung der Determinante für transponierte Matrizen zu vermeiden.

$ 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);
  }
}
SteelRaven
quelle
Es sieht so aus, als würden Sie bald Ihren ersten OEIS-Eintrag
2

C

Kompilieren mit:

$ clang -Ofast 52851.c -o 52851

Laufen mit:

$ ./52851

Kann die maximale Determinante n = 1..10in ~ 115 Sekunden auf meinem Computer ausgeben .

Das Programm ermittelt gerade die Determinante jeder möglichen binären Toeplitz-Matrix der Größe n, jedoch wird jede Determinante von Matrizen der Größe 5x5oder kleiner unter Verwendung von Memoization zwischengespeichert.

Anfangs habe ich fälschlicherweise angenommen, dass jede Submatrix einer Toeplitz-Matrix auch eine Toeplitz-Matrix sein wird, also musste ich mir nur 2^(2n-1)Werte merken anstatt 2^(n^2)für jede n. Ich habe das Programm erstellt, bevor ich meinen Fehler bemerkte, daher ist diese Übermittlung nur eine Korrektur des Programms.


#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;
}
Tyilo
quelle
Es sieht so aus, als würden Sie die Determinante mithilfe der Erweiterung um Minderjährige berechnen. Dies ist O(n!)komplex, sodass Sie möglicherweise besser mit einem anderen Algorithmus umgehen können.
Lirtosiast
@ThomasKwa Ich wusste nicht, dass es schnellere Algorithmen gibt, also ist diese Lösung ziemlich schlecht.
Tyilo
Möglicherweise möchten Sie die Verwendung der LU-Zerlegung untersuchen , um die Determinante einer Matrix zu finden. Es ist O(n^3), glaube ich, kann jedoch schneller mit einigen interessanten Algorithmen erfolgen. Ich glaube, dass die meisten der hier verwendeten Builtins im Allgemeinen eine Variante der Zerlegung verwenden, um Determinanten durchzuführen.
BrainSteel
@BrainSteel, ja, ich habe es mir angesehen, aber ich könnte genauso gut einen O(n^2)Algorithmus wählen, wenn ich meine Antwort aktualisiere.
Tyilo
Nach einer zufälligen Wikipedia-Suche kann die Determinante einer Toeplitz-Matrix in bestimmt werden O(n^2). Aber ich denke, der Engpass des Problems ist die Suche unter den O(4^n)vielen 0-1 nnach nMatrizen.
Legendre
2

R

Sie müssen R und die mit aufgelisteten Pakete installieren install.packages("package_name")

Ich habe mit dieser Version weniger als 2 Minuten Zeit auf meinem Computer (ich muss es mit einer parallelen Modifikation versuchen)

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

Aufruf und Ausgabe:

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

Benchmark auf meiner Maschine:

> 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

Zur Information: Für einen Bereich von 1:11 werden 285 Sekunden benötigt.

Tensibai
quelle
1

PARI / GP, n = 11

Dies ist brachiale Gewalt, aber unter Ausnutzung det(A^T) = det(A). Ich poste es nur, um zu zeigen, wie einfach es ist, Transponierungen zu überspringen. Das niedrigste Bit von b1enthält die obere linke Zelle, und die anderen Bits enthalten den Rest der oberen Reihe. b2hält den Rest der linken Spalte. Wir setzen einfach durch 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);
  )
}

In Bezug auf die Berechnung von Toeplitz-Determinanten in der O(n^2)Zeit: In meiner begrenzten Forschung bin ich immer wieder auf die Anforderung gestoßen, dass alle führenden Hauptminderjährigen ungleich Null sein müssen, damit die Algorithmen funktionieren, was ein Haupthindernis für diese Aufgabe darstellt. Zögern Sie nicht, mir Hinweise zu geben, wenn Sie mehr darüber wissen als ich.

Mitch Schwartz
quelle
Hast du dieses Papier gesehen? scienpress.com/upload/JAMB/Vol%201_1_4.pdf . Mir war nicht klar, was die Komplexität ist. Für das Beispiel n = 5 schien es ziemlich viele Begriffe zu geben.
Reto Koradi
@RetoKoradi Ja, das habe ich gesehen. Es scheint, dass die Komplexität kein Polynom ist, wenn man bedenkt, dass zB e_{k+1}die vierfache Anzahl von Komponenten vorliegt e_k. Es gibt viele Auslassungen in der Zeitung. Eine invertierbare Matrix hat eine LU-Zerlegung, wenn alle führenden Hauptminderjährigen ungleich Null sind. (Beachten Sie die Nenner, z. B. a_0- implizit wird garantiert, dass sie ungleich Null sind.) Die Eindeutigkeit ergibt sich aus L als Einheitsdreieck. Der Autor erwähnte auch nicht die numerische Stabilität. Für den Fall, dass der Link nicht mehr verfügbar ist, wird auf "Zur Berechnung der Determinanten von Toeplitz-Matrizen" von Hsuan-Chu Li (2011) verwiesen.
Mitch Schwartz