2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки


Правила форума


Посмотреть правила форума



Начать новую тему Ответить на тему
 
 Практические способы представления конечной группы
Сообщение26.07.2024, 11:57 
Аватара пользователя


26/05/12
1700
приходит весна?
Я раздумываю тут над тем, как наиболее удобно можно хранить группу в комьюторе для работы с ней. Хотелось бы услышать комментарии по тому, что я уже пробовал, а так же предложения чего-то нового.

Наиболее очевидный метод (что называется "в лоб") — это представление в виде таблицы умножения. С одной стороны очень удобно: сразу известен порядок группы, каждый элемент нумеруется от нуля до порядка не включая, групповая операция над элементами выполняется элементарно просто и в одну примитивную операцию: взятие элемента по двум индексам из двумерного массива. Недостаток тоже очевиден: представление занимает много памяти. Даже у группы порядка 2500 при использовании типа shot в два байта таблица умножения займёт $$\frac{2\cdot 2500^2}{2^{20}}\approx 11,9\;\text{МБайт}$$ Это за пределами не только кэша 1-го и 2-го уровня, но и 3-го тоже (для современных процессоров, у меня старенький, только первые два есть: 64 КБ / 1 МБ). Это значит, что программа будет работать с оперативной памятью, которая, как известно, очень медленная.

Этой медленностью, а так же ограниченностью её размера, мотивируется мой поиск альтернатив. В виде текстовых соотношений способ отпадает сразу. Он компактный, да, но он удобен лишь для красивого хранения или указания какая группа имеется в виду. Развернуть соотношения в таблицу умножения группы или ещё во что-то удобное для работы — задача весьма нетривиальна.

Некоторые группы можно задать формулами, например циклические группы. Операция будет просто сумма по модулю. Причём последнюю, чтобы избавиться от медленной операции деления можно превратить в операцию индексирования в массиве двойной (от порядка группы) длины. Аналогично можно реализовать прямое или даже некоторые полупрямые произведения двух групп: входные индексы элементов разбираются на компоненты для каждой из двух групп в произведении, находятся значения групповой операции в каждой из двух групп, а затем эти значения собираются в конечный индекс. Здесь тоже есть деление с остатком, и оно тоже оптимизируется заранее подсчитанными массивами. Это работает и экономит память, но недостатки тоже очевидны. Во-первых, далеко не все группы представимы в виде произведения двух или более других легко считаемых. Просто потому, что существуют простые группы со сложной структурой. Так что у метода отсутствует универсальность. Во-вторых, даже одно скалярное произведение считается уже медленнее, чем индексация таблицы умножения на всю память (на моём компе, во всяком случае, хотя я может криво накодил).

код: [ скачать ] [ спрятать ]
Используется синтаксис Java
public class CyclicGroup implements Group {
   
    private int order;
    private int [] productFunc, inverseFunc;
   
    public CyclicGroup (int argOrder) {
        int k, l;
        if (1 > argOrder) {
            throw new IllegalArgumentException ("\n\nIllegal group order.\n");
        }
        order       = argOrder;
        l           = argOrder * 2 - 1;
        productFunc = new int [l];
        inverseFunc = new int [order];
        for (k = 1; order > k; ++k) {
            productFunc [k] = k;
            inverseFunc [k] = order - k;
        }
        for (++k; l > k; ++k) {
            productFunc [k] = k - order;
        }
    }
   
    @Override
    public int order () {
        return order;
    }
   
    @Override
    public int product (int argLeft, int argRight) {
        if (0 > argLeft || 0 > argRight || order <= argLeft || order <= argRight) {
            throw new IllegalArgumentException ("\n\nIllegal element index.\n");
        }
        //return order > argLeft + argRight ? argLeft + argRight : argLeft + argRight - order;
        return productFunc [argLeft + argRight];
    }

    @Override
    public int inverse (int arg) {
        if (0 > arg || order <= arg) {
            throw new IllegalArgumentException ("\n\nIllegal element index.\n");
        }
        //return 0 == arg ? 0 : order - arg;
        return inverseFunc [arg];
    }
   
