Знайдіть максимальний визначник для кожної величини матриці Toeplitz


14

Для фіксованого n розглянемо n на n матриць Toeplitz із записами, які дорівнюють 0 або 1. Метою є знайти максимальний визначник для всіх таких матриць Toeplitz.

Завдання

Для кожної nвід 1 вгору виведіть максимальний визначник для всіх n по n матриць Toeplitz із записами, що дорівнюють 0 або 1. Повинно бути один вихід, на nякий повинен бути максимальний визначник, а також матриця прикладу, яка його досягає.

Оцінка

Ваш рахунок найбільший, за який nви дістаєте код за 2 хвилини на моєму комп’ютері. Щоб трохи уточнити, ваш код може працювати протягом 2 хвилин, це не 2 хвилини на кожну n.

Автоматичний вимикач

Якщо дві записи отримають однаковий nбал, то виграшним буде той, який потрапить до найвищого nза найкоротший час на моїй машині. Якщо два найкращі результати за цим критерієм рівні, то переможцем буде відповідь, подана першим.

Мови та бібліотеки

Ви можете використовувати будь-яку вільно доступну мову та бібліотеки, які вам подобаються. Я повинен бути в змозі запустити ваш код, тому, будь-ласка, включіть повне пояснення, як запустити / скомпілювати ваш код у Linux

My Machine Часи синхронізуються на моїй машині. Це стандартна установка ubuntu на восьмиядерний процесор AMD FX-8350. Це також означає, що мені потрібно мати можливість запускати ваш код.

Невеликі відповіді

Для n = 1..10 виходи повинні становити 1,1,2,3,5,9,32,56,125,315

Ця послідовність відсутня в OEIS, і тому переможний запис також може запропонувати новий запис там.

Записи поки що

  • n=10 n=11Віоз у Пітоні
  • n=9Тиїло в С
  • n=12by Legendre in J
  • n=10від Tensibai в R
  • n=14від SteelRaven в C ++
  • n=14автор RetoKoradi в C ++

@AlexA. Ви маєте рацію, і я вибачився. На щастя, дві проблеми дуже схожі, тому він може легко змінити свій код.

Рішення від @Vioz створює послідовність, яка починається з 1, 1, 2, 3, 5, 9, 32. Отже, значення n = 5 відрізняється від того, що ви перераховуєте. Оскільки всі інші значення відповідають, схоже, рішення, ймовірно, правильне, і це лише помилка друку у питанні?
Рето Кораді

@RetoKoradi Дякую Виправлено.

Ось 10 можливих двійкових матриць Toeplitz з максимальними детермінантами для n = 1..10: ghostbin.com/paste/axkpa
Тайло

2
Як спостереження, яке може допомогти іншим, але я не можу перевірити його після 14 років. Здається, що відповідні засоби верхнього ряду та перший стовпець матриці Toeplitz завжди є 0,4 <= m <= 0,6 для максимального визначника.
MickyT

Відповіді:


3

C ++ з pthreads

Це доходить до n = 14 трохи менше 1 хвилини на моїй машині. Але оскільки це лише двоядерний ноутбук, я сподіваюся, що 8-ядерний тестовий апарат зможе закінчити n = 15 за 2 хвилини. На моїй машині йде близько 4:20 хвилин.

Я дуже сподівався придумати щось більш ефективне. Там вже є повинні бути способом більш ефективно розрахувати детерміновані бінарну матрицю. Я хотів придумати якийсь динамічний підхід програмування, який підраховує терміни +1 і -1 в детермінантному розрахунку. Але це поки що не зовсім зійшлося.

Оскільки щедрості закінчуються, я застосував стандартний підхід грубої сили:

  • Переведіть петлю на всі можливі матриці Toeplitz.
  • Пропустіть один із двох у кожній перекладеній матричній парі. Оскільки матриця описується значеннями біткових масок, це зробити нескладно, пропустивши всі значення, де зворотний біт маска менша, ніж сама бітова маска.
  • Детермінант обчислюється за допомогою розкладання текстової книги LR. За винятком незначної настройки продуктивності, головне вдосконалення алгоритму з моєї книги чисельних методів коледжу полягає в тому, що я використовую більш просту стратегію зведення.
  • Паралелізація проводиться за допомогою pthreads. Просто використання регулярного інтервалу для значень, оброблених кожною ниткою, спричинило дуже погане врівноваження навантаження, тому я ввів деякий хит.

Я тестував це на Mac OS, але раніше я використовував подібний код на Ubuntu, тому сподіваюся, що це буде компілюватися та працювати без перешкод:

  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.
Рето Коради

1
@Lembik Дивіться код paste.ubuntu.com/11915546 для коду та paste.ubuntu.com/11915532 для результатів до n = 19.
Рето Коради

