You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NumPy数组/Pandas Series中找和最大的n个连续非零值起始索引

问题描述

给定NumPy数组 arr = np.array([0, 0, 1, 8, 10, 20, 26, 32, 37, 52, 0, 0, 46, 42, 30, 19, 8, 2, 0, 0, 0]),需要找到长度为n的连续非零子数组中,和最大的那个的起始索引。例如当n=5时,目标子数组为[20,26,32,37,52],对应起始索引为5。

现有方案存在的问题:

  • 循环遍历每个窗口:时间复杂度为O(m*n)(m为数组长度),长数组下重复计算多,效率极低。
  • 非零元素分组:思路方向正确,但未找到简洁高效的落地方式。

高效NumPy解决方案

核心思路:通过卷积操作快速标记合法窗口(连续n个非零元素的起始位置),结合滑动窗口求和一次性计算所有窗口的和,最后筛选出合法窗口中的最大值索引,整体时间复杂度为O(m)。

import numpy as np

arr = np.array([0, 0, 1, 8, 10, 20, 26, 32, 37, 52, 0, 0, 46, 42, 30, 19, 8, 2, 0, 0, 0])
n = 5

# 1. 生成数组的非零掩码
non_zero_mask = arr != 0

# 2. 用卷积标记所有可作为n长度连续非零窗口的起始点
# 卷积核为n个1,结果等于n的位置即为合法起始索引
valid_start_mask = np.convolve(non_zero_mask.astype(int), np.ones(n, dtype=int), mode='valid') == n

# 3. 滑动窗口求和(底层C实现,O(m)时间)
window_sums = np.convolve(arr, np.ones(n), mode='valid')

# 4. 将非法窗口的和设为负无穷,避免被选中
valid_sums = np.where(valid_start_mask, window_sums, -np.inf)

# 5. 找到最大和对应的起始索引
max_loc = np.argmax(valid_sums)

print(max_loc)  # 输出:5

效率优势

  • 滑动求和与合法窗口标记均通过np.convolve完成,避免了Python层面的循环开销。
  • 仅需遍历数组一次即可完成所有计算,适合长数组及批量处理场景。

Pandas解决方案(直观易读)

如果需要结合其他Pandas数据处理操作,可使用滚动窗口API实现,代码更直观,适合中等规模数据:

import pandas as pd
import numpy as np

arr = np.array([0, 0, 1, 8, 10, 20, 26, 32, 37, 52, 0, 0, 46, 42, 30, 19, 8, 2, 0, 0, 0])
n = 5

s = pd.Series(arr)

# 计算滚动窗口和,同时检查窗口内是否全为非零
rolling_sum = s.rolling(window=n).sum()
rolling_all_nonzero = s.rolling(window=n).apply(lambda x: (x != 0).all(), raw=True)

# 筛选合法窗口,转换为起始索引
valid_windows = rolling_sum[rolling_all_nonzero.astype(bool)]
max_loc = valid_windows.idxmax() - n + 1

print(max_loc)  # 输出:5

注意:Pandas的rolling.apply依赖Python层面的lambda循环,超大规模数组下效率略低于纯NumPy方案。


稀疏数组优化方案(零元素占比高时)

如果数组中存在大量零块,可先分割出所有连续非零块,仅在长度≥n的块内计算最大窗口和,进一步减少无效计算:

import numpy as np

arr = np.array([0, 0, 1, 8, 10, 20, 26, 32, 37, 52, 0, 0, 46, 42, 30, 19, 8, 2, 0, 0, 0])
n = 5

non_zero_mask = arr != 0
diffs = np.diff(non_zero_mask.astype(int))

# 提取所有非零块的起始、结束索引
starts = np.where(diffs == 1)[0] + 1
ends = np.where(diffs == -1)[0]
# 处理数组首尾的非零块
if non_zero_mask[0]:
    starts = np.insert(starts, 0, 0)
if non_zero_mask[-1]:
    ends = np.append(ends, len(arr)-1)

max_sum = -np.inf
max_loc = 0

# 遍历每个非零块,计算块内最大n窗口和
for start, end in zip(starts, ends):
    block_len = end - start + 1
    if block_len < n:
        continue
    block = arr[start:end+1]
    block_sums = np.convolve(block, np.ones(n), mode='valid')
    block_max_idx = np.argmax(block_sums)
    block_max_sum = block_sums[block_max_idx]
    if block_max_sum > max_sum:
        max_sum = block_max_sum
        max_loc = start + block_max_idx

print(max_loc)  # 输出:5

内容的提问来源于stack exchange,提问作者Arvid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 04:05:53