*python3__leecode/0240. 搜索二维矩阵 II

本文详细介绍了如何解决LeetCode上的搜索二维矩阵II问题,提供了四种不同的解题方法,包括暴力法、二分法搜索、搜索空间的缩减以及优化的搜索策略。每种方法都分析了其时间复杂度和空间复杂度,并通过实例解释了算法的运行过程。通过这些方法,可以更高效地在有序矩阵中查找目标值。

一、刷题内容

原题链接

https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

内容描述

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

示例 1:
在这里插入图片描述
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

示例 2:
在这里插入图片描述
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false

提示:
m == matrix.length
n == matrix[i].length
1 <= n, m <= 300
-10^9 <= matix[i][j] <= 10^9
每行的所有元素从左到右升序排列
每列的所有元素从上到下升序排列
-10^9 <= target <= 10^9

二、解题方法

1.方法一:暴力法

对于每一行我们可以像搜索未排序的一维数组——通过检查每个元素来判断是否有目标值。

这个算法并没有做到聪明的事情。我们循环数组,依次检查每个元素。如果,我们找到了,我们返回 true。否则,对于搜索到末尾都没有返回的循环,我们返回 false。此算法在所有情况下都是正确的答案,因为我们耗尽了整个搜索空间。

class Solution:
    def searchMatrix(self, matrix, target):
        for row in matrix:
            if target in row:
                return True
        
        return False

时间复杂度:O(mn)。因为我们在 n×m 矩阵上操作,总的时间复杂度为矩阵的大小
空间复杂度:O(1),暴力法分配的额外空间不超过少量指针,因此内存占用是恒定的。

2.方法二:二分法搜索

矩阵已经排过序,就需要使用二分法搜索以加快我们的算法。

算法:
首先,我们确保矩阵不为空。那么,如果我们迭代矩阵对角线,从当前元素对列和行搜索,我们可以保持从当前 (row,col) 对开始的行和列为已排序。 因此,我们总是可以二分搜索这些行和列切片。我们以如下逻辑的方式进行 : 在对角线上迭代,二分搜索行和列,直到对角线的迭代元素用完为止(意味着我们可以返回 false )或者找到目标(意味着我们可以返回 true )。binary search 函数的工作原理和普通的二分搜索一样,但需要同时搜索二维数组的行和列。

class Solution:
    def binary_search(self, matrix, target, start, vertical):
        lo = start
        hi = len(matrix[0])-1 if vertical else len(matrix)-1

        while hi >= lo:
            mid = (lo + hi)//2
            if vertical: # searching a column
                if matrix[start][mid] < target:
                    lo = mid + 1
                elif matrix[start][mid] > target:
                    hi = mid - 1
                else:
                    return True
            else: # searching a row
                if matrix[mid][start] < target:
                    lo = mid + 1
                elif matrix[mid][start] > target:
                    hi = mid - 1
                else:
                    return True
        
        return False

    def searchMatrix(self, matrix, target):
        # an empty matrix obviously does not contain `target`
        if not matrix:
            return False

        # iterate over matrix diagonals starting in bottom left.
        for i in range(min(len(matrix), len(matrix[0]))):
            vertical_found = self.binary_search(matrix, target, i, True)
            horizontal_found = self.binary_search(matrix, target, i, False)
            if vertical_found or horizontal_found:
                return True
        
        return False

3.方法三:搜索空间的缩减

我们可以将已排序的二维矩阵划分为四个子矩阵,其中两个可能包含目标,其中两个肯定不包含。

算法:
由于该算法是递归操作的,因此可以通过它的基本情况和递归情况的正确性来判断它的正确性。

基本情况 :
对于已排序的二维数组,有两种方法可以确定一个任意元素目标是否可以用常数时间判断。第一,如果数组的区域为零,则它不包含元素,因此不能包含目标。其次,如果目标小于数组的最小值或大于数组的最大值,那么矩阵肯定不包含目标值。

class Solution:
    def searchMatrix(self, matrix, target):
        # an empty matrix obviously does not contain `target`
        if not matrix:
            return False

        def search_rec(left, up, right, down):
            # this submatrix has no height or no width.
            if left > right or up > down:
                return False
            # `target` is already larger than the largest element or smaller
            # than the smallest element in this submatrix.
            elif target < matrix[up][left] or target > matrix[down][right]:
                return False

            mid = left + (right-left)//2

            # Locate `row` such that matrix[row-1][mid] < target < matrix[row][mid]
            row = up
            while row <= down and matrix[row][mid] <= target:
                if matrix[row][mid] == target:
                    return True
                row += 1
            
            return search_rec(left, row, mid-1, down) or search_rec(mid+1, up, right, row-1)

        return search_rec(0, 0, len(matrix[0])-1, len(matrix)-1)

4.方法四:

因为矩阵的行和列是排序的(分别从左到右和从上到下),所以在查看任何特定值时,我们可以修剪O(m)O(m)或O(n)O(n)元素。

算法:
首先,我们初始化一个指向矩阵左下角的 (row,col) 指针。然后,直到找到目标并返回 true(或者指针指向矩阵维度之外的 (row,col) 为止,我们执行以下操作:如果当前指向的值大于目标值,则可以 “向上” 移动一行。 否则,如果当前指向的值小于目标值,则可以移动一列。不难理解为什么这样做永远不会删减正确的答案;因为行是从左到右排序的,所以我们知道当前值右侧的每个值都较大。 因此,如果当前值已经大于目标值,我们知道它右边的每个值会比较大。也可以对列进行非常类似的论证,因此这种搜索方式将始终在矩阵中找到目标(如果存在)。

在下面的动画中查看算法的一些示例运行:

class Solution:
    def searchMatrix(self, matrix, target):
        # an empty matrix obviously does not contain `target` (make this check
        # because we want to cache `width` for efficiency's sake)
        if len(matrix) == 0 or len(matrix[0]) == 0:
            return False

        # cache these, as they won't change.
        height = len(matrix)
        width = len(matrix[0])

        # start our "pointer" in the bottom-left
        row = height-1
        col = 0

        while col < width and row >= 0:
            if matrix[row][col] > target:
                row -= 1
            elif matrix[row][col] < target:
                col += 1
            else: # found it
                return True
        
        return False

时间复杂度:O(n+m)。
时间复杂度分析的关键是注意到在每次迭代(我们不返回 true)时,行或列都会精确地递减/递增一次。由于行只能减少 m 次,而列只能增加 n 次,因此在导致 while 循环终止之前,循环不能运行超过 n+m 次。因为所有其他的工作都是常数,所以总的时间复杂度在矩阵维数之和中是线性的。
空间复杂度:O(1),因为这种方法只处理几个指针,所以它的内存占用是恒定的。

File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/infer/autoguide/guides.py", line 510, in forward self._setup_prototype(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/infer/autoguide/guides.py", line 460, in _setup_prototype super()._setup_prototype(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/infer/autoguide/guides.py", line 157, in _setup_prototype self.prototype_trace = poutine.block(poutine.trace(model).get_trace)( File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/poutine/messenger.py", line 32, in _context_wrap return fn(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/poutine/trace_messenger.py", line 216, in get_trace self(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/poutine/trace_messenger.py", line 198, in __call__ raise exc from e File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/poutine/trace_messenger.py", line 191, in __call__ ret = self.fn(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/poutine/messenger.py", line 32, in _context_wrap return fn(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/poutine/messenger.py", line 32, in _context_wrap return fn(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/pyro/nn/module.py", line 520, in __call__ result = super().__call__(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1751, in _wrapped_call_impl return self._call_impl(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1762, in _call_impl return forward_call(*args, **kwargs) File "/home/zhengyeyang/anaconda3/envs/cell2loc_env/lib/python3.10/site-packages/cell2location/models/reference/_reference_module.py", line 227, in forward detection_y_c = obs2sample @ detection_mean_y_e # (self.n_obs, 1) RuntimeError: expected mat1 and mat2 to have the same dtype, but got: long int != float Trace Shapes: Param Sites: Sample Sites: per_cluster_mu_fg dist | 34 10237 value | 34 10237 detection_tech_gene_tg dist | 2 10237 value | 2 10237 detection_mean_y_e dist | 23 1 value | 23 1
最新发布
07-16
<think>我们遇到了一个RuntimeError,提示在矩阵乘法中两个矩阵的数据类型(dtype)不匹配。具体来说,在cell2location模型的前向传播过程中,进行矩阵乘法操作时,输入的两个矩阵mat1和mat2必须具有相同的数据类型,但当前它们的类型不同。 解决这个问题的关键在于确保参与矩阵乘法的两个张量具有相同的数据类型。我们可以按照以下步骤进行排查和修复: 1. **检查输入数据的数据类型**:首先,确保输入到模型的数据(通常是表达矩阵)是统一的数据类型。在单细胞分析中,数据通常是浮点型,但可能是32位或64位。 2. **检查模型参数的数据类型**:模型中的参数(如权重矩阵)也有其数据类型。通常,我们使用32位浮点数(float32)进行训练,因为它在精度和速度之间取得了平衡。但是,如果输入数据是64位(float64),那么就会导致数据类型不匹配。 3. **强制转换数据类型**:在训练开始前,将模型和输入数据都转换为相同的数据类型。通常,我们选择float32,因为它在GPU上计算效率更高。 4. **检查PyTorch Lightning或PyTorch的版本**:有时,版本不兼容也可能导致数据类型自动转换的问题。 5. **在代码中显式指定数据类型**:在创建模型和加载数据时,显式指定数据类型。 具体到cell2location,我们可以按照以下步骤操作: 步骤1:在加载数据后,将数据转换为float32。 例如,如果使用AnnData对象,可以这样转换: ```python adata.X = adata.X.astype(&#39;float32&#39;) ``` 步骤2:在创建模型时,确保模型参数为float32。cell2location的模型通常会自动匹配输入数据的类型,但如果输入数据是float64,则模型参数也会是float64。因此,我们需要确保输入数据是float32。 步骤3:在训练之前,检查数据类型。可以在训练代码中添加如下检查: ```python # 检查输入数据的类型 print("Input data type:", adata.X.dtype) # 检查模型参数类型(例如,取一个参数) # 注意:模型需要初始化后才能访问参数 mod = cell2location.models.Cell2location(adata, ...) print("Model parameter type:", next(mod.parameters()).dtype) ``` 步骤4:如果发现不一致,使用`to`方法转换数据类型。例如,将模型转换为float32: ```python mod = mod.float() # 将模型的所有参数转换为float32 ``` 但是,请注意,在cell2location的代码中,可能已经考虑了数据类型。所以,重点应放在输入数据上。 另外,在PyTorch中,矩阵乘法(如`torch.mm`或`@`)要求两个矩阵具有相同的数据类型。常见的错误组合是:一个矩阵是float32,另一个是float64。 下面是一个示例代码,展示如何在训练前确保数据与模型的数据类型匹配: ```python # 假设adata是AnnData对象 import scipy import numpy as np # 将adata.X转换为float32 if adata.X.dtype != np.float32: # 注意:如果adata.X是稀疏矩阵,需要先转换为密集矩阵吗?通常不需要,但要注意内存。 # 稀疏矩阵也可以转换数据类型 if scipy.sparse.issparse(adata.X): adata.X = adata.X.astype(np.float32) else: adata.X = adata.X.astype(np.float32) # 创建模型 mod = cell2location.models.Cell2location(adata, ...) # 检查模型参数类型,并转换为float32(如果模型初始化时不是float32,但通常输入数据是float32,模型参数也会是float32) # 如果模型参数是float64,我们可以强制转换为float32 mod = mod.float() # 然后开始训练 mod.train(...) ``` 注意:将模型转换为float32可能会影响精度,但在深度学习训练中,float32通常足够。如果数据原本是float64,转换到float32可能会丢失一些精度,但影响通常很小。 如果问题仍然存在,我们需要检查在模型的前向传播过程中是否有产生不同数据类型的操作。例如,是否某些操作将float32转换成了float64?这通常发生在使用double()方法或者使用了64位精度的数值计算(如numpy的默认精度)时。 另外,在PyTorch中,一些操作(如求逆或求解线性方程组)可能会将结果转换为double(float64)。如果在前向传播中使用了这样的操作,那么后续的矩阵乘法就会出错。解决方法是在这些操作后使用`.float()`将结果转换回float32。 但是,由于我们使用的是cell2location的预定义模型,可能需要查看其内部代码。如果发现有这样的操作,可以在操作后添加`.float()`转换。 由于我们无法直接修改cell2location的源代码(除非fork并修改),因此,我们可以在创建模型后,通过覆盖模型的前向传播函数来修复数据类型问题(不推荐,除非必要)。或者,向cell2location的开发者报告此问题。 然而,根据经验,这个错误通常是由于输入数据是float64导致的,所以第一步转换输入数据到float32通常可以解决问题。 如果上述方法都不能解决问题,我们可以尝试在训练循环之前,使用PyTorch的自动混合精度(AMP)功能。但AMP通常用于混合精度训练(即部分用float16,部分用float32),而我们的问题是不匹配的数据类型(float32和float64)。所以,AMP可能不是解决此问题的直接方法。 因此,总结解决方案: 1. 确保输入数据是float32。 2. 确保模型参数是float32(通过mod.float())。 3. 如果模型内部有操作产生float64,则需要在后续操作前转换为float32(可能需要修改模型代码)。 由于我们是在使用cell2location的例子,所以重点检查输入数据。在例子中,我们可能从文件中读取的数据是float64,需要转换。 另外,在cell2location的示例代码中,可能已经进行了数据预处理,但可能没有转换数据类型。因此,我们需要在数据加载后添加转换步骤。 下面是一个修改后的cell2location示例代码片段,重点展示数据类型转换: ```python # 加载数据 adata = sc.read("data.h5ad") # 转换数据类型为float32 adata.X = adata.X.astype(&#39;float32&#39;) # 其他预处理... # 创建模型 mod = cell2location.models.Cell2location(adata, ...) # 将模型转换为float32(这一步可能不是必须的,因为模型会从输入数据中推断,但为了安全可以加上) mod = mod.float() # 训练模型 mod.train(max_epochs=250, device=0) ``` 如果问题仍然存在,我们可以尝试在训练之前打印模型中的一些参数和输入数据的数据类型,以便进一步诊断。 最后,如果以上方法都无效,考虑更新cell2location和PyTorch到最新版本,因为可能是一个已知的bug,并且在新版本中已经修复。 希望这些步骤能够解决你的问题。</think>### 解决 cell2location 矩阵乘法数据类型不匹配问题 在 cell2location 模型训练过程中出现的 `RuntimeError: expected mat1 and mat2 to have the same dtype` 错误,通常是由于矩阵乘法操作中的张量数据类型不一致导致的。以下是系统性的解决方案: #### 1. **检查并统一数据类型**(最常见解决方案) 在调用训练函数前,显式转换所有张量为相同数据类型(推荐 float32): ```python # 转换 AnnData 对象的数据类型 adata.X = adata.X.astype(&#39;float32&#39;) # 转换模型参数 mod = cell2location.models.Cell2location(adata, ...) mod = mod.float() # 确保所有参数为 float32 ``` #### 2. **验证输入数据源** 检查数据加载过程中是否引入混合精度: ```python # 检查数据类型一致性 print(f"adata.X dtype: {adata.X.dtype}") print(f"First model parameter dtype: {next(mod.parameters()).dtype}") ``` #### 3. **处理稀疏矩阵问题** 当使用稀疏矩阵时,需显式转换为密集矩阵: ```python from scipy.sparse import csr_matrix if isinstance(adata.X, csr_matrix): adata.X = adata.X.toarray().astype(&#39;float32&#39;) ``` #### 4. **强制设置 PyTorch 全局数据类型** 在训练前添加全局类型设置: ```python import torch torch.set_default_dtype(torch.float32) # 确保所有新张量为 float32 ``` #### 5. **检查自定义层的数据类型** 如果使用自定义模型组件,确保前向传播中无类型转换: ```python class CustomLayer(nn.Module): def forward(self, x): # 错误示例:意外转换类型 # x = x.double() # 会导致后续类型不匹配 # 正确做法:保持类型一致 return self.linear(x) ``` #### 6. **完整修复示例** ```python # 步骤1: 统一输入数据类型 adata.X = adata.X.astype(&#39;float32&#39;) # 步骤2: 创建模型并统一参数类型 mod = cell2location.models.Cell2location( adata, N_cells_per_location=10, detection_alpha=20 ).float() # 显式转换为 float32 # 步骤3: 设置全局默认类型 torch.set_default_dtype(torch.float32) # 步骤4: 训练模型 mod.train(max_epochs=250, device=0) ``` #### 问题根源分析 该错误发生在张量乘法操作 $ \mathbf{Y} = \mathbf{W}\mathbf{X} $ 中,当: $$ \text{dtype}(\mathbf{W}) \neq \text{dtype}(\mathbf{X}) $$ PyTorch 要求矩阵乘法中的两个张量必须具有相同的数据类型[^1]。在单细胞分析流程中,常见原因是: 1. AnnData 默认使用 float64,而 PyTorch 模型默认 float32 2. 稀疏矩阵与密集矩阵混合使用 3. 自定义层中意外类型转换 > **关键提示**:cell2location 基于 Pyro/PyTorch,其矩阵运算严格要求数据类型一致。单细胞数据通常使用 float64 存储,但 GPU 计算推荐 float32[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里 Jess

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值