Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Uniform Distribution Properties in Randomized Permutation Subgroup Algorithms

Tech 1

When analyzing subgroups of the symmetric group generated by a set of permutations, a crucial observation is the uniform distribution property across reachable configurations. Consider a subgroup $H \leq S_n$ generated by permutations $\pi_1, \ldots, \pi_k$. For any fixed index $i$, if the $i$-th position takes values $v_1, \ldots, v_m$ across elements of $H$, each value appears equally often. This extends to tuples of positions: for indices $i_1, \ldots, i_t$, the distribution of $(h(i_1), \ldots, h(i_t))$ is uniform over all attainable tuples in $H$.

To establish this, model the group action as a graph. For tuples of length $m$, vertices represent $m$-tuples of distinct indices. Directed edges correspond to generator applications: from $(a_1, \ldots, a_m)$ to $(\pi(a_1), \ldots, \pi(a_m))$ labeled with $\pi$. Since $\pi^{-1} \in H$, edges are effectively bidirectional. Fix a root tuple $r$ and consider a spanning tree. Each vertex $v$ corresponds to a unique path $r \to v$, yielding a group element $g_v$. The mapping $g \mapsto g \circ g_v^{-1}$ establishes a bijection between paths to $v$ and paths to $r$, proving uniformity.

This property enables efficient computation of average inversion counts. For the problem of computing expected inversions over the generated subgroup, construct a graph on ordered pairs $(i,j)$ with $i \neq j$. For each generator $\pi$, add undirected edges between $(i,j)$ and $(\pi(i), \pi(j))$. Each connected component $C$ contains some number $a$ of pairs with $i < j$ and $b$ with $i > j$. By uniformity, the probability of an inversion for a random element of $H$ restricted to pairs in $C$ is $\frac{b}{a+b}$, contributing $\frac{ab}{a+b}$ to the total expectation.

Direct computation uses $O(n^3)$ time via union-find on $O(n^2)$ pair nodes. However, the generator set can be reduced randomly. Sampling $O(\log n)$ random subsets of the original generators, composing them arbitrarily, produces a small generating set with high probability. This yields an $O(n^2 \log n)$ algorithm:

const int MAXN = 505;
const int MOD = 998244353;

int dim, genCnt;
int generators[MAXN][MAXN];
int perm[MAXN], temp[MAXN];
int parent[MAXN * MAXN], rev[MAXN * MAXN];
int lessCnt[MAXN * MAXN], greatCnt[MAXN * MAXN];

int encode(int x, int y) { return (x - 1) * dim + y; }

int find(int x) {
    return parent[x] == x ? x : parent[x] = find(parent[x]);
}

int main() {
    dim = read(); genCnt = read();
    for (int g = 1; g <= genCnt; ++g)
        for (int i = 1; i <= dim; ++i)
            generators[g][i] = read();
    
    for (int i = 1; i <= dim * dim; ++i) parent[i] = i;
    
    for (int trial = 0; trial < LOG; ++trial) {
        for (int i = 1; i <= dim; ++i) perm[i] = i;
        for (int g = 1; g <= genCnt; ++g) {
            if (rng() & 1) {
                for (int i = 1; i <= dim; ++i) temp[perm[i]] = generators[g][i];
                for (int i = 1; i <= dim; ++i) perm[i] = temp[i];
            }
        }
        for (int i = 1; i <= dim; ++i) {
            for (int j = 1; j <= dim; ++j) {
                if (i == j) continue;
                parent[find(encode(i, j))] = find(encode(perm[i], perm[j]));
            }
        }
    }
    
    for (int i = 1; i <= dim; ++i)
        for (int j = 1; j <= dim; ++j)
            if (i != j) {
                if (i < j) ++lessCnt[find(encode(i, j))];
                else ++greatCnt[find(encode(i, j))];
            }
    
    rev[1] = 1;
    for (int i = 2; i <= dim * dim; ++i)
        rev[i] = (long long)rev[MOD % i] * (MOD - MOD / i) % MOD;
    
    int ans = 0;
    for (int i = 1; i <= dim * dim; ++i)
        if (parent[i] == i && lessCnt[i] && greatCnt[i])
            ans = (ans + (long long)lessCnt[i] * greatCnt[i] % MOD * rev[lessCnt[i] + greatCnt[i]]) % MOD;
    
    printf("%d\n", ans);
    return 0;
}

