【OI】Xor Hash X 算法:基于异或哈希优化的奇覆盖求解算法

问题定义

给定一个 $m \times n$ 的 0-1 矩阵 $A$,奇覆盖问题要求找到行集合 $S \subseteq {1,2,\ldots,m}$,使得对于每一列 $j \in {1,2,\ldots,n}$,有:

$$\bigoplus_{i \in S} A_{i,j} = 1$$

其中 $\oplus$ 表示按位异或运算。换句话说,每个列在选中行中的 1 的个数必须为奇数。

Xor Hash X 算法核心思想

XHX 算法将经典的 X 算法(舞蹈链)与 哈希优化技术 相结合,通过哈希函数在 GF(2) 域上建立高效的剪枝策略。

关键组件

  1. 哈希映射:将行向量映射到哈希值,建立快速匹配机制
  2. 舞蹈链结构:维护 X 算法的高效搜索框架
  3. 哈希剪枝:利用哈希冲突检测提前排除不可能的解分支

数学建模

哈希函数定义

为每列 $j$ 分配一个随机大质数 $p_j$,定义行 $i$ 的哈希值为:

$$H(i) = \bigoplus_{j=1}^n (A_{i,j} \cdot p_j)$$

目标哈希值(全 1 列对应的哈希)为:

$$H_{\text{target}} = \bigoplus_{j=1}^n p_j$$

哈希性质

对于任意行集合 $S$,其组合哈希满足:

$$H(S) = \bigoplus_{i \in S} H(i)$$

当且仅当 $S$ 是奇覆盖解时,$H(S) = H_{\text{target}}$。

算法步骤

阶段一:预处理

  1. 初始化哈希质数:为每列生成唯一的大质数
  2. 计算行哈希:预处理所有行的哈希值
  3. 构建哈希桶:建立哈希值到行索引的映射

阶段二:候选集生成

对于每行 $i$,检查是否存在行 $j$ 使得:
$$H(i) \oplus H(j) = H_{\text{target}}$$

仅保留可能形成解的候选行,显著缩小搜索空间。

阶段三:舞蹈链求解

在候选行集合上构建舞蹈链数据结构,执行标准 X 算法搜索:

  1. 列选择:采用最小剩余值启发式
  2. 行选择:哈希启发式排序,优先选择哈希接近目标的行
  3. 回溯搜索:标准的覆盖/取消覆盖操作

阶段四:解验证

验证找到的解是否满足奇覆盖条件,处理可能的哈希冲突。

复杂度分析

  • 时间复杂度:最坏情况下 $O(2^n)$,但哈希剪枝在实践中显著改善
  • 空间复杂度:$O(mn)$ 存储矩阵和舞蹈链结构
  • 哈希开销:$O(m+n)$ 的额外空间

C++ 实现

Xor Hash X
#include <vector>
#include <unordered_map>
#include <random>
#include <algorithm>
#include <chrono>
using namespace std;

class XHXSolver {
private:
    // 舞蹈链节点结构
    struct Node {
        int row, col;
        Node *up, *down, *left, *right;
        Node(int r, int c) : row(r), col(c), 
                           up(this), down(this), left(this), right(this) {}
    };

    vector<vector<int>> matrix;          // 原始01矩阵
    vector<long long> hashPrimes;        // 每列对应的哈希质数
    vector<long long> rowHashes;         // 每行的哈希值
    unordered_map<long long, vector<int>> hashBuckets;  // 哈希桶
    vector<Node*> columnHeaders;         // 列头节点
    vector<int> solution;                // 当前解
    int rows, cols;                      // 矩阵维度
    long long targetHash;                // 目标哈希值

    // 初始化随机哈希质数
    void initHashPrimes() {
        mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
        uniform_int_distribution<long long> dist(1e9, 1e18);

        hashPrimes.resize(cols);
        for (int j = 0; j < cols; j++) {
            hashPrimes[j] = dist(rng);
            // 确保质数为奇数,避免哈希冲突
            hashPrimes[j] |= 1;
        }

        // 计算目标哈希:全1列对应的哈希
        targetHash = 0;
        for (long long prime : hashPrimes) {
            targetHash ^= prime;
        }
    }

    // 计算单行哈希值
    long long computeRowHash(int rowIdx) {
        long long hash = 0;
        for (int j = 0; j < cols; j++) {
            if (matrix[rowIdx][j] == 1) {
                hash ^= hashPrimes[j];
            }
        }
        return hash;
    }

    // 构建哈希桶
    void buildHashBuckets() {
        rowHashes.resize(rows);
        for (int i = 0; i < rows; i++) {
            rowHashes[i] = computeRowHash(i);
            hashBuckets[rowHashes[i]].push_back(i);
        }
    }

    // 哈希剪枝:判断行是否可能出现在解中
    bool isCandidateRow(int rowIdx) {
        long long currentHash = rowHashes[rowIdx];
        long long requiredHash = targetHash ^ currentHash;

        // 检查是否存在其他行能与当前行组合形成目标哈希
        return hashBuckets.find(requiredHash) != hashBuckets.end();
    }

