用“预先计算每个点能够和它在一个集合(即满足 (a-x)(b-y)≤0 的条件)的所有点作为邻居,每次在未选的邻居点中尝试,而不是所有未选的点中尝试”的思路改写程序,
结果他给出的程序更慢,让他修改,结果又恢复了原方法。
于是更详细地描述算法:如果一个点有n个邻居,那么最好的情况就是这n+1个数构成的集合就是具有最大元素个数的集合,否则去掉一些点才能构成最大元素个数的集合,那么从第一个邻居开始,看余下的点是否是它的邻居,如果某点不是,则删除此点,如果删除后元素个数少于前面已有的最大个数,去掉该邻居,每次的搜索均不超过邻居的范围,编写程序,不做别的
得到的程序如下
def find_maximal_subsets_optimized(n):
"""
使用邻居限制的搜索空间来找到所有满足条件的最大子集 B
"""
all_points = [(i, j) for i in range(1, n + 1) for j in range(1, n + 1)]
# 预计算每个点的邻居(可以共存于同一集合的点)
neighbors = {}
for point in all_points:
neighbors[point] = []
for i, point1 in enumerate(all_points):
a, b = point1
for j, point2 in enumerate(all_points):
if i == j:
continue
x, y = point2
if (a - x) * (b - y) <= 0:
neighbors[point1].append(point2)
max_size = 0
maximal_subsets = []
def backtrack(current_set, candidate_neighbors):
nonlocal max_size
current_size = len(current_set)
if current_size > max_size:
max_size = current_size
maximal_subsets.clear()
maximal_subsets.append(set(current_set))
elif current_size == max_size:
maximal_subsets.append(set(current_set))
# 如果没有候选点了,返回
if not candidate_neighbors:
return
# 如果当前集合大小加上剩余候选点数 <= 已知最大大小,剪枝
if current_size + len(candidate_neighbors) < max_size: #只有这一处需要修改,从<=改为<,以输出所有满足的集合
return
# 尝试每个候选点
for i, point in enumerate(candidate_neighbors):
# 检查该点是否与当前集合中所有点都满足条件
can_add = True
for existing_point in current_set:
a, b = point
x, y = existing_point
if (a - x) * (b - y) > 0:
can_add = False
break
if can_add:
# 新的候选集是当前候选集中该点之后的点,且必须是该点的邻居
new_candidates = []
for candidate in candidate_neighbors[i+1:]:
if candidate in neighbors[point]:
new_candidates.append(candidate)
current_set.append(point)
backtrack(current_set, new_candidates)
current_set.pop()
# 从每个点开始搜索
for start_point in all_points:
# 初始候选集是起始点的所有邻居(按顺序排列)
initial_candidates = [p for p in neighbors[start_point] if p > start_point]
backtrack([start_point], initial_candidates)
# 去重
unique_subsets = []
seen = set()
for s in maximal_subsets:
key = tuple(sorted(s))
if key not in seen:
seen.add(key)
unique_subsets.append(s)
return unique_subsets, max_size
def main():
n = 6 # 可以修改这个参数
print(f"I = {{1, 2, ..., {n}}}")
print("使用邻居限制搜索空间算法寻找最大子集...")
result, max_size = find_maximal_subsets_optimized(n)
print(f"\n最大元素个数: {max_size}")
print(f"去重前集合数: {len(maximal_subsets)}")
print(f"找到 {len(result)} 个满足条件的最大集合 B:")
for i, B in enumerate(result, 1):
print(f"{i}: {sorted(B)}")
if __name__ == "__main__":
main()
这个版本只输出了一个集合,只要修改一个符号,就能输出所有,见程序中注释。
性能比较
n=6
I = {1, 2, ..., 6}
使用优化算法寻找最大子集...
最大元素个数: 11
找到 252 个满足条件的最大集合 B:
real 0m0.060s
user 0m0.056s
sys 0m0.000s
原程序
real 0m0.298s
user 0m0.296s
sys 0m0.000s
n=7
I = {1, 2, ..., 7}
使用优化算法寻找最大子集...
最大元素个数: 13
找到 924 个满足条件的最大集合 B:
real 0m0.445s
user 0m0.444s
sys 0m0.000s
原程序
real 0m4.067s
user 0m4.064s
sys 0m0.000s


被折叠的 条评论
为什么被折叠?



