在列表中查找包含特定值的最小元素数
Find minimum number of elements in a list that covers specific values
一位招聘人员想组建一个具有不同技能的团队,他想挑选能够涵盖所有所需技能的最少人数。
N
代表人数,K是需要包括的不同技能的数量。列表 spec_skill = [[1,3],[0,1,2],[0,2,4]]
提供有关每个人技能的信息。例如人 0
有技能 1 and 3
,人 1
有技能 0, 1 and 2
等等。
代码应输出招聘人员可以找到的最小团队规模(最少人数)和指示要招聘到团队的人员的具体 ID 的值。
我用暴力实现代码如下,但由于一些数据超过数千,看来我需要用 heuristic 方法来解决。在这种情况下,可能会有大概的答案。
任何有关如何使用启发式方法解决它的建议将不胜感激。
N,K = 3,5
spec_skill = [[1,3],[0,1,2],[0,2,4]]
A = list(range(K))
set_a = set(A)
solved = False
for L in range(0, len(spec_skill)+1):
for subset in itertools.combinations(spec_skill, L):
s = set(item for sublist in subset for item in sublist)
if set_a.issubset(s):
print(str(len(subset)) + '\n' + ' '.join([str(spec_skill.index(item)) for item in subset]))
solved = True
break
if solved: break
这是我的方法。代码中可能存在潜在的优化可能性,但基本思想应该是可以理解的。
import random
import time
def man_power(lst, K, iterations=None, period=0):
"""
Specify a fixed number of iterations
or a period in seconds to limit the total computation time.
"""
# mapping each sublist into a (sublist, original_index) tuple
lst2 = [(lst[i], i) for i in range(len(lst))]
mini_sample = [0]*(len(lst)+1)
if period<0 or (period == 0 and iterations is None):
raise AttributeError("You must specify iterations or a positive period")
def shuffle_and_pick(lst, iterations):
mini = [0]*len(lst)
for _ in range(iterations):
random.shuffle(lst2)
skillset = set()
chosen_ones = []
idx = 0
fullset = True
# Breaks from the loop when all skillsets are found
while len(skillset) < K:
# No need to go further, we didn't find a better combination
if len(chosen_ones) >= len(mini):
fullset = False
break
before = len(skillset)
skillset.update(lst2[idx][0])
after = len(skillset)
if after > before:
# We append with the orginal index of the sublist
chosen_ones.append(lst2[idx][1])
idx += 1
if fullset:
mini = chosen_ones.copy()
return mini
# Estimates how many iterations we can do in the specified period
if iterations is None:
t0 = time.perf_counter()
mini_sample = shuffle_and_pick(lst, 1)
iterations = int(period / (time.perf_counter() - t0)) - 1
mini_result = shuffle_and_pick(lst, iterations)
if len(mini_sample)<len(mini_result):
return mini_sample, len(mini_sample)
else:
return mini_result, len(mini_result)
一位招聘人员想组建一个具有不同技能的团队,他想挑选能够涵盖所有所需技能的最少人数。
N
代表人数,K是需要包括的不同技能的数量。列表 spec_skill = [[1,3],[0,1,2],[0,2,4]]
提供有关每个人技能的信息。例如人 0
有技能 1 and 3
,人 1
有技能 0, 1 and 2
等等。
代码应输出招聘人员可以找到的最小团队规模(最少人数)和指示要招聘到团队的人员的具体 ID 的值。
我用暴力实现代码如下,但由于一些数据超过数千,看来我需要用 heuristic 方法来解决。在这种情况下,可能会有大概的答案。
任何有关如何使用启发式方法解决它的建议将不胜感激。
N,K = 3,5
spec_skill = [[1,3],[0,1,2],[0,2,4]]
A = list(range(K))
set_a = set(A)
solved = False
for L in range(0, len(spec_skill)+1):
for subset in itertools.combinations(spec_skill, L):
s = set(item for sublist in subset for item in sublist)
if set_a.issubset(s):
print(str(len(subset)) + '\n' + ' '.join([str(spec_skill.index(item)) for item in subset]))
solved = True
break
if solved: break
这是我的方法。代码中可能存在潜在的优化可能性,但基本思想应该是可以理解的。
import random
import time
def man_power(lst, K, iterations=None, period=0):
"""
Specify a fixed number of iterations
or a period in seconds to limit the total computation time.
"""
# mapping each sublist into a (sublist, original_index) tuple
lst2 = [(lst[i], i) for i in range(len(lst))]
mini_sample = [0]*(len(lst)+1)
if period<0 or (period == 0 and iterations is None):
raise AttributeError("You must specify iterations or a positive period")
def shuffle_and_pick(lst, iterations):
mini = [0]*len(lst)
for _ in range(iterations):
random.shuffle(lst2)
skillset = set()
chosen_ones = []
idx = 0
fullset = True
# Breaks from the loop when all skillsets are found
while len(skillset) < K:
# No need to go further, we didn't find a better combination
if len(chosen_ones) >= len(mini):
fullset = False
break
before = len(skillset)
skillset.update(lst2[idx][0])
after = len(skillset)
if after > before:
# We append with the orginal index of the sublist
chosen_ones.append(lst2[idx][1])
idx += 1
if fullset:
mini = chosen_ones.copy()
return mini
# Estimates how many iterations we can do in the specified period
if iterations is None:
t0 = time.perf_counter()
mini_sample = shuffle_and_pick(lst, 1)
iterations = int(period / (time.perf_counter() - t0)) - 1
mini_result = shuffle_and_pick(lst, iterations)
if len(mini_sample)<len(mini_result):
return mini_sample, len(mini_sample)
else:
return mini_result, len(mini_result)