    // 构建舞蹈链数据结构(仅包含候选行)
    void buildDancingLinks() {
        // 初始化列头节点(环形双向链表)
        columnHeaders.resize(cols);
        Node* header = new Node(-1, -1);  // 主头节点
        columnHeaders[0] = header;

        for (int j = 0; j < cols; j++) {
            Node* colNode = new Node(-1, j);
            columnHeaders[j] = colNode;

            // 链接到列链表
            if (j > 0) {
                colNode->left = columnHeaders[j-1];
                columnHeaders[j-1]->right = colNode;
            }
        }
        // 完成列链表的环形连接
        columnHeaders[cols-1]->right = header;
        header->left = columnHeaders[cols-1];

        // 添加候选行到舞蹈链
        for (int i = 0; i < rows; i++) {
            if (!isCandidateRow(i)) continue;

            Node* rowHeader = nullptr;
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] == 1) {
                    Node* newNode = new Node(i, j);

                    // 垂直链接:插入到列中
                    newNode->up = columnHeaders[j]->up;
                    newNode->down = columnHeaders[j];
                    columnHeaders[j]->up->down = newNode;
                    columnHeaders[j]->up = newNode;

                    // 水平链接:建立行链表
                    if (!rowHeader) {
                        rowHeader = newNode;
                    } else {
                        newNode->left = rowHeader->left;
                        newNode->right = rowHeader;
                        rowHeader->left->right = newNode;
                        rowHeader->left = newNode;
                    }
                }
            }
        }
    }

    // 覆盖列操作
    void coverColumn(Node* col) {
        col->right->left = col->left;
        col->left->right = col->right;

        for (Node* row = col->down; row != col; row = row->down) {
            for (Node* node = row->right; node != row; node = node->right) {
                node->down->up = node->up;
                node->up->down = node->down;
            }
        }
    }

    // 取消覆盖列操作
    void uncoverColumn(Node* col) {
        for (Node* row = col->up; row != col; row = row->up) {
            for (Node* node = row->left; node != row; node = node->left) {
                node->down->up = node;
                node->up->down = node;
            }
        }

        col->right->left = col;
        col->left->right = col;
    }

    // 哈希启发式评分函数
    int hashHeuristic(int rowIdx) {
        long long currentHash = 0;
        for (int r : solution) {
            currentHash ^= rowHashes[r];
        }
        currentHash ^= rowHashes[rowIdx];

        // 计算当前哈希与目标哈希的汉明距离
        long long diff = currentHash ^ targetHash;
        return __builtin_popcountll(diff);
    }

    // 递归求解函数
    bool solveRecursive(int depth) {
        Node* header = columnHeaders[0];
        if (header->right == header) {
            // 找到解,验证奇覆盖条件
            return verifyOddCover();
        }

        // 选择列(最小剩余值启发式)
        Node* selectedCol = header->right;
        int minSize = INT_MAX;
        for (Node* col = header->right; col != header; col = col->right) {
            int count = 0;
            for (Node* node = col->down; node != col; node = node->down) count++;
            if (count > 0 && count < minSize) {
                minSize = count;
                selectedCol = col;
            }
        }

        if (minSize == 0) return false;

        coverColumn(selectedCol);

        // 按哈希启发式排序候选行
        vector<Node*> candidates;
        for (Node* row = selectedCol->down; row != selectedCol; row = row->down) {
            candidates.push_back(row);
        }
        sort(candidates.begin(), candidates.end(), [&](Node* a, Node* b) {
            return hashHeuristic(a->row) < hashHeuristic(b->row);
        });

        for (Node* row : candidates) {
            solution.push_back(row->row);

            // 覆盖该行涉及的所有列
            for (Node* node = row->right; node != row; node = node->right) {
                coverColumn(columnHeaders[node->col]);
            }

            if (solveRecursive(depth + 1)) return true;

            // 回溯:取消覆盖
            for (Node* node = row->left; node != row; node = node->left) {
                uncoverColumn(columnHeaders[node->col]);
            }

            solution.pop_back();
        }

        uncoverColumn(selectedCol);
        return false;
    }

    // 验证解的正确性
    bool verifyOddCover() {
        vector<int> colSum(cols, 0);
        for (int rowIdx : solution) {
            for (int j = 0; j < cols; j++) {
                colSum[j] ^= matrix[rowIdx][j];
            }
        }

        for (int sum : colSum) {
            if (sum != 1) return false;
        }
        return true;
    }

public:
    XHXSolver(const vector<vector<int>>& mat) : matrix(mat) {
        rows = mat.size();
        cols = mat[0].size();
    }

    // 主求解函数
    vector<int> solve() {
        // 初始化阶段
        initHashPrimes();
        buildHashBuckets();
        buildDancingLinks();

        // 求解阶段
        solution.clear();
        if (solveRecursive(0)) {
            return solution;
        }
        return {};
    }
};

算法特色

  1. 哈希剪枝:利用哈希冲突检测大幅减少搜索空间
  2. 启发式搜索:哈希距离指导行选择顺序
  3. 保持完备性:虽然剪枝,但通过验证保证解的正确性
  4. 易于实现:基于标准 X 算法框架,易于理解和扩展

应用场景

  • 组合数学中的奇覆盖问题
  • GF(2) 域上的线性方程组求解
  • 密码学中的相关密钥分析
  • 编码理论中的奇偶校验矩阵分析

后言

前言放在最后就变成了后言。

在对 Deepseek 长达 0.5 h 的调教后让它帮我们生成出了这篇文章,我几乎没有修改。本文纯属整活,算法纯属乱搞,文中的代码我压根没拿出来运行过,但是算法名字的缩写是 XHX 就对了。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