1
@Lembik Результати до n = 20 наведено на paste.ubuntu.com/11949738 . Тепер вони перелічують усі пов'язані рішення, включаючи атрибути, щоб швидко побачити значення діагоналі та чи є вони циркуляційними. Всі максимуми для m = 18,19,20 - циркуляційні матриці. Будь ласка, двічі перевірте визначники, перш ніж публікувати їх де-небудь.
Рето Коради

8

J

Оновлення: вдосконалений код для пошуку понад половини значень. Тепер обчислюється n=12зручно протягом 120 секунд (з 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''

Запустіть це та вбийте, коли дві хвилини підняться. Мої результати (MBP 2014 - 16 ГБ оперативної пам’яті):

┌────┬─┬─────────────┬─┐
│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потрібно приблизно 18 ГБ пам'яті.
Денніс

Це дуже приємне поліпшення. Вихід, однак, злегка баггі. Для мене, використовуючи j64-804, він виводить n = 1 вдвічі, тому він виходить одним на будь-який час.

@ Лембік А це правильно. Я щойно оновив код; ви можете спробувати запустити ще раз? Спасибі! (Я встановив це для обчислення до n=13. Ви можете змінити 13в другому до останнього рядка, щоб він обчислював все, що завгодно.)
Legendre

Я запустив його ще раз, і він все одно доходить до 12.

@ Лембік Хм .. Ви кажете, що він потрапляє до 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для обчислення визначника, весь час відстежуючи максимальне значення. В кінці цього я просто роздруковую максимальний приріст nна 1 і продовжую, поки не пройде 10 хвилин.

Для цього вам знадобиться встановити scipy .

Редагувати 1: Змінено спосіб побудови початкових рядків, використовуючи натомість itertools.product, завдяки Sp3000!

Редагування 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, 8 Гб оперативної пам’яті):

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
Я бачу так багато гольфу Python на цьому сайті, що я часто забуваю, що для загальних цілей це насправді читабельна мова.
Олексій А.

Можливо, це може бути значно прискорено, оскільки це не скористається тим, що це двійкова матриця.
lirtosiast

@ThomasKwa Якщо я чесний, я не маю уявлення, як цим скористатися: P
Kade

Цитата з numpy документації: "Детермінант обчислюється за допомогою LU-факторизації, використовуючи процедуру LAPACK z / dgetrf." Я подивився на dgetrf, і він говорить, що він використовує подвійну точність; Залежно від GPU ОП, одна точність може бути швидшою.
lirtosiast

4

C ++

Bruteforce з використанням OpenMP для паралелізації та простої оптимізації, щоб уникнути оцінки визначника для транспонованих матриць.

$ 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 Я не знав, що існують швидші алгоритми, так що так, це рішення досить погано.
Тайло

Ви можете вивчити використання LU Decomposition, щоб знайти визначник матриці. Це O(n^3), я вважаю, хоча можна швидше з деякими цікавими алгоритмами. Я вважаю, що більшість побудованих тут використовуваних варіантів декомпозиції зазвичай використовують варіант розкладання.
BrainSteel

@BrainSteel, так, я подивився на це, але я міг би також скористатися O(n^2)алгоритмом, якщо я оновлюю свою відповідь.
Тайло

Відповідно до випадкового пошуку у Вікіпедії, детермінант матриці Toeplitz може бути визначений в O(n^2). Але я думаю , що вузьке місце цієї проблеми є пошук серед O(4^n)багатьох 0-1 nпо nматрицями.
Legendre

2

R

Вам доведеться встановити 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);
  )
}

Щодо обчислювальних визначень Toeplitz у O(n^2)часі: У своєму обмеженому дослідженні я постійно наштовхувався на вимогу, щоб всі провідні неповнолітні мали бути ненульовими для того, щоб алгоритми працювали, що є головною перешкодою для цього завдання. Сміливо дайте мені покажчики, якщо ви знаєте про це більше, ніж я.


Ви бачили цей документ? scienpress.com/upload/JAMB/Vol%201_1_4.pdf . Мені не було зрозуміло, у чому полягає складність. Для прикладу n = 5 здавалося досить багато термінів.
Рето Коради

@RetoKoradi Так, я це бачив. Здається, складність не є многочленною, якщо врахувати, що, наприклад, e_{k+1}має в 4 рази більше, ніж кількість компонентів e_k. У роботі багато пропусків. Обернена матриця має розклад LU, якщо всі провідні головні неповнолітні є ненульовими. (Зверніть увагу на знаменники, наприклад a_0- неявно вони гарантовані як ненульові.) Унікальність походить від L, що є одиницею трикутної форми. Автор також не згадав про числову стійкість. Якщо посилання стає недоступним, стаття - "Про обчислення детермінант матриць Топліца" Хусана-Чу Лі (2011).
Мітч Шварц
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.