    @Override
    public int [] generators () {
        return new int [] {1};
    }
}


код: [ скачать ] [ спрятать ]
Используется синтаксис Java
import java.util.Arrays;

public class CartesianGroupProduct implements Group {
   
    private int order, subOrder;
    private int [] gens, inverseFunc, subFunc1, subFunc2;
    private Group group1, group2;
   
    public CartesianGroupProduct (Group argGroup1, Group argGroup2) {
        int k, l, m;
        int [] gens1, gens2;
        Group .checkIntegrity (argGroup1);
        Group .checkIntegrity (argGroup2);
        order    = argGroup1 .order () * argGroup2 .order ();
        subOrder = argGroup2 .order ();
        group1   = argGroup1;
        group2   = argGroup2;
        gens1    = argGroup1 .generators ();
        gens2    = argGroup2 .generators ();
        gens     = new int [gens1 .length + gens2 .length];
        k = 0;
        for (l = 0; gens1 .length > l; ++k, ++l) {
            gens [k] = gens1 [l] * subOrder;
        }
        for (l = 0; gens2 .length > l; ++k, ++l) {
            gens [k] = gens2 [l];
        }
        Arrays .sort (gens);
        subFunc1    = new int [order];
        subFunc2    = new int [order];
        inverseFunc = new int [order];
        for (k = 1; order > k; ++k) {
            subFunc1 [k] = k / subOrder;
            subFunc2 [k] = k % subOrder;
        }
        for (k = 1; order > k; ++k) {
            l = 0;
            while (true) {
                m = product (k, l);
                if (0 == m) {
                    break;
                }
                l = m;
            }
            inverseFunc [k] = l;
        }
    }
   
    @Override
    public int order () {
        return order;
    }
   
    @Override
    public int product (int argLeft, int argRight) {
        if (0 > argLeft || 0 > argRight || order <= argLeft || order <= argRight) {
            throw new IllegalArgumentException ("\n\nIllegal element index.\n");
        }
        return  group1 .product (subFunc1 [argLeft], subFunc1 [argRight]) * subOrder +
                group2 .product (subFunc2 [argLeft], subFunc2 [argRight]);
        //return  group1 .product (argLeft / subOrder, argRight / subOrder) * subOrder +
        //        group2 .product (argLeft % subOrder, argRight % subOrder);
    }
   
    @Override
    public int inverse (int arg) {
        if (0 > arg || order <= arg) {
            throw new IllegalArgumentException ("\n\nIllegal element index.\n");
        }
        return inverseFunc [arg];
    }
   
    @Override
    public int [] generators () {
        return Arrays .copyOf (gens, gens .length);
    }
}


Третий (или четвёртый?) метод заключается в представлении элементов группы в виде перестановок. Групповая операция тогда будет их композицией. (Этот способ является одним из первых, рассматриваемых в любой книжке по теории групп, начинающей с азов). Ну, таблица умножения уже является в прямом смысле таким представлением, надо лишь присмотреться. Однако, я имею в виду такие перестановки, которые значительно короче порядка группы. Здесь сразу возникают большая проблема и тележка вопросов. А как найти для заданной группы набор перестановок с минимальной длиной? Возможно ли это алгоритмизировать в общем случае? Может получится, если искать не минимальной длины, а просто достаточно короткие перестановки? Всегда ли можно найти перестановку более короткую, чем порядок группы? (ОК, нет, контрпример: симметрические группы). Если нет, то когда можно, и насколько эффективно? Ну и практический вопрос (когда задачи выше решены): быстрый поиск результата композиции двух перестановок в списке перестановок, чтобы вернуть индекс результирующего элемента. Ничего более эффективного, чем бинарный поиск массива в сортированном массиве массивов (перестановок), мне не видится.

