您的问题似乎与
加权最小顶点覆盖问题类似(这是NP完全问题)。感谢@Gareth Rees在下面的评论中澄清了我的错误,解释了顶点覆盖与您正在寻找的集合之间的关系。但是,您仍然可以调查顶点覆盖问题和相关文献,因为它们仍然具有一些共同特征。
如果您愿意使用直径而不是图权重总和,则可以使用您在问题中链接的最小直径集方法。如果您当前的距离度量称为
d
(您希望找到彼此距离最远的点),那么只需定义
d'=1/d
并使用
d'
解决最小距离问题。
还可能存在某种形式的图形切割算法(例如
标准化割)与您所寻找的子集之间的关系。如果您的距离度量用作节点之间的图权重或亲和力,则可能能够修改现有的图形切割目标函数以匹配您的目标函数(寻找具有最大总权重的k个节点组)。
这似乎是一个组合难题。您可以考虑使用模拟退火之类的简单方法。提议函数可以随机选择当前在
k
子集中的一个点,并用不在
k
子集中的点随机替换它。
您需要一个良好的温度调度和可能需要根据成本使用加热。但是,这种方法编程非常简单。只要
n
相对较小,您就可以不断随机选择
k
子集,并退火到具有非常大总距离的
k
子集。
这只会给您一个近似值,但即使确定性方法也可能以近似方式解决此问题。
下面是模拟退火代码的第一次尝试。请注意,我不能保证这个解决方案是否有效,如果计算距离太困难或问题实例大小增长太快,则可能效率低下。我使用非常幼稚的几何冷却和固定冷却速率,您可能还想尝试比仅随机交换节点更高级的提议。
all_nodes = np.asarray(...)
all_dists = np.asarray(...)
N = len(all_nodes)
k = 10
def calculate_distance(node_subset, distances):
shuffle = np.random.shuffle(all_nodes)
current_subset = shuffle[0:k]
current_outsiders = shuffle[k:]
temp = 100.0
cooling_rate = 0.95
num_iters = 10000
for ii in range(num_iters):
proposed_subset = current_subset.copy()
proposed_outsiders = current_outsiders.copy()
index_to_swap = np.random.randint(k)
outsider_to_swap = np.random.randint(N - k)
tmp = current_subset[index_to_swap]
proposed_subset[index_to_swap] = current_outsiders[outsider_to_swap]
proposed_outsiders[outsider_to_swap] = tmp
potential_change = np.exp((-1.0/temp)*
calculate_distance(proposed_subset,all_dists)/
calculate_distance(current_subset, all_dists))
if potential_change > 1 or potential_change >= np.random.rand():
current_subset = proposed_subset
current_outsiders = proposed_outsiders
temp = cooling_rate * temp