Python 查找具有给定排名的固定长度的所有子阵列
我有一个数字数组,例如:Python 查找具有给定排名的固定长度的所有子阵列,python,algorithm,ranking,Python,Algorithm,Ranking,我有一个数字数组,例如: A = [1, 5, 2, 4, 3] B = [0, 2, 1] 以及确定秩的数组,例如: A = [1, 5, 2, 4, 3] B = [0, 2, 1] 我的目标是找到“服从”秩B的A的所有子数组。如果子数组服从秩,这意味着子数组的第i个最小元素必须具有B[i]作为其(子数组)索引。因此,子阵列要匹配,其中最小的元素必须在位置0,第二个最小的元素必须在位置2,最大的元素必须在位置1 例如,这里有两个A的子数组与排名匹配:[1,5,2](因为A[0]
A = [1, 5, 2, 4, 3]
B = [0, 2, 1]
以及确定秩的数组,例如:
A = [1, 5, 2, 4, 3]
B = [0, 2, 1]
我的目标是找到“服从”秩B的A的所有子数组。如果子数组服从秩,这意味着子数组的第i个最小元素必须具有B[i]
作为其(子数组)索引。因此,子阵列要匹配,其中最小的元素必须在位置0,第二个最小的元素必须在位置2,最大的元素必须在位置1
例如,这里有两个A的子数组与排名匹配:[1,5,2](因为A[0]
到目前为止,我已经设法找到了一个时间复杂度为O(mn)
(m是len(a)和n是len(B))的解决方案,它迭代长度为3的所有子阵列,并验证它们的顺序是否正确:
A = [1, 5, 2, 4, 3]
B = [0, 2, 1]
m = len(A)
n = len(B)
for i in range(m - n + 1):
current_subarray = A[i:i + n]
# we now do n - 1 comparaisons to check whether the subarray is correctly ordered.
for B_index in range(n - 1):
if current_subarray[B[B_index]] > current_subarray[B[B_index + 1]]:
break
else:
print("Subarray found:", current_subarray)
结果:
Subarray found: [1, 5, 2]
Subarray found: [2, 4, 3]
这是可行的,但我想知道是否有一个更好的优化算法(比
O(mn)
更好)来完成这项任务。您可以循环a
并检查生成的子阵列:
A, B = [1, 5, 2, 4, 3], [0, 2, 1]
def results(a, b):
_l = len(b)
for c in range(len(a)-_l+1):
_r = a[c:c+_l]
new_r = [_r[i] for i in b]
if all(new_r[i] < new_r[i+1] for i in range(len(new_r)-1)):
yield _r
print(list(results(A, B)))
您可以使用直接获取列组,而不是在B上迭代以比较列组:
from scipy.stats import rankdata
A = [1, 5, 2, 4, 3]
B = [0, 2, 1]
m = len(A)
n = len(B)
for i in range(m - n + 1):
current_subarray = A[i:i + n]
ranked_numbers = (rankdata(current_subarray).astype(int) - 1).tolist()
if ranked_numbers == B:
print("Subarray found:", current_subarray)
# Subarray found: [1, 5, 2]
# Subarray found: [2, 4, 3]
注意:
rankdata()
从1开始排列,而不是从0开始排列,这就是为什么上面的公式会从numpy数组中的每个元素中减去1 这是一个基于一些线性代数的numpy
解决方案
首先将B
转换为基准:
import numpy as np
A = [1, 5, 2, 4, 3]
B = [0, 2, 1]
b = np.eye(len(B))[B]
print(b)
#array([[1, 0, 0],
# [0, 0, 1],
# [0, 1, 0]])
现在我们可以遍历A
的每个子数组,并将其投影到这个空间中。如果结果向量被排序,这意味着子数组遵循排序
for i in range(0, (len(A) - len(B))+1):
a = np.array(A[i:i+len(B)])
if (np.diff(a.dot(b))>0).all():
print(a)
#[1 5 2]
#[2 4 3]
我不是numpy专家,因此可能有一种方法可以进一步优化此功能并消除循环
更新,这里有一个更干净的版本:
def get_ranked_subarrays(A, B):
m = len(A)
n = len(B)
b = np.eye(n)[B]
a = np.array([A[i:i+n] for i in range(0, m - n+1)])
return a[(np.diff(a.dot(b))>0).all(1)].tolist()
A = [1, 5, 2, 4, 3]
B = [0, 2, 1]
get_ranked_subarrays(A, B)
#[[1, 5, 2], [2, 4, 3]]
业绩结果: 您的解决方案非常适合于较小的
n
,但随着A
的尺寸变大,numpy解决方案的性能会更好:
以下是我将其转换为返回所需子数组(而不是打印)的函数的代码:
大型随机a
的计时结果:
array_size = 1000000
A = np.random.randint(low=0, high=10, size=array_size)
B = [0, 2, 1]
%%timeit
get_ranked_subarrays_op(A, B)
#1 loop, best of 3: 1.57 s per loop
%%timeit
get_ranked_subarrays(A, B)
#1 loop, best of 3: 890 ms per loop
但是,如果m
也变大,则由于短路,您的解决方案会稍微好一点(对于较大的m
,短路的可能性会变大)。下面是我们让m
100的计时结果
array_size = 1000000
basis_size = 100
A = np.random.randint(low=0, high=10, size=array_size)
B = range(basis_size)
np.random.shuffle(B)
%%timeit
get_ranked_subarrays_op(A, B)
#1 loop, best of 3: 1.9 s per loop
%%timeit
get_ranked_subarrays(A, B)
#1 loop, best of 3: 2.79 s per loop
至少,通过考虑相邻元素的(二进制)关系,我们可以更快地排除候选窗口,从而允许并行检查。调用
小于
0
和大于
1
。然后:
A = [1, 5, 2, 4, 3]
A'= [0, 1, 0, 1]
B'= [0, 1]
B = [0, 2, 1]
显然,任何候选者都必须匹配关系序列。还请注意,
B
中唯一允许重叠的部分类型是升序或降序(这意味着如果找到匹配项,我们可能会提前跳过)。变量中使用的所有下划线是怎么回事?@coldspeed只是个人风格感谢您的快速回答!然而,也许我在我的问题上不够清楚,我只需要服从排名的A的子数组。你的解决方案给了我所有可能的服从排名的子数组,但其中大多数不是A的子数组。这难道不会使这个方法更长(因为我必须删除不属于A的子数组)?@平果根据计时结果,看来,对于解决方案,您真的无法获得比现有解决方案更好的解决方案。@pault我添加了新的计时,但是,如果您觉得它们不准确,我将删除它们。您是在寻找时间复杂度更低的解决方案吗?因为我不确定这样的东西是否存在。@Paritossingh是的,这就是我要找的。也许没有,但我想这就是为什么我问:)。但让我怀疑的是,我正在研究子阵列,其中一些子阵列重叠-也许有一种方法可以通过缓存一些子阵列来减少计算量,比如优化字符串搜索(如KMP)算法的工作方式?我看到的问题是这一点。考虑[0,1,3,2]。在第一个子列表中,[1,3]的相对秩为1和2,而在第二个子列表中,[1,3]的相对秩为0和2。本质上,结果取决于“窗口”,因此需要重新评估才能确定。在这种情况下,无论缓存的结果是什么,最终都需要重新检查,失去了所有好处。(如果我错了,有人请纠正我)“PrimoSothigh”是正确的,但是考虑长度为2的子数组。例如,当我从[0,1,3]到[1,3,2](在您的列表中)时,假设我对第一个子阵列进行了比较,并推断它不符合要求。我转到第二个子阵列,但是我可能已经做了一些比较(最后两个元素成为第二个子阵列的前两个元素)。尽管正如你所说的,知道1<3是不有用的,因为2是中间的,但是有些情况下,子数组的重叠部分必须是有用的——至少,这就是我所想的。确实,但是因为它的“一些”情况而不是全部,所以无论如何都必须重新检查所有的情况。由于比较是一个恒定时间的运算,所以最终会得到平方1。更改窗口会改变关于比较的一切,有利的和不利的。谢谢,但是我对使用的算法更感兴趣,我已经查看了scipy源代码-如果我错了,请纠正我-但是看起来他们正在对列表排序-所以最后复杂性并不比O(mn)好?@平果是的,它看起来像是使用了合并排序或快速排序。在这种情况下,由于需要对每个排名执行O(nlogn)排序,上述操作可能会较慢。你必须两个都计时才能确定。我不认为你能做得比你的解决方案更好。谢谢你的回答-我从来没有想过这样做!然而,尽管