Пока единственный способ (который я знаю) получения групп в таком представлении — это случай, когда они естественным образом вычисляются сразу в этом виде. Я имею в виду группы автоморфизмов других групп. Не уверен, что группа автоморфизмов порядка 98784 от группы порядка 147 на много лучше случая выше (в плане расхода памяти), но хотя бы в теории с ней можно будет хоть как-то работать, потому что таблица умножения для неё займёт 18 ГБ! (С моими 3-мя явно запредельная величина). И тут можно пытаться делать всякие оптимизации, например, сгруппировать элементы по величине порядка, сделать списки перестановок внутри каждого порядка, а полную перестановку задавать как комбинацию перестановок для каждого из порядков. Тогда память будет ещё больше экономиться, правда производительность пострадает.

Я ничего не забыл? Буду рад любым интересным и необычным предложениям.

 Профиль  
                  
 
 Re: Практические способы представления конечной группы
Сообщение26.07.2024, 12:34 
Заслуженный участник


07/08/23
1196
Ещё есть задание группы через представление, скажем, над $\mathbb Q^{\mathrm{alg}}$, в виде набора образующих матриц (обычных или разреженных). Вообще можно задавать группы как подгруппы в любых уже известных группах наборами образующих, но через перестановки и через линейные представления — это самые удобные варианты.

Группы типа универсальных центральных расширений $\mathrm S_n$ и через представления задавать не очень удобно (у спинорного представления размерность около $2^n$), их удобнее представлять просто как абелево расширение уже известной группы с данным 2-коциклом. Не забывайте и про другие групповые конструкции, которые дают новые группы: прямые произведения, полупрямые произведения, амальгамы (для бесконечных групп), факторгруппы.

Не знаю насчёт работы с группами на компьютере, но с точки зрения математики всё это время от времени удобно. Задание образующими и соотношениями тоже бывает алгоритмически эффективным, хотя бы для групп Кокстера типа $\mathsf E_8$.

 Профиль  
                  
 
 Re: Практические способы представления конечной группы
Сообщение26.07.2024, 17:10 
Аватара пользователя


26/05/12
1700
приходит весна?
О, я забыл про граф Кэли! Это очень компактное представление группы. Для каждой вершины, представляющей элемент, нужно указать в какой элемент ведёт каждое направленное ребро, соответствующее умножению на образующую справа. Поэтому расход памяти будет $$O(rn)$$ где r — выбранное число образующих, а n — порядок группы. При этом, чтобы найти групповую операцию над двумя элементами, необходимо знать представление второго элемента в виде слова образующих (кратчайшего, разумеется). Дальше потребуется просто взять первый элемент и перейти по цепочке рёбер, соответствующих символам этого слова. Для хранения всех слов потребует ещё память в размере $$O(wn)$$ где w — средняя длина слова. Трудоёмкость групповой операции при этом будет всего $$O(w)$$ Это не константное время, как с таблицей, но тоже ничего, если удастся представить слова достаточно коротко. В этом плане чем "более неабелева" группа, тем лучше. Каждая комбинация из двух, трёх и так далее символов будет как правило несократима. Это значит, что максимальная длина слова будет в лучше случае находится из соотношения $$w_m\sim\min\limit_{w_m}^{}\left(n\le\sum\limit_{k=0}^{w_m}r^k=\frac{r^{w_m+1}-1}{r-1}\right)\sim\frac{\ln n}{\ln r}$$ В худшем случае группа абелева (или вообще циклическая), все образующие переставляются, и мы можем только надеяться на $$w_m\sim\sqrt[r]{n}$$ за счёт выбора образующих как равноудалённых степеней нескольких порождающих элементов большого порядка. Но такое редко встречается. Если удвоить расход памяти для графа, то можно хранить умножение на образующих слева. Тогда в случае, когда два умножаемых элемента имеют разную длину слова, как отправной берётся тот, у которого она больше, а направление (и соответствующих граф) определяется тем, оказался ли элемент с коротким именем слева или справа. Но это количественное, а не качественное улучшение.

Это очень неплохое представление группы. Я сейчас так думаю, даже идеальное для больших групп. И тут можно выбирать баланс между производительностью и расходом памяти, задавая различное число образующих (оно, разумеется, значительно больше ранка группы, чтобы слова были по-короче). Единственная проблема правильно выбрать эти самые образующие. Здесь мне видится пока только жадный алгоритм: смотрим на слово с наибольшей длиной и добавляем его в качестве образующей. Дальше можно чуть-чуть пооптимизировать: когда достигнуто желаемое количество образующих, пробуем выкидывать каждую их них, смотрим какие слова получаются самыми длинными (и/или почти самыми), пробуем соответствующие элементы вместо выкинутой образующей. Таким образом оптимизируется средняя длина слова. Или максимальная длина слова (даже не знаю, на какую характеристику смотреть в первую очередь). Этот алгоритм не даст наилучшего решения, но его можно проделать довольно быстро, если выбрать удачный критерий останова (несмотря на эксперименты, мне так и не вышло его подобрать).

Построение имён элементов по таблице умножения группы (или по любой другой функции, возвращающей результат групповой операции) делается за время $$O(rn+wn)$$

 Профиль  
                  
 
 Re: Практические способы представления конечной группы
Сообщение27.07.2024, 08:55 
Аватара пользователя


26/05/12
1700
приходит весна?
B@R5uk в сообщении #1647438 писал(а):
В худшем случае группа абелева (или вообще циклическая), все образующие переставляются, и мы можем только надеяться на $$w_m\sim\sqrt[r]{n}$$

Это не верно. Во всяком случае, для циклической группы $$\mathbb{Z}_n=\left\langle\;a,\;|\;a^n=I\;\right\rangle$$ можно воспользоваться соображениями алгоритма быстрого возведения в степень. Если выбрать образующие как степени элемента a, в свою очередь являющиеся степенями двойки: $$b_k=a^{2^{k-1}},\quad 1\le k\le r,\quad r=1+\left\lfloor\log_2{(n-1)}\right\rfloor$$ то длина слова будет не больше $$w\le r=O(\ln n)$$ Если ли же n очень велико, а r фиксировано, то можно использовать степени не двойки, а тройки или четвёрки. Можно вообще переменный показатель замутить, главное, чтобы степени элемента на логарифмической шкале отстояли одна от другой более-менее равномерно.

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

 Профиль  
                  
 
 Re: Практические способы представления конечной группы
Сообщение27.07.2024, 10:08 
Заслуженный участник


07/08/23
1196
Про память для хранения произвольной конечной группы порядка $n$ как будто всё известно, нужно $\Theta(n)$ машинных слов. Есть препринт про достаточность, там внутри ссылка на необходимость. Заодно умножение считается за постоянное время. Правда, результат теоретический (использующий CFSG) и мне, как неспециалисту, не очень понятно, насколько это вообще реалистично запрограммировать и насколько там разумная константа в асимптотике.

 Профиль  
                  
 
 Re: Практические способы представления конечной группы
Сообщение27.07.2024, 11:22 
Аватара пользователя


26/05/12
1700
приходит весна?
dgwuqtj, спасибо! Щас попытаюсь ознакомиться, надеюсь, матан мне будет по зубам.

 Профиль  
                  
 
 Re: Практические способы представления конечной группы
Сообщение27.07.2024, 13:30 
Аватара пользователя


26/05/12
1700
приходит весна?
Интересный метод, надо взять на вооружение. В общих чертах он заключается в том, что для группы G находится подгруппа: $$H\subseteq G:\quad|H|\sim\sqrt{|G|}$$ Произведение в этой подгруппе считается по таблице, размер которой будет линеен по порядку группы G, а произведение элементов исходной группы считается с помощью этой подгруппы и четырёх вспомогательных структур (связанных с разложением элементов группы в правые и левые смежные классы этой подгруппы), размер каждой из которых в точности равен порядку. В результате размер данных получается линеен, а время константное. Там есть исключения и особенности, в которых классификационная теорема фигурирует, я не вникал в них, это детали. Единственное, что я так и не понял, это нужно ли знать внутреннее устройство группы (то есть её подгруппы) перед тем, как получится создать эту структуру данных? Я что-то не заметил алгоритма или практических рекомендаций для нахождения подгруппы H просто по имеющейся таблице умножения группы.

-- 27.07.2024, 13:52 --

Я правильно понимаю, что в композиционном ряде все подгруппы нормальные?

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 7 ] 

Модераторы: Модераторы Математики, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group