0-1矩阵列互斥问题——回溯法 Python实现

三、 0-1 矩阵的列集互斥问题。给定一个 m × n m \times n m×n 的 0-1 矩阵 A \mathrm{A} A 。定义列互斥为: 对于矩阵 A A A 中的任意两列 i i i 和 j j j, 如果在对应的每一行上, i i i 和 j j j 不存在同时为 1 的情况, 则称列 i \mathrm{i} i 和 j \mathrm{j} j 互斥。定义列集互斥为: 设 S 1 \mathrm{S} 1 S1 和 S 2 \mathrm{S} 2 S2 为矩阵 A \mathrm{A} A 中的列的集合, S 1 S1 S1 和 S 2 S2 S2 之间没有交集 (即, 不允许 A \mathrm{A} A 中的某列既属于 S 1 \mathrm{S} 1 S1 又属于 S 2 \mathrm{S} 2 S2 ), 如果在对应的每一行上, S 1 \mathrm{S} 1 S1 中的任意一列和 S 2 \mathrm{S} 2 S2 中的任意一列不存在同时为 1 的情况, 则称列集 S 1 \mathrm{S} 1 S1 和 S 2 S2 S2 互斥。设计一个算法, 求出 A \mathrm{A} A 上的一组 S 1 \mathrm{S} 1 S1 和 S 2 \mathrm{S} 2 S2 ,使得 S 1 \mathrm{S} 1 S1 和 S 2 \mathrm{S} 2 S2 包含的列的个数为最多

S 1 S1 S1和 S 2 S2 S2非空。

思路

适当的利用剪枝函数限界函数以减少搜索的空间:

  • 剪枝函数:即题目要求,只有互斥才能进入下一层。
  • 限界函数:目前A和B矩阵的列数加上剩余的列数已经小于当前最优解,放弃向下搜索。

使用Py编写这个算法的时候,可以使用numpy库的数据,加快我们运行的速度,同时可以减少很多循环遍历数组的冗余代码。

为了节省时间,我们在开始计算前,先把 n n n列向量的互斥关系都计算出来,保存在一个 n × n n \times n n×n的矩阵内。

python 复制代码
import numpy as np
import matplotlib.pyplot as plt


class Matrix:
    def __init__(self, array):
        self.array = array
        rows, columns = array.shape
        self.belong = np.zeros(columns, dtype=int)  # 1属于A, 2属于B
        self.solve = np.zeros(columns, dtype=int)   # 最终解
        self.best = 0  # 最佳列数
        self.sumA = 0  # 记录当前A列数
        self.sumB = 0  # 记录当前B列数
        self.judge = np.ones((columns, columns), dtype=int)  # 减少时间的关键,判断两列互斥
        self.diff = 9999

        # 先计算出列与列之间的互斥关系1代表不互斥,0代表互斥
        for i in range(columns):
            self.judge[i, i] = 0
            for j in range(i):
                for k in range(rows):
                    if array[k, i] == 1 and array[k, j] == 1:
                        self.judge[i, j] = 0
                        self.judge[j, i] = 0
                        break


    # j列能否归入A
    def could_be_a(self, j):
        for i in range(j):
            if self.belong[i] == 2 and self.judge[i, j] == 0:
                return False
        return True

    # j列能否归入B
    def could_be_b(self, j):
        for i in range(j):
            if self.belong[i] == 1 and self.judge[i, j] == 0:
                return False
        return True

    def biggest_divide(self, i):

        columns = self.array.shape[1]

        if i >= columns:
            if self.sumA + self.sumB > self.best and self.sumA and self.sumB and np.abs(self.sumA-self.sumB) < self.diff:
                self.best = self.sumA + self.sumB
                self.solve = self.belong.copy()
                self.diff = np.abs(self.sumA-self.sumB)
            return

        if self.could_be_a(i):
            self.belong[i] = 1
            self.sumA += 1
            self.biggest_divide(i + 1)
            self.belong[i] = 0
            self.sumA -= 1

        if self.could_be_b(i):
            self.belong[i] = 2
            self.sumB += 1
            self.biggest_divide(i + 1)
            self.belong[i] = 0
            self.sumB -= 1

        if self.sumA + self.sumB + columns - i >= self.best:
            self.biggest_divide(i + 1)

    def show(self):
        a_indices = np.where(self.solve == 1)[0]
        b_indices = np.where(self.solve == 2)[0]

        print("A:", a_indices)
        print("B:", b_indices)
        
        color_array = self.array.copy()
        color_array[:, a_indices] *= 10
        color_array[:, b_indices] *= 7
        
        plt.matshow(color_array, cmap=plt.cm.Reds)
        plt.show()


row = 50
colume = 20
array = np.random.choice([0, 1], size=(row, colume), p=[0.8, 0.2])
test = Matrix(array)
test.biggest_divide(0)
test.show()

使用show来可视化最终结果,如果这里只取列数合最大,一般A列都比较多,如果要好看的结果可以限制A列和B列之间距离越小越好,多设置一个diff参数,当列数合相同时,保存A列与B列相差较小的结果。

在 m m m=50, n n n=20下,1填充率为20%,随机填充下的互斥结果,深红色为A集合,鲜红色为B集合。

shell 复制代码
A: [ 0 5 16 17 19]
B: [ 2 7]

时间复杂度分析

  1. 对于每一列,回溯算法会考虑三种可能性:将其归入 A 部分或归入 B 部分或者不归入。
  2. 对于每一列的三种可能性,又会递归考虑下一列的三种可能性,以此类推。
  3. 这样的递归结构导致了指数级的搜索树。
  4. 在最坏情况下,需要考虑的列数等于矩阵的列数,因此有 3 n 3^n 3n种可能性,其中 n n n 是列数。
相关推荐
B站_计算机毕业设计之家3 分钟前
机器学习实战项目:Python+Flask 汽车销量分析可视化系统(requests爬车主之家+可视化 源码+文档)✅
人工智能·python·机器学习·数据分析·flask·汽车·可视化
SandySY24 分钟前
品三国谈人性
算法·架构
羊羊小栈26 分钟前
基于「多模态大模型 + BGE向量检索增强RAG」的航空维修智能问答系统(vue+flask+AI算法)
vue.js·人工智能·python·语言模型·flask·毕业设计
星期天要睡觉30 分钟前
模型部署——Flask 部署 PyTorch 模型
pytorch·python·flask
小欣加油32 分钟前
leetcode 62 不同路径
c++·算法·leetcode·职场和发展
夏鹏今天学习了吗33 分钟前
【LeetCode热题100(38/100)】翻转二叉树
算法·leetcode·职场和发展
夏鹏今天学习了吗33 分钟前
【LeetCode热题100(36/100)】二叉树的中序遍历
算法·leetcode·职场和发展
weixin_4569042736 分钟前
SHAP可视化代码详细讲解
python
DTS小夏37 分钟前
算法社Python基础入门面试题库(新手版·含答案)
python·算法·面试
刘一哥GIS1 小时前
Windows环境搭建:PostGreSQL+PostGIS安装教程
数据库·python·arcgis·postgresql·postgis