Как стать автором
Обновить
91
0

Пользователь

Отправить сообщение

Замена Set на битовую маску дала ускорение еще в 20 раз, программа отрабатывает за пару сотых секунды.


Скрытый текст
#include <set>
#include <stdio.h>
#include <ctime>
// #include "stdafx.h"

typedef std::set<int> Set;
typedef Set::iterator SetIterator;

#define N 4
#define MAX (N*N)
#define S 34

int main(int argc, char *argv[])
{
  // x11 x12 x13 x14
  // x21 x22 x23 x24
  // x31 x32 x33 x34
  // x41 x42 x43 x44

  const clock_t begin_time = clock();

  int squares = 0;
  int mask = 0;
  for (int x11 = 1; x11 <= MAX; x11++) {
    mask += 1 << x11;
    for (int x14 = 1; x14 <= MAX; x14++) {
      if (mask & (1 << x14)) continue;
      mask += 1 << x14;
      for (int x41 = 1; x41 <= MAX; x41++) {
        if (mask & (1 << x41)) continue;
        int x44 = S - x11 - x14 - x41;
        if (x44 < 1 || x44 > MAX) continue;
        if (mask & (1 << x44) || x44 == x41) continue;
        mask += 1 << x41;
        mask += 1 << x44;
        for (int x22 = 1; x22 <= MAX; x22++){
          if (mask & (1 << x22)) continue;
          int x33 = S - x11 - x22 - x44;
          if (x33 < 1 || x33 > MAX) continue;
          if (mask & (1 << x33) || x33 == x22) continue;
          mask += 1 << x22;
          mask += 1 << x33;
          for (int x23 = 1; x23 <= MAX; x23++){
            if (mask & (1 << x23)) continue;
            int x32 = S - x14 - x23 - x41;
            if (x32 < 1 || x32 > MAX) continue;
            if (mask & (1 << x32) || x32 == x23) continue;
            mask += 1 << x23;
            mask += 1 << x32;
            for (int x12 = 1; x12 <= MAX; x12++){
              if (mask & (1 << x12)) continue;
              int x13 = S - x11 - x12 - x14;
              if (x13 < 1 || x13 > MAX) continue;
              if (mask & (1 << x13) || x12 == x13) continue;
              int x42 = S - x12 - x22 - x32;
              if (x42 < 1 || x42 > MAX) continue;
              if (mask & (1 << x42) || x42 == x12 || x42 == x13) continue;
              int x43 = S - x13 - x23 - x33;
              if (x43 < 1 || x43 > MAX) continue;
              if (mask & (1 << x43) || x43 == x12 || x43 == x13 || x43 == x42) continue;
              mask += 1 << x12;
              mask += 1 << x13;
              mask += 1 << x42;
              mask += 1 << x43;
              for (int x21 = 1; x21 <= MAX; x21++){
                if (mask & (1 << x21)) continue;
                int x31 = S - x11 - x21 - x41;
                if (x31 < 1 || x31 > MAX) continue;
                if (mask & (1 << x31) || x31 == x21) continue;
                int x24 = S - x21 - x22 - x23;
                if (x24 < 1 || x24 > MAX) continue;
                if (mask & (1 << x24) || x24 == x21 || x24 == x31) continue;
                int x34 = S - x31 - x32 - x33;
                if (x34 < 1 || x34 > MAX) continue;
                if (mask & (1 << x34) || x34 == x21 || x34 == x24 || x34 == x31) continue;

                printf("%d %d %d %d  %d %d %d %d  %d %d %d %d  %d %d %d %d\n", x11, x12, x13, x14, x21, x22, x23, x24, x31, x32, x33, x34, x41, x42, x43, x44);
                squares++;
                }
              mask -= 1 << x12;
              mask -= 1 << x13;
              mask -= 1 << x42;
              mask -= 1 << x43;
              }
            mask -= 1 << x23;
            mask -= 1 << x32;
            }
          mask -= 1 << x22;
          mask -= 1 << x33;
          }
        mask -= 1 << x41;
        mask -= 1 << x44;
        }
      mask -= 1 << x14;
      }
    mask -= 1 << x11;
    }

  printf("CNT: %d\n", squares);

  float diff_t = float(clock() - begin_time)/CLOCKS_PER_SEC;
  printf("T = %.2fs\n", diff_t);

  return 0;
}

Во-первых, x22 + x23 + x32 + x33 = 2S — (x21 + x24 + x31 + x34) = 2S — (2S — (x11 + x14 + x41 + x44)) = x11 + x14 + x41 + x44. Но (x22 + x23 + x32 + x33) + (x11 + x14 + x41 + x44) = 2S как сумма двух диагоналей. Отсюда x22 + x23 + x32 + x33 = x11 + x14 + x41 + x44 = S. Насколько я помню, непосредственно это свойство на квадраты высших порядков не обобщается, но я не специалист.

Я ничего не понимаю в С++, но немного переработал вашу программу, получив ускорение в 300 раз. В одном потоке, на CPU.


