Найти максимальный определитель для каждого размера матрицы Теплица

14

Для фиксированного n рассмотрим матрицы Теплица n by n с записями, которые либо 0, либо 1. Цель состоит в том, чтобы найти максимальный определитель для всех таких матриц Теплица.

задача

Для каждого значения nот 1 и выше выведите максимальный определитель по всем n на n матриц Тёплица с записями, равными 0 или 1. Должен быть один выход, для nкоторого должен быть максимальный определитель, а также пример матрицы, которая его достигает.

Гол

Ваша оценка - самая большая сумма, которую nваш код получает за 2 минуты на моем компьютере. Чтобы уточнить, ваш код может работать в общей сложности 2 минуты, это не 2 минуты n.

Стяжка

Если две записи получают одинаковый nсчет, то выигрышная запись будет той, которая наберет наибольшее nза самое короткое время на моей машине. Если по этому критерию две лучшие записи равны, то победителем будет ответ, представленный первым.

Языки и библиотеки

Вы можете использовать любой свободно доступный язык и библиотеки, которые вам нравятся. Я должен быть в состоянии запустить ваш код, поэтому, пожалуйста, включите полное объяснение того, как запустить / скомпилировать ваш код в Linux, если это вообще возможно.

Моя машина Время будет запущено на моей машине. Это стандартная установка Ubuntu на восьмиъядерный процессор AMD FX-8350. Это также означает, что мне нужно иметь возможность запустить ваш код.

Маленькие ответы

Для n = 1..10 выходы должны быть 1,1,2,3,5,9,32,56,125,315

Эта последовательность отсутствует в OEIS, и поэтому победившая заявка также может предложить новую запись там.

Записи до сих пор

  • n=10 n=11Vioz в Python
  • n=9Тийло в Си
  • n=12Лежандр в J
  • n=10Тенсибай в R
  • n=14SteelRaven в C ++
  • n=14RetoKoradi в C ++

источник
@AlexA. Вы правы, и я извинился. К счастью, эти две проблемы очень похожи, поэтому он легко сможет изменить свой код.
Решение от @Vioz дает последовательность, которая начинается с 1, 1, 2, 3, 5, 9, 32. Таким образом, значение для n = 5 отличается от того, что вы перечисляете. Поскольку все другие значения совпадают, похоже, что решение, вероятно, правильное, и это просто опечатка в вопросе?
Рето Коради
@RetoKoradi Спасибо. Исправлена.
Вот 10 возможных двоичных матриц Теплица с максимальными определителями для n = 1..10: ghostbin.com/paste/axkpa
Tyilo
2
Как наблюдение, которое может помочь другим, но я не могу проверить больше 14. Похоже, что соответствующие средства верхней строки и первого столбца матрицы Теплица всегда равны 0,4 <= m <= 0,6 для максимального определителя.
MickyT

Ответы:

3

C ++ с потоками

Это достигает п = 14 всего за 1 минуту на моей машине. Но поскольку это всего лишь 2-ядерный ноутбук, я надеюсь, что 8-ядерный тестовый компьютер сможет завершить n = 15 менее чем за 2 минуты. На моей машине это занимает около 4:20 минут.

Я действительно надеялся придумать что-нибудь более эффективное. Там уже есть должны быть способом более эффективно рассчитать детерминированные бинарную матрицу. Я хотел придумать какой-то подход динамического программирования, который учитывает члены +1 и -1 в вычислении детерминанта. Но пока что это не совсем так.

Поскольку срок действия награды истекает, я применил стандартный метод грубой силы:

  • Обведите все возможные матрицы Теплица.
  • Пропустите один из двух в каждой транспонированной матричной паре. Поскольку матрица описывается значениями битовой маски, это легко сделать, пропустив все значения, где обратная сторона битовой маски меньше самой битовой маски.
  • Детерминант рассчитывается с помощью учебника 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)? Это может быть полезно для больших матриц, где точность с плавающей точкой в ​​противном случае стала бы проблемой. Я действительно не искал литературу. Ну, я сделал быстрый поиск и нашел статью о вычислении определителей матриц Теплица. Но у меня было слишком много открытых вопросов, чтобы выделить время, чтобы попытаться реализовать их.
Рето Коради
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 дважды, так что его на единицу больше.
@Lembik Ах, все верно. Я только что обновил код; не могли бы вы попробовать снова? Благодарность! (Я установил его для вычисления до n=13. Вы можете изменить 13в строке от последней к последней, чтобы она вычисляла все, что вы пожелаете.)
Legendre
Я запустил его снова, и он все еще доходит до 12.
@Lembik Хм .. Вы говорите, что до 12 в течение определенного времени и до 13 через некоторое время (что я и ожидаю) или до 13 (то есть программа останавливается после 12)?
Лежандр
4

Python 2

Это очень простое решение и, вероятно, не выиграет конкурс. Но эй, это работает!

Я дам краткий обзор того, что именно происходит.

  1. Сначала я генерирую все возможные начальные строки для n. Например, когда n=2это сгенерирует массив длиной 2 n + 1 , где каждая строка имеет длину 2n-1. Это будет выглядеть следующим образом : [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]].
  2. Затем для каждой из этих возможных начальных строк я поворачиваю ее nраз и отрезаю первые nэлементы, чтобы сгенерировать соответствующую матрицу, и использую ее scipyдля вычисления определителя, сохраняя при этом отслеживание максимального значения. В конце этого я просто распечатываю максимум с шагом n1 и продолжаю, пока не пройдет 10 минут.

Для запуска вам понадобится Scipy .

Редактировать 1: Изменено, как начальные строки были построены с использованием вместо этого 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
Када
источник
Спасибо, но я думаю, что вы ответили на старую версию вопроса, который я боюсь. Теперь речь идет о матрицах Теплица и ограничение по времени составляет 2 минуты.
4
На этом сайте я вижу так много гитарного Python, что часто забываю, что для общих целей это действительно читаемый язык.
Алекс А.
Вероятно, это может быть значительно ускорено, потому что оно не использует тот факт, что это двоичная матрица.
lirtosiast
@ThomasKwa Если честно, я понятия не имею, как воспользоваться этим: P
Kade
Цитата из пустой документации: «Определитель вычисляется с помощью факторизации LU с использованием подпрограммы LAPACK z / dgetrf». Я посмотрел на dgetrf, и он говорит, что он использует двойную точность; в зависимости от графического процессора OP одиночная точность может быть быстрее.
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);
  }
}
SteelRaven
источник
Похоже, вы скоро сделаете свою первую запись OEIS, если кто-то не
2

С

Компилировать с:

$ clang -Ofast 52851.c -o 52851

Бежать с:

$ ./52851

Может вывести максимальный определитель в течение n = 1..10~ 115 секунд на моем компьютере.

Программа просто получает определитель каждой возможной двоичной матрицы Тёплица с размером n, однако каждый определитель матриц размера 5x5или меньше будет кэшироваться с использованием мемоизации.

Сначала я ошибочно предположил, что каждая подматрица матрицы Тёплица также будет матрицей Тёплица, поэтому мне нужно было только запоминать 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;
}
Tyilo
источник
Похоже, вы вычисляете определитель, используя расширение по несовершеннолетним; это имеет O(n!)сложность, поэтому вам лучше использовать другой алгоритм.
lirtosiast
@ThomasKwa Я не знал, что были более быстрые алгоритмы, так что да, это решение довольно плохое.
Tyilo
Возможно, вы захотите изучить использование декомпозиции LU, чтобы найти определитель матрицы. Это O(n^3), я считаю, хотя можно быстрее с некоторыми интересными алгоритмами. Я полагаю, что большинство встроенных функций, используемых здесь, обычно используют вариант декомпозиции для выполнения определителей.
BrainSteel
@BrainSteel, да, я смотрел на это, но я мог бы также пойти на O(n^2)алгоритм, если я обновляю свой ответ.
Tyilo
Согласно случайному поиску в Википедии, определитель матрицы Теплица можно определить в O(n^2). Но я думаю, что узким местом проблемы является поиск среди O(4^n)множества 0-1 nпо nматрицам.
Лежандр
2

р

Вам придется установить R и пакеты, перечисленные с install.packages("package_name")

Не получалось менее 2 минут на моей машине с этой версией (я должен попробовать с параллельной модификацией)

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

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

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

Вызов и выход:

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

Бенчмарк на моей машине:

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

Для информации, для диапазона 1:11, это занимает 285 секунд.

Tensibai
источник
1

PARI / GP, n = 11

Это грубая сила, но она пользуется det(A^T) = det(A). Я только публикую это, чтобы продемонстрировать, как легко пропустить транспонирование. Самый младший бит b1содержит верхнюю левую ячейку, а остальные биты содержат остаток верхней строки. b2содержит остаток левой колонки. Мы просто применяем b2 <= (b1>>1).

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

Что касается вычисления детерминантов Теплица во O(n^2)времени: в моем ограниченном исследовании я постоянно сталкивался с требованием, чтобы все ведущие главные миноры были ненулевыми, чтобы алгоритмы работали, что является основным препятствием для этой задачи. Не стесняйтесь указывать мне, если вы знаете об этом больше, чем я.

Митч Шварц
источник
Вы видели эту газету? scienpress.com/upload/JAMB/Vol%201_1_4.pdf . Мне не было ясно, в чем сложность. Казалось, что для примера n = 5 было довольно много терминов.
Рето Коради
@RetoKoradi Да, я видел это. Кажется, сложность не является полиномиальной, учитывая, что, например, e_{k+1}имеет в 4 раза больше компонентов, чем e_k. В статье много упущений. Обратимая матрица имеет LU-разложение, если все ведущие главные миноры отличны от нуля. (Обратите внимание на знаменатели, например a_0- неявно они гарантированно не равны нулю.) Уникальность исходит из того, что L является единичным треугольником. Автор также не упомянул численную стабильность. В случае, если ссылка становится недоступной, статья «О вычислении детерминантов матриц Теплица» Хсуан-Чу Ли (2011).
Митч Шварц