For computing the subgroup order (the size of $H$), apply a recursive dimension reduction. The number of distinct values at position $n$ equals the size of the orbit of $n$, computable via the graph method above. To count stabilizers (elements fixing $n$), construct new generators from cycles involving $n$: for each edge $(u,v)$ in the orbit graph, the permutation mapping $n \to u \to v \to n$ stabilizes $n$ when appropriately conjugated.

Specifically, build a BFS tree from node $n$ in the action graph on single points. For each tree edge to node $u$ via generator $g$, and for each outgoing edge from $u$ to $v$ via generator $h$, the element $\text{path}[u] \cdot g \cdot h \cdot \text{path}[v]^{-1}$ fixes $n$, where $\text{path}[x]$ denotes the tree path from $n$ to $x$. This generates the stabilizer subgroup $H_n$. Randomly reducing these $O(n \cdot \text{genCnt})$ elements to $O(n)$ generators allows recursion on $n-1$ dimnesions.

The recursive algorithm operates in $O(n^5)$ time:

struct Permutation {
    int img[MAXN];
    void identity() { for (int i = 1; i <= curSize; ++i) img[i] = i; }
    friend Permutation compose(const Permutation& a, const Permutation& b) {
        Permutation c;
        for (int i = 1; i <= curSize; ++i) c.img[i] = b.img[a.img[i]];
        return c;
    }
    Permutation invert() {
        Permutation inv;
        for (int i = 1; i <= curSize; ++i) inv.img[img[i]] = i;
        return inv;
    }
};

Permutation gens[MAXN], reduced[MAXN];
Permutation treePath[MAXN << 1];
vector<pair<int,int>> adj[MAXN];
bool visited[MAXN];
int curSize, genCount;
long long result[20];
int resLen;
long long orbitSize;

void dfs(int u, Permutation cur) {
    treePath[u] = cur;
    visited[u] = true;
    ++orbitSize;
    for (auto [v, gid] : adj[u]) {
        if (visited[v]) continue;
        dfs(v, compose(cur, gens[gid]));
    }
}

void reduceGenerators() {
    for (int i = 1; i <= curSize; ++i) adj[i].clear();
    for (int g = 1; g <= genCount; ++g)
        for (int i = 1; i <= curSize; ++i)
            adj[i].push_back({gens[g].img[i], g});
    
    Permutation id;
    id.identity();
    dfs(curSize, id);
    
    for (int i = 1; i <= curSize; ++i) {
        treePath[i + curSize] = treePath[i].invert();
        adj[i].clear();
    }
    
    for (int t = 1; t <= BOUND; ++t) {
        reduced[t].identity();
        for (int g = 1; g <= genCount; ++g)
            for (int i = 1; i <= curSize; ++i)
                if (visited[i] && (rng() & 1))
                    reduced[t] = compose(reduced[t], compose(compose(treePath[i], gens[g]), treePath[gens[g].img[i] + curSize]));
    }
    
    for (int i = 1; i <= curSize; ++i) visited[i] = false;
    genCount = BOUND;
    for (int i = 1; i <= genCount; ++i) gens[i] = reduced[i];
    
    --curSize;
    long long carry = 0;
    for (int i = 0; i <= resLen; ++i) {
        result[i] = result[i] * orbitSize + carry;
        carry = result[i] / BASE;
        result[i] %= BASE;
    }
    if (carry) result[++resLen] = carry;
    orbitSize = 0;
}

int main() {
    curSize = read(); genCount = read();
    for (int i = 1; i <= genCount; ++i)
        for (int j = 1; j <= curSize; ++j)
            gens[i].img[j] = read();
    
    result[resLen = 0] = 1;
    while (curSize > 0) reduceGenerators();
    
    printf("%lld", result[resLen]);
    for (int i = resLen - 1; i >= 0; --i)
        printf("%016lld", result[i]);
    puts("");
    return 0;
}

The rank of a finite group—the minimum size of a generating set—can reach $\Theta(n)$, necessitating retention of $O(n)$ generators for order computation. However, for connectivity problems like inversion counting, $O(\log n)$ random generators suffice to connect the graph with high probability, explaining the efficacy of the randomized reduction.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.