Ключевая идея заключается в том, что в магическом квадрате 4х4 помимо вертикалей, горизонталей и диагоналей магическую сумму дает также сумма угловых ячеек и сумма центральных ячеек. Это позволяет намного раньше останавливать перебор вариантов.


Скрытый текст
#include <set>
#include <stdio.h>
#include <ctime>

typedef std::set<int> Set;
typedef Set::iterator SetIterator;

#define N 4
#define MAX (N*N)
#define S 34

int main(int argc, char *argv[])
{
  // x11 x12 x13 x14
  // x21 x22 x23 x24
  // x31 x32 x33 x34
  // x41 x42 x43 x44

  const clock_t begin_time = clock();

  int squares = 0;
  int digits[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
  Set mset(digits, digits + N*N);
  for (int x11 = 1; x11 <= MAX; x11++) {
    Set set14(mset); set14.erase(x11);
    for (SetIterator it14 = set14.begin(); it14 != set14.end(); it14++) {
      int x14 = *it14;
      Set set41(set14); set41.erase(x14);
      for (SetIterator it41 = set41.begin(); it41 != set41.end(); it41++) {
        int x41 = *it41;
        int x44 = S - x11 - x14 - x41;
        if (x44 < 1 || x44 > MAX) continue;
        if (x44 == x11 || x44 == x14 || x44 == x41) continue;
        Set set22(set41); set22.erase(x41); set22.erase(x44);
        for (SetIterator it22 = set22.begin(); it22 != set22.end(); it22++){
          int x22 = *it22;
          int x33 = S - x11 - x22 - x44;
          if (x33 < 1 || x33 > MAX) continue;
          if (x33 == x11 || x33 == x14 || x33 == x41 || x33 == x44 || x33 == x22) continue;
          Set set23(set22); set23.erase(x22); set23.erase(x33);
          for (SetIterator it23 = set23.begin(); it23 != set23.end(); it23++){
            int x23 = *it23;
            int x32 = S - x14 - x23 - x41;
            if (x32 < 1 || x32 > MAX) continue;
            if (x32 == x11 || x32 == x14 || x32 == x41 || x32 == x44 || x32 == x22 || x32 == x33 || x32 == x23) continue;
            Set set12(set23); set12.erase(x23); set12.erase(x32);
            for (SetIterator it12 = set12.begin(); it12 != set12.end(); it12++){
              int x12 = *it12;
              int x13 = S - x11 - x12 - x14;
              if (x13 < 1 || x13 > MAX) continue;
              if (x13 == x11 || x13 == x14 || x13 == x41 || x13 == x44 || x13 == x22 || x13 == x33 || x13 == x23 || x13 == x32 || x13 == x12) continue;
              int x42 = S - x12 - x22 - x32;
              if (x42 < 1 || x42 > MAX) continue;
              if (x42 == x11 || x42 == x14 || x42 == x41 || x42 == x44 || x42 == x22 || x42 == x33 || x42 == x23 || x42 == x32 || x42 == x12 || x42 == x13) continue;
              int x43 = S - x13 - x23 - x33;
              if (x43 < 1 || x43 > MAX) continue;
              if (x43 == x11 || x43 == x14 || x43 == x41 || x43 == x44 || x43 == x22 || x43 == x33 || x43 == x23 || x43 == x32 || x43 == x12 || x43 == x13 || x43 == x42) continue;
              Set set21(set12); set21.erase(x12); set21.erase(x13); set21.erase(x42); set21.erase(x43);
              for (SetIterator it21 = set21.begin(); it21 != set21.end(); it21++){
                int x21 = *it21;
                int x31 = S - x11 - x21 - x41;
                if (x31 < 1 || x31 > MAX) continue;
                if (x31 == x11 || x31 == x14 || x31 == x41 || x31 == x44 || x31 == x22 || x31 == x33 || x31 == x23 || x31 == x32 || x31 == x12 || x31 == x13 || x31 == x42 || x31 == x43 || x31 == x21) continue;
                int x24 = S - x21 - x22 - x23;
                if (x24 < 1 || x24 > MAX) continue;
                if (x24 == x11 || x24 == x14 || x24 == x41 || x24 == x44 || x24 == x22 || x24 == x33 || x24 == x23 || x24 == x32 || x24 == x12 || x24 == x13 || x24 == x42 || x24 == x43 || x24 == x21 || x24 == x31) continue;
                int x34 = S - x31 - x32 - x33;
                if (x34 < 1 || x34 > MAX) continue;
                if (x34 == x11 || x34 == x14 || x34 == x41 || x34 == x44 || x34 == x22 || x34 == x33 || x34 == x23 || x34 == x32 || x34 == x12 || x34 == x13 || x34 == x42 || x34 == x43 || x34 == x21 || x34 == x31 || x34 == x24) continue;

                printf("%d %d %d %d  %d %d %d %d  %d %d %d %d  %d %d %d %d\n", x11, x12, x13, x14, x21, x22, x23, x24, x31, x32, x33, x34, x41, x42, x43, x44);
                squares++;
                }
              }
            }
          }
        }
      }
    }

  printf("CNT: %d\n", squares);

  float diff_t = float(clock() - begin_time)/CLOCKS_PER_SEC;
  printf("T = %.2fs\n", diff_t);

  return 0;
}

Мне кажется, что если вместо Set использовать просто битовую маску, то перебор ускорится еще больше. А потом уже можно и многопоточность, и GPU прикрутить.

В тексте статьи написано про wheel optimization.

Разумеется.


В 1996 году [Святослав Вакарчук] поступил в аспирантуру кафедры теоретической физики [Львовского национального] университета. Тема кандидатской диссертации — «Суперсимметрия электронов в магнитном поле».
https://ru.wikipedia.org/wiki/Вакарчук,_Святослав_Иванович
Написать закладку, а затем подогнать хеш, модифицируя комментарии? Это при условии, что мы умеем находить коллизии, разумеется.

К сожалению, потребление памяти О(1) в задаче факторизации невозможно. Потребуется по крайней мере O(n), чтобы было куда прочитать исходное число и сохранить результаты работы.


P. S. Ваша энергия в столь зрелом возрасте вызывает неподдельное уважение. Я восхищен.

Гарантирую: Потребляет меньше памяти, значительно меньше.

Сколько именно? Пусть дано n-битное число. Какой объем памяти понадобится: O(1), O(n), O(2^n), иное?


Надеюсь, что сравнят другие.

Ох. Я не имел в виду сравнение уровня прозорливости и вдохновенности. Меня сравнение тактико-технических характеристик интересовало.

Чем ваша методика факторизации предположительно лучше уже известных алгоритмов? Быстрее работает? Потребляет меньше памяти? Наконец, может быть она проще? Из вашего текста это не ясно, а без такого сравнения читатель мало заинтересован разбираться в путанном и непривычном изложении.


Проблема действительного разложения данного числа на простые множители одна из труднейших задач математики. В третьем столетии до н.э. Эрастофен предложил метод нахождения всех простых чисел меньших данного предела А. После некоторого усовершенствования в начале 20 века Бруном, этот способ до сих пор является единственным способом решения данной задачи без использования вычислительных устройств. Другие попытки найти законы распределения простых чисел не дали значительных результатов.

Это, мягко говоря, неправда и, простите, говорит о глубоком невежестве. К сожалению, я не в силах дать синопсис развития вычислительной теории чисел за последний век в рамках комментария на Хабре. Так, например, для нахождения всех простых чисел, меньших данного, в 20-м веке было разработано решето Аткина, превосходящее решето Евклида по скорости работы и все еще доступное для ручных вычислений.


Впрочем, непонятно, почему в статье, посвященной алгоритму факторизации данного числа, вы приводите в качестве примера алгоритм Евклида, который к задаче факторизации отношения не имеет. Сравните свой алгоритм хотя бы с методом факторизации Ферма, которым последний пользовался без всяких компьютеров в 17-м веке.

Тогда с тем же успехом можно сложить первые эннадцать рядом треугольника Паскаля в массив и объявить о революционном алгоритме, который вычисляет биномиальные коэффициенты за O(1).
Для приближенного вычисления больших коэффициентов уж лучше воспользоваться рядом Стирлинга.
> При вычислении с помощью треугольника Паскаля трудоёмкость имеет оценку O(n^2).
> При вычислении с помощью быстрых Фурье-преобразований трудоёмкость имеет оценку O(n*log n).

Сам массив значений биномиальных коэффициентов от С_n^0 до C_n^n занимает что-то типа O(n^2) бит. Как вы умудрились их посчитать за асимптотически меньшее время?
Скажите, пожалуйста, каким автоматическим переводчиком вы пользовались? Кошмар же.
Мопед не мой, я только перевожу, причем начиная с пятой статьи. До этого переводы готовил Monnoroch.
Пришлите почту личным сообщением, я отправлю пятую и шестую статьи в LaTeX.
С удовольствием, если вы подскажете, по какому критерию он срабатывает или не срабатывает. Я погуглил и не нашел ничего конкретного. Могу, если хотите, выдать вам исходник в markdown для экспериментов.
В Safari вид для чтения доступен во всех трех статьях.
Про коконус и нус — хорошо подмечено. Возможно, что это одна из причин по которой Маклейн избегает этого термина в своем руководстве: «Мы предпочитаем говорить „конус с основанием F“, а не „коконус“».
Проблема теорката в том, что нужно иметь в голове достаточно примеров разных категорий, включая экзотические. Но даже математики многих специальностей всю жизнь работают с какой-то одной и довольно узкой. В этом отношении, кстати, программирование даёт много понятных широким массам примеров, в то время как примеры из книги Маклейна не всегда доступны неалгебраистам-нетопологам.
Я же не предлагаю писать ковесть, кочувствие и кознание :) Просто в данной отрасли терминологическая традиция сложилась так, а не иначе.
Мне больше нравятся кококонструкции.
Никогда не встречал такого термина.
1
23 ...

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность