К оглавлению

Подстановки

С.И.Хашин


subs.h - заголовочный файл
subs.cpp - реализация.

Мы рассматриваем подстановки на множестве из n элементов, а именно на множестве целых чисел от 0 до n-1.
Так как 21! > 264, то мы в основном будем рассматривать небольшие подстановки, но многие функции будут работать и при n вплоть до 256.

Подстановка из n элементов представляет собой байтовый массив из целых чисел от 0 до n-1, которые все различны. Поэтому базовым классом для подстановок является байтовый массив:

class barray{           // байтовый массив
protected:
    int n;              // number of elements
    BYTE *a;    // elements
public:
    ...
    void setN(int n_);                  // setSize
    int  getN() { return n;}
    BYTE &operator[] (int i);           // обращение к элементу массива (с проверкой индексов)
    void setA(BYTE *a1);                // copy from array
    void copyFrom(barray &s);           // this := copyFrom(s)
    void set(BYTE v0, BYTE v1);
    void set(BYTE v0, BYTE v1, BYTE v2);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3, BYTE v4);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3, BYTE v4, BYTE v5);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3, BYTE v4, BYTE v5, BYTE v6);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3, BYTE v4, BYTE v5, BYTE v6, BYTE v7);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3, BYTE v4, BYTE v5, BYTE v6, BYTE v7, BYTE v8);
    void set(BYTE v0, BYTE v1, BYTE v2, BYTE v3, BYTE v4, BYTE v5, BYTE v6, BYTE v7, BYTE v8, BYTE v9);

    char *toString();
    bool isEqual(barray &s);            // this == s?
    bool operator==(barray &s);
    bool isSubs();                      // all elements are different
    void sort();                        // (22031) -> (32210)
};

Примеры работы:

    barray ba;
    ba.set(2, 2, 0, 3, 1);
    printf("  ini: %s\n", ba.toString());
    ba.sort();
    printf("  srt: %s\n", ba.toString());

Результат:

  ini: 22031
  srt: 32210

Класс подстановок является наследником байтового массива:

class subs: public barray{
public:
    // constructors/destructor
    subs(int n_ = 0);

    char *toStringS();                  // разложение на циклы
    void setId();                       // Единичная подстановка
    void mult(subs &s1, subs &s2);      // Умножение подстановок
    void div(subs &s1, subs &s2);       // this := s1/s2
    void revers(subs &s);               // Обратная подстановка
    bool isCommut(subs &s);             // Будет ли s*this == this*s ?
    bool isH();                         // Подстановка без неподвижных точек?

    unsigned index();                   // index of substitution 
                                        // можно применять только при n<=12 (13!>2^32)
    unsigned long long index64();       // index of substitution
                                        // можно применять только при n<=20 (21!>2^64)
    void setByIndex(unsigned ind);      // this := substitution number ind,          n<=12
    void setByIndex64(unsigned long long ind);  // this := substitution number ind,  n<=20

    bool isE();                         // Будет ли подстановка единичной
    bool isSim(subs &s);                // this/s без неподвижных точек?
    int  fixCount();                    // количество неподвижных точек

    void stype(barray &ba);             // ba:=тип разложения на циклы подстановки this,
                                        // например при this=(01)(2)(3456)(78)(9),
                                        // получим ba=(422110000)
    int  nKind();                       // количество типов подстановок данного размера
    int  itype();                       // номер типа подстановки
    void set_min(int k);                // минимальная подстановка типа k

    void conj(subs &r, subs &t);        // this := r*t/r
    bool Find_conj(subs &r, subs &t);   // this := s such, that: t = s*r/s, 
};

Опишем некоторые методы более подробно.
Все подстановки пронумерованы от 0 до n!-1. Узнать номер (индекс) подстановки позволяет функции index() и index64().

Замечание. Индексы подстановок упорядочены лексикографически.
То есть, если индекс s1 меньше индекса s2, то s1 лексикографически меньше s2.

Обратную операцию – построить подстановку с данным номером (индексом) выполняют фунцкии setByIndex() и setByIndex64().

    subs s(18);
    unsigned long long ind, in2;
    ind = 3591172723459074ULL;
    s.setByIndex64(ind);
    in2 = s.index64();
    printf("%20llu %20llu %s\n", ind, in2, s.toStringS());

Получаем:

    3591172723459074     3591172723459074 (hbdf50a)(34862c)(9g)
Ещё пример:
    int N = 3;
    subs s(N);
    for (unsigned i = 0; i < factorial(N); i++) {
        s.setByIndex(i);
        printf("%2d: {%s} = ", i, s.toString());
        printf("%s\n", s.toStringS());
    }

Получаем:

 0: {012} = Id
 1: {021} = (12)
 2: {102} = (01)
 3: {120} = (012)
 4: {201} = (021)
 5: {210} = (02)

Напомним, что числа – элементы подстановки, большие 9 отображаются маленькими латинскими буквами: a,b,c,... и циклы длины 1, в данном случае (1) и (e) не показываются.

Умножение и деление.

    t3.div(t1,t2);
    t4.mult(t3,t2);
В результате получаем t4==t1.

В моих вычислениях мне часто требовалось проверить для двух подстановок σ и τ будет ли подстановка σ τ-1 иметь неподвижные точки? Это можно выяснить с помощью функции

    bool isSim(subs &s);                // this/s без неподвижных точек?

Типом разложения на циклы подстановки будем называть список с количеством циклов каждой длины.

    subs s;
    ...
    barray ba;
    s.stype(ba);

Например, если подстановка имеет разложение на циклы (01)(2)(3456)(78)(9), то её типом разложения будет ba=(422110000), то есть цикл длины 4, два цикла длины 2 и два цикла длины 1. Сумма элементов массива ba обязательно будет равна n.

Различных типов подстановов без неподвижных точек при данной длине немного:

         N                   : 2,3,4,5,6,7,8,9,10, ...
 количество типов подстановок: 1,1,2,2,4,4,7,8,12, ...

Количество типов подстановок без неподвижных точек возвращается функцией

    int  nKind();                       // количество типов подстановок данного размера

Перенумеруем все возможные типы подстановок (без неподвижных точек). Номер типа данной подстановки можно узнать функцией

    int  itype();                       // номер типа подстановки
Среди всех подстановок данного типа выделим одну – минимальную в лексикографическом порядке. Её можно получить функцией
    void set_min(int k);                // минимальная подстановка типа k

Следующие функции связаны с сопряженными подстановками:

    void conj(subs &r, subs &t);        // this := r*t/r
    bool Find_conj(subs &r, subs &t);   // this := s such, that: t = s*r/s, 
Функция conj просто строит сопряжённую подстановку, а функция Find_conj для данной пары подстановок r и t ищет сопрягающую подстановку. Если такой не существует, то есть подстановки не сопряжены, возвращается false.

Пример использования:

    const int N = 5;
    subs s(N);
    s.setByIndex(7);
    printf("s =<%s> = ", s.toString());
    printf("%s\n", s.toStringS());
    subs r(N), t(N), tr(N), g(N);
    r.setByIndex(16);
    if (t.Find_conj(s, r)) {
        printf("r =<%s> = ", r.toString());
        printf("%s\n", r.toStringS());
        printf("t =<%s> = ", t.toString());
        printf("%s\n", t.toStringS());

        tr.revers(t);       // tr = 1/t
        g.mult(s, tr);      // g = s/t
        g.mult(t, g);       // g = t*s/t
        if (g.isEqual(r)) printf("Ok 1!\n");
        // проверка с помощью функции conj:
        g.conj(t, s);       // g = t*s/t 
        if (g.isEqual(r)) printf("Ok 2!\n");
    }

Получаем:

s =<02143> = (12)(34)
r =<03412> = (13)(24)
t =<01324> = (23)
Ok 1!
Ok 2!

К оглавлению


free counters