我有一个数字数组,例如:
A = [1, 5, 2, 4, 3]
和确定排名的数组,例如:
B = [0, 2, 1]
我的目标是找到“服从”等级B的A的所有子阵列。如果一个子阵列服从等级,则意味着该子阵列的第i个最小元素必须以B[i]
作为其(子数组)索引。因此,要使子数组匹配,其中的最小元素必须位于位置0,第二个最小元素必须位于位置2,最大元素必须位于位置1。
答案 0 :(得分:3)
您可以使用scipy.stats.rankdata
直接获取排名,而不是遍历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的原因。
答案 1 :(得分:3)
这是一个基于线性代数的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]
我不是一个麻木的专家,所以也许有办法进一步优化它并消除循环。
更新,这是一个更干净的版本:
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解决方案的性能会超越:
这是您的代码,我将其变成一个返回所需子数组(而不是打印)的函数:
def get_ranked_subarrays_op(A, B):
m = len(A)
n = len(B)
out = []
for i in range(m - n + 1):
current_subarray = A[i:i + n]
# we now do n - 1 comparisons 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:
out.append(current_subarray)
return out
大型随机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
答案 2 :(得分:2)
您可以遍历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)))
输出:
[[1, 5, 2], [2, 4, 3]]
答案 3 :(得分:1)
至少,我们可以通过考虑相邻元素的(二进制)关系来更快地排除候选窗口,这可能允许并行检查。呼叫less than
0
和greater than
1
。然后:
A = [1, 5, 2, 4, 3]
A'= [0, 1, 0, 1]
B'= [0, 1]
B = [0, 2, 1]
很明显,任何候选人都必须匹配关系序列。还要注意,B
唯一允许重叠的部分类型是升序或降序(意味着如果找到匹配项,我们可以先验跳过)。