【问题标题】:python: how to find a subset of one set that is closest to anotherpython:如何找到一组中最接近另一组的子集
【发布时间】:2023-03-18 15:44:02
【问题描述】:

我有以下优化问题,我可以通过“蛮力”解决,但我想知道是否有人已经实现了一个求解器,我可以用它来更快、更优雅地完成它。

我有两个不相交的整数列表。这些实际上是独一无二的,所以我可以说这是两组。一个是短的 (s) 约 S=90000 元素,另一个是长 (l) 约 L=2.5M 元素。我需要的是从l 中提取长度正好为S 的子集l2,以便sl2 的元素之间的总体距离在l 长度为@987654332 的所有子集中最小@。 sl 的元素之间的成对距离只是它们差异的绝对值。

因此,如果sl 不是不相交的,并且ls 的超集,则生成的l2 将与s 完全相同。

由于数组很长,通过测试l 的各种子集来进行暴力破解是不切实际的。

是否有某种现有的优化库或其他包可以用来解决这个问题?

顺便说一句,测量两组之间的距离可能有不同的方法,我并不关心它是哪一种,只要它会为上述极端超集示例给出 0。

【问题讨论】:

  • 这感觉有点类似于Boyer-Moore 字符串搜索的作用。不一样,但有共鸣。

标签: python algorithm optimization subset distance


【解决方案1】:

我知道你说这些是列表,但是有什么理由不暂时将它们转换为 numpy 数组?这可以直截了当(如果您不知道如何进行转换):

s = np.array(s)
l = np.array(l)

从那里,您可以使用“searchsorted”功能。我的测试运行时间不到 1.5 秒。

from __future__ import division, print_function

import numpy as np
import datetime as dt

# build numpy array
s = np.random.rand(90000)
l = np.random.rand(2.5E6)


# sort
s.sort()
l.sort()

# searchsorted finds where values in array2 should be inserted in array1 to 
# maintain the "sortedness" of a new list
# define index locations where "s" should be inserted in "l"
indices = np.searchsorted(l,s)

# build dummy list to store "s2"
# this is faster than repeatedly resizing an array
s2 = s*0


# using "indices" determine which adjacent value is the nearest match
# need to be careful here since we cannot look "below" the first index
# nor can we look "above" the last value

d1 = dt.datetime.now()
for r in np.arange(s.shape[0]):
    ix = indices[r]

    if indices[r]==0:
        s2[ix] = l[0]
    elif indices[r]==l.shape[0]:
        s2[ix] = l[r-1]
    else:
        tmp = l[ix:ix+2]
        s2[r] = tmp[ np.abs(s[r]-tmp)==np.min(np.abs(s[r]-tmp)) ]

print('Execution time: ',dt.datetime.now()-d1)

我已经进行了几次试验,看起来这很有效,但请自己确认。如果这不起作用,则不应该花费太多精力来调整它。


开始编辑


将for循环改为:

for r in np.arange(s.shape[0]):
    ix = indices[r]

    if indices[r]==0:
        s2[ix] = l[0]
        l[0] = np.nan
    elif indices[r]==l.shape[0]:
        s2[ix] = l[r-1]
        l[r-1] = np.nan
    else:
        width = 0

        while width<min([10,r]) and np.isnan(l[ix-width:ix+2+width].mean()):
            width += 1

        tmp = l[ix-width:ix+2+width]
        s2[r] = tmp[ np.abs(s[r]-tmp)==np.nanmin(np.abs(s[r]-tmp)) ][0]
        l[l==s2[r]] = np.nan

这做了两件事: 1. 它在未来的迭代中删除 l 内最近的邻居 2. 它在 l 内逐渐增加搜索宽度,以确保找到最近的邻居

同样,这可能需要调整才能拨入。

【讨论】:

  • 这是一个非常优雅且可能快速的解决方案。
  • 这会在s2 中产生很多重复的元素,而我需要一对一。我看看能不能修好。谢谢
  • 没错。我没有考虑过。这是由于两个原因: 1. 来自 s 的两个值可能在 l 中具有相同的最近邻,即使这些 s 值的 s[ix] 和 l[ix2] 之间的差异可能不同。 2. searchsorted 可能会在 l2 中的相邻值之间进行多次插入(即,来自 s 的五个值可能会卡在 s[1] 和 s[2] 之间)。我认为可以通过进行上述建议的更改来减轻(如果不能避免的话)。但是,这些更改显着增加了执行时间(对我来说大约 3-4 分钟)。我猜这取决于你需要多快。
  • 第一个算法不是问题的解决方案,因为结果集不是 l 的子集。第二种解决方案看起来是一种启发式方法——它肯定会返回 l 的一个子集,但你如何证明或说服某人它确实是最优的?
  • Praveen - 正如 OP 指出的那样,第一个解决方案不是问题的正确解决方案是正确的。 “优化”在旁观者的眼中,因为我们通常会因为收益递减而停止优化。正如问题所在,OP 没有提供足够的信息来提供完整/最终的解决方案,因此我只是试图在此处将 OP 指向正确的方向。
【解决方案2】:

距离函数可能很关键。如果将距离函数定义为集合 s 和 l2 中元素之和之间的绝对差,那么问题实际上是 NP-hard。本质上,您试图找到集合 l 的一个子集,其总和接近于 s 中元素的总和。而子集和问题(https://en.wikipedia.org/wiki/Subset_sum_problem)可以映射到这个;这意味着没有多项式时间算法可以解决这个问题。但是,您也可以针对您的案例修改子集和问题的伪多项式时间解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    • 2014-11-29
    • 1970-01-01
    相关资源
    最近更新 更多