二进制数组中长连续零区域检测及高效实现方法问询
高效查找二进制数组中连续零超5的区间
嘿,针对你这个找二进制数组中连续零个数超过5的区域起止索引的问题,我有几个比纯for循环高效得多的方案,不管是用Python的numpy还是MATLAB都能轻松实现,而且思路很清晰!
方案一:向量化差分+累积和(无冗余循环,性能最优)
这个思路的核心是把连续零的检测转化为连续1的长度计算,通过差分快速定位所有连续零段的边界,再筛选出符合长度要求的段。
Python(numpy)实现代码:
import numpy as np # 输入数组 x = np.array([1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1]) # 将0转为1,1转为0,把连续零转化为连续1方便计算 zero_mask = (x == 0).astype(int) # 计算差分,定位连续段的起止边界(前后补0是为了处理数组首尾的连续段) diff_arr = np.diff(np.concatenate([[0], zero_mask, [0]])) # 找到连续零段的起始索引(差分=1的位置) start_pos = np.where(diff_arr == 1)[0] # 找到连续零段的结束索引(差分=-1的位置减1,因为差分标记的是段结束的下一个位置) end_pos = np.where(diff_arr == -1)[0] - 1 # 计算每个连续零段的长度 segment_lengths = end_pos - start_pos + 1 # 筛选出长度超过5的段 valid_segments = segment_lengths > 5 # 转换为1-based索引(符合你的示例需求) valid_starts = start_pos[valid_segments] + 1 valid_ends = end_pos[valid_segments] + 1 # 合并结果 c = np.concatenate([valid_starts, valid_ends]) print(c) # 输出: [12 18]
MATLAB实现代码:
x = [1 1 1 1 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 1]; zero_mask = (x == 0); % 计算差分,前后补0处理首尾 diff_mask = diff([0; zero_mask; 0]); % 定位连续零段的起止 start_indices = find(diff_mask == 1); end_indices = find(diff_mask == -1) - 1; % 筛选长度超5的段 segment_lengths = end_indices - start_indices + 1; valid_idx = segment_lengths > 5; % 生成结果 c = [start_indices(valid_idx), end_indices(valid_idx)]; disp(c); % 输出: 12 18
优势:全程都是向量化操作,没有逐元素的循环,当数组规模很大时,性能比for循环提升非常明显,代码也简洁易读。
方案二:滑动窗口检测指定mask,再扩展完整区间
如果你需要先检测[0,0,0,0,0]这个mask的出现位置,再扩展得到完整的连续零段,这个方案很合适:先找到所有匹配mask的滑动窗口,再把相邻的窗口合并,最后扩展到整个连续零区间。
Python(numpy)实现代码:
import numpy as np x = np.array([1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1]) target_mask = np.array([0, 0, 0, 0, 0]) mask_len = len(target_mask) # 生成所有滑动窗口 windows = np.lib.stride_tricks.sliding_window_view(x, mask_len) # 找到所有匹配target_mask的窗口起始索引 match_indices = np.where((windows == target_mask).all(axis=1))[0] if len(match_indices) == 0: c = [] else: # 合并连续的匹配索引,得到mask出现的连续段 diff_matches = np.diff(match_indices) segment_starts = np.concatenate([[match_indices[0]], match_indices[1:][diff_matches != 1]]) segment_ends = np.concatenate([match_indices[:-1][diff_matches != 1], [match_indices[-1]]]) full_starts = [] full_ends = [] # 扩展每个mask连续段到完整的连续零区间 for s, e in zip(segment_starts, segment_ends): # 向左扩展到第一个非0的位置 left = s while left > 0 and x[left-1] == 0: left -= 1 # 向右扩展到第一个非0的位置 right = e + mask_len - 1 while right < len(x)-1 and x[right+1] == 0: right += 1 # 转为1-based索引 full_starts.append(left + 1) full_ends.append(right + 1) c = np.concatenate([full_starts, full_ends]) print(c) # 输出: [12 18]
优势:精准满足你"先检测mask出现位置"的需求,扩展步骤只针对筛选后的少数段,效率依然很高,逻辑也直观。
内容的提问来源于stack exchange,提问作者Alexandros Mel
相关产品推荐
相关产品推荐

