Python 查找列表中至少相隔x的最小n值
我试图在一个列表中找到最小的n个值,其中它们的位置至少相隔x,考虑到重复。e、 g.最小5个值,彼此至少相距2 简单的例子:Python 查找列表中至少相隔x的最小n值,python,pandas,list,numpy,Python,Pandas,List,Numpy,我试图在一个列表中找到最小的n个值,其中它们的位置至少相隔x,考虑到重复。e、 g.最小5个值,彼此至少相距2 简单的例子: values = [-9995, -82, -659, -1006, -2009, 2062, -10107, -12, 13] result: [-10107, -9995, -2009, -659, 13] 更复杂的例子: values = [-9995, -83, -82, -82, -1006, -2009, 18, 2062, -659 ,-9995, -99
values = [-9995, -82, -659, -1006, -2009, 2062, -10107, -12, 13]
result: [-10107, -9995, -2009, -659, 13]
更复杂的例子:
values = [-9995, -83, -82, -82, -1006, -2009, 18, 2062, -659 ,-9995, -9995]
例如,在上述列表中:
def smallest_values(list_of_numbers: list, n_many: int, x_apart: int):
sorted_values = sorted(values)
small_val, small_val_loc = [], []
for val in sorted_values:
if len(small_val) <= n_many:
ind = list_of_numbers.index(val)
within_x = [i for i in range(ind-(x_apart-1), ind+x_apart)]
if not any(i in small_val_loc for i in within_x):
small_val_loc.append(ind)
small_val.append(val)
return small_val
values_simple = [-9995, -82, -659, -1006, -2009, 2062, -10107, -12, 13]
values_complex = [-9995, -83, -82, -82, -1006, -2009, 18, 2062, -659 ,-9995, -9995]
d = 2
n = 5
smallest_values(values_simple, n, d) # [-10107, -9995, -2009, -659, 13] CORRECT
smallest_values(values_complex, n, d) # [-9995, -2009, -659, -82] INCORRECT
def最小值(数字列表:列表,n个数:int,x个数:int):
已排序的值=已排序的(值)
small_val,small_val_loc=[],[]
对于排序值中的val:
如果len(small_val)这是一项复杂的工作,我们希望构建一个索引列表,其中第一个条目是数字列表中最小值的索引,并且该索引列表中的每个下一个条目都指向数字列表中的下一个最高值。操纵这种类型的列表将更加容易和高效。
我们可以做到以下几点:
index_map=dict()
for i in range(len(list_of_numbers)):
value=list_of_numbers[i]
if value in index_map:
index_map[value]+=[i]
else:
index_map[value]=[i]
sorted_values = sorted(index_map)
现在我们有了一个字典,它包含了所有指向它的索引的数字列表中的每个唯一值。我们还有一个从最小的唯一值到最大的列表。现在,我们可以构建索引列表:
index_list=[]
for value in sorted_values:
index_list+=index_map[value]
del index_map, sorted_values
剩下要做的就是在我们的index_列表中从左到右迭代,并找到具有适当间隔的第一个索引组合。这在算法中计算起来更容易、更快
不幸的是,时间复杂度不可能小于O(n),因为您需要检查数字列表中的每个条目以找到最小的条目
我使用递归函数实现了这一点,但您肯定可以对此进行优化,并使算法更智能:
def gap_selecter(numlist, n_many, gap):
if numlist==None: # Fast exit if recursion fails
return None
x=numlist[0]
speudolist=numlist[1:]
if n_many==1: # base case
return [x]
else:
for i in range(len(speudolist)):
if abs(x-speudolist[i])>=gap: #recursive step occurs here
recursion_list = gap_selecter(speudolist[i:], n_many-1, gap)
if recursion_list !=None:
return [x]+recursion_list
return None # if we find no possible list we return None
这是所有的东西
def smallest_values(list_of_numbers: list, n_many: int, x_apart: int):
index_map=dict()
for i in range(len(list_of_numbers)):
value=list_of_numbers[i]
if value in index_map:
index_map[value]+=[i]
else:
index_map[value]=[i]
sorted_values = sorted(index_map)
index_list=[]
for value in sorted_values:
index_list+=index_map[value]
del index_map, sorted_values
final_indices=gap_selecter(index_list, n_many, x_apart)
if final_indices==None:
return None
final_numbers=[]
for i in final_indices:
final_numbers+=[list_of_numbers[i]]
return final_numbers
values_simple = [-9995, -82, -659, -1006, -2009, 2062, -10107, -12, 13]
values_complex = [-9995, -83, -82, -82, -1006, -2009, 18, 2062, -659 ,-9995, -9995]
d = 2
n = 5
test_simple = smallest_values(values_simple, n, d) # [-10107, -9995, -2009, -659, -12]
test_complex = smallest_values(values_complex, n, d) # [-9995, -9995, -2009, -659, -83]
//编辑:啊,现在我明白了。关键短语是(假设我们取第一个和最后一个-9995,忽略倒数第二个)
最大的问题是您不能选择任何重复的值(在您的示例中是列表中的倒数第二个-9995)。相反,您希望选取重复的值,以便最后一个元素(或结果列表的总和?)最小,对吗
对我来说,这听起来像是一个受限优化问题。我甚至不确定它是否具有相同的结果,这取决于您定义为“最优”的内容(总和或最后一个元素或其他内容……)这里的关键问题是打破重复值的联系,例如示例中的-9995。我们基本上需要尝试按不同的顺序挑选它们,并检查哪一个生成具有下一个较低值的序列(或者如果下一个值相同,则检查后面的一个值,依此类推)
一种方法是使用递归搜索:
from collections import defaultdict
# find the next smallest and return all locations of that number
# that can be used (i.e. not within d from the previously used values)
def get_next(vs, vd, d, skip):
for v in vs:
os = []
for l in vd[v]:
if not any([l>x-d and l<x+d for x in skip]):
os.append((l, v))
if len(os) > 0:
return os
return None
# recursive search
def r(vs, vd, n, d, skip=[], out=[]):
if len(out) >= n:
return out
os = []
for (l, v) in get_next(vs, vd, d, skip):
o = r(vs, vd, n, d, skip+[l], out+[v])
os.append(o)
mo = min(os)
return mo
# main func
def smallest_values(values, n, d):
vd = defaultdict(list)
for l, v in enumerate(values):
vd[v].append(l)
vs = sorted(vd.keys())
return r(vs, vd, n, d, [], [])
输出:
simple: [-10107, -9995, -2009, -659, 13]
complex: [-9995, -9995, -2009, -659, -82]
CPU times: user 780 ms, sys: 20.8 ms, total: 800 ms
Wall time: 800 ms
[3, 5, 6, 7, 8]
在1000000值列表上进行计时测试(800ms,因此对于1000个单线程列表,大约15分钟):
输出:
simple: [-10107, -9995, -2009, -659, 13]
complex: [-9995, -9995, -2009, -659, -82]
CPU times: user 780 ms, sys: 20.8 ms, total: 800 ms
Wall time: 800 ms
[3, 5, 6, 7, 8]
另一方面,这将查找序列中较早时具有最低值的序列。例如,它更喜欢[1,2100]
而不是[1,3,4]
(两个序列的位置1都有1,但第一个序列的位置2有2<3)。IIUC这是预期的,根据您的评论我猜逻辑的措辞是,是否有一个先前选择的值的选择,允许选择下一个最小值。
我不确定您试图优化的内容是否明确。考虑列表<代码> [ 1, 0, 1,300, 500, 400 ] < /代码> <代码> n=3 < /代码>。如果你从零开始,你会得到[0300400]
,但是如果你从一开始,你会得到[1400]
。第一个数字最小,但第二个总数最小。哪个是正确答案?[0300400]
将是我问题的正确答案。我不是在寻找最小的和,我想要最小的n个数,它们之间至少相隔x。谢谢该算法将从最小的数字开始,然后迭代地添加下一个最小的数字,该数字在列表中与之前添加的数字至少相隔x。因此,您是说您将始终获取下一个最小的数字,即使该选择迫使您稍后获取更大的数字?因此,在上面加上-1
,使n=4
-[-1100,1,0,1300,500,400]
,正确的答案是[-1,0300400]
而不是[-1,1,1400]
?是的,完全正确在更复杂的例子中,取最后一个-9995值而不是倒数第二个值的逻辑是什么(哪个值相同,但在列表中出现得更早)?test_complex
失败,不是吗?它不应该有-83
(如果我正确理解了这个问题)是的,很抱歉,在这个简单的示例中,它应该是[-10107,-9995,-2009,-659,13]
-12紧挨着-10107,正如perl提到的那样,不应该选择-83。您的方法非常聪明,非常感谢。您对问题陈述的理解是正确的,[1,2100]
应该更喜欢酷,很高兴它有帮助!这是一个非常有趣的问题:)
CPU times: user 780 ms, sys: 20.8 ms, total: 800 ms
Wall time: 800 ms
[3, 5, 6, 7, 8]