算法,列表元素之间的最接近点

时间:2018-07-01 14:07:22

标签: python python-3.x algorithm nested-lists closest-points

我有n个大小不等的排序列表(我事先不知道会有多少个列表)。我需要找到每个列表中一个元素之间的最小平均距离。

例如,给定三个列表的n = 3:

a = [14, 22, 36, 48]
b = [14, 23, 30, 72]
c = [1, 18, 24]

输出应为(22,23,24),因为:

mean(abs(22-23), abs(23-24), abs(22-24)) = 1.33333

是上面示例中所有点中最小的。

我试图按照以下方式在Python中实现

def alligner(aoa):
'''
read arrays of arrays of peaks and return closest peaks
'''
#one of arrays is empty
if not [y for x in aoa for y in x]:
    return None
# there is the same nr in all array no need to do anything
candidate = set.intersection(*map(set, aoa))
if candidate:
    # returns intersect
    return [max(list(candidate))] * len(aoa)
else:
    #tried cartesian product via bumpy malloc err
    pass

我现在怀疑的是另一部分的实施。我曾考虑过使用笛卡尔乘积来生成所有组合,但是会遇到内存问题。我的猜测是确实会以某种方式生成所有组合(也许是itertools ??)并遍历所有这些组合,但是我不知道是否有任何算法可以解决这个问题。 我不需要代码,而只是暗示是否有任何有效的方法可以解决这个问题,或者在排列后的列表上使用n for循环的蛮力是唯一的

编辑

关于问题的大小,列表的nr最大值为100(固定),而元素的nr可以变化,但是我想说的是每个列表具有4或5点的示例。 所有点都是非负的。 尝试了提议的itertools解决方案,但是当然不是内存问题,但是已经运行了好几个小时,并停留在第三个元素上。

4 个答案:

答案 0 :(得分:2)

首先,优化差异均值与优化差异总和相同。

如果您将问题建模为有向图,则可以解决此问题:

让您的列表为A,B,C。列表的每个条目都是图v_ai的顶点,其中a是列表,i是索引。

对于A中的每个索引i,B中的j,添加边v_ai -> v_bj,边长为abs(A(i) - B(j))

对于B中的每个索引i,C中的每个j,添加一个边v_bi -> v_cj,边长为abs(B(i) - C(j))

对于C中的每个索引i,A中的j,都添加一个边v_ci -> v_aj,且边长为abs(C(i) - A(j))

您现在要寻找的是此图中的最小周期。将此answer用于O(n ^ 3)算法。 (一种改良的Floyd-Warshall算法)

答案 1 :(得分:1)

此方法是蛮力方法,但使用类似于Dijkstra算法的消除方法,导致的情况要少得多(使算法最有可能快几个数量级,特别是对于大型列表或大量列表) 。告诉我您是否不了解,我可以澄清。可以在这里找到实现:https://github.com/nerryoob/closestPoint

您正在做的是列出数字的不同组合的列表(即答案)?一开始最好(索引0),一开始最好(索引0),反之亦然,请参见最有效的方法。您将只为第一个输入列表创建结果列表,而完全忽略其他列表。当然,对于一个列表,所有项目都是解决方案-它们的总和为0。因此,只需将第一个输入列表复制到结果列表中即可。

接下来,可能使用 while 循环,遵循此算法。取第一项并从结果列表中将其弹出。存储其值。转到下一个输入列表,并为该下一个输入列表中的每个项目制作一个刚弹出的顶部项目的副本,该项目也包含下一个输入列表中的项目。找到新的总体差异,然后将新的差异插入列表中。重复直到最上面的解决方案包含所有列表。这意味着您保证(至少是联合第一)是最好的解决方案,同时花费明显更少的时间花费在显然不是解决方案的组合上

  • 示例( 方括号中的数字是总差)

    [14、22、36、48] [14,23,30,72] [1,18,24]

结果列表为[14(0), 22(0), 36(0), 48(0)]

  • 查看14。插入新的数字[14和14(0),22(0),36(0), 48(0),14和23(9),14和30(16),14和72(58)]
  • 查看14和14。插入新的数字[22(0),36(0),48(0),14和 14和18(8),14和23(9),14和30(16),14和14和24(20),14 和14和1(26),14和72(58)]
  • 查看22。插入新的数字[36(0),48(0),22和23(1),14 和14和18(8),22和14(8),22和30(8),14和23(9),14和30 (16),14和14和24(20),14和14和1(26),22和72(50),14 和72(58)]

继续重复,最后您将获得22、23、24。由于其中包含所有 n 个列表,因此您可以停下来并给出答案

对其进行优化:

  • 删除重复项
  • 也许以某种方式利用有序列表
  • 考虑将总差异相同的物品放在何处,也许数量最多的物品排在首位

编辑: 算法复杂度为O(n ^ 2)

答案 2 :(得分:0)

我不确定找到最佳解决方案的最佳方法,但是一种启发式方法可能是检查范围。如果我们的列表已排序,我们可以使用二进制搜索来检查列表中的元素是否在范围内。因此,我们可以分而治之,尝试缩小包含每个列表中一个元素的范围。由于均值计算的性质,很不幸,我们可能也会对包含许多但不是全部列表的元素的范围感兴趣,因为非常接近的数字和一些离群值的集合可能会产生较小的差异-均值大于较小范围内的差异范围;这使解决方案相当复杂。

答案 3 :(得分:0)

对于您的问题的规模,我们真的不是很了解,也就是说,每个列表有多少个列表以及多少个元素。对于初学者和设置基线,您可以仅使用itertools.product来迭代三个列表中元素的所有可能组合,而无需在列表中实现它们。然后,您可以对其进行迭代并找到最好的一个,或者将它们直接传递到min中,并使用特殊的key函数,并使用itertools.combinationssum来找到最低的一个平均距离(如果总和最低,则平均距离也是如此)。

>>> a = [14, 22, 36, 48]
>>> b = [14, 23, 30, 72]
>>> c = [1, 18, 24]
>>> len(list(itertools.product(a, b, c)))
48
>>> min(itertools.product(a, b, c),
...     key=lambda t: sum(abs(n-m) for n, m in itertools.combinations(t, 2)))
(22, 23, 24)

根据问题的大小,这可能太慢了,但也许就足够了。

相关问题