gpt4 book ai didi

python - 在保持最小频率的同时减小集合大小

转载 作者:太空狗 更新时间:2023-10-29 22:15:53 24 4
gpt4 key购买 nike

假设我有以下集合:

{(2,), (3,), (1, 4), (1, 2, 3), (2, 3), (3, 4), (2, 4)}

这为每个数字提供了以下频率:

2: 4, 3: 4, 4: 3, 1: 2

您能否提出一种减少集合的方法,使每个数字至少出现 2 次,但集合中的元组数量减少到最少?

例如,可以从集合中删除元组 (3, 4),给出这些频率:

2: 4, 3: 3, 4: 2, 1: 2

这是我解决这个问题的非常微弱的尝试:

def reduce(a, limit):
while True:
remove = None
for i in a:
c = Counter([i for s in a for i in s])

if c.most_common(1)[0][0] in i:
if min([c[j] for j in i]) > limit:
remove = i
break

if remove:
a.remove(remove)
else:
break

reduce(a, 2) # we want at least two of each number

这个解决方案的问题是它可能会减少集合,但不一定会留下尽可能小的集合。

对于我的特定示例,我希望减少的集合包含字符串,可以这样说:

a = [("一","八十一","三"), ("八十五","六十一","三", "十一"), ...]其中a的长度为1000。a中每个元组的长度为3到9。元组可以组成的唯一值有100个,例如, “一”就是一个这样的值。我希望每个唯一值在我减少集合后至少出现 25 次。 PC 计算缩减集可能需要多长时间?我们说的是几秒钟还是几分钟?

最佳答案

如评论中所述,NP-hard 问题 Set Cover 是一个特殊的问题这个问题的最小频率是 k = 1 的情况,使得这个问题 NP-hard 也是如此。我会推荐像这样的图书馆 PuLP具有以下整数程序。

minimize sum over tuples T of x(T)
subject to
y(e): for all elements e, (sum over tuples T of (count of e in T) * x(T)) >= k
z(T): for all tuples T, x(T) in {0, 1}

PuLP 的一个缺点是它需要一个外部求解器。我曾是然而,我想破解,所以我写了一个(经过非常轻微测试的)纯Python 求解器。它使用深度优先搜索和最佳优先回溯,使用简单的传播策略来确定哪些元组必须或不得选择和基于原始对偶的启发式函数前一个程序的以下对偶的近似值(所以它是复杂的玩具,但仍然是玩具)。

maximize (sum over elements e of k * y(e)) - (sum over tuples T of z(T))
subject to
x(T): for all tuples T, (sum over elements e in T of y(e)) - z(T) <= 1
for all elements e, y(e) >= 0
for all tuples T, z(T) >= 0

原始对偶策略是以相同的速率增加那些值y 增加的不需要无利可图的相应增加在 z 中。

from collections import Counter, defaultdict, namedtuple
from fractions import Fraction
from heapq import heappop, heappush
from math import ceil
from operator import itemgetter


class _BestFirstSearchDepthFirstBacktracking:
def optimize(self):
node = self._make_root_node()
heap = []
upper_bound = None
while True:
lower_bound = ceil(node.lower_bound)
if upper_bound is None or lower_bound < upper_bound:
child_nodes = list(self._make_child_nodes(node))
if child_nodes:
i, node = min(enumerate(child_nodes), key=itemgetter(1))
del child_nodes[i]
for child_node in child_nodes:
heappush(heap, child_node)
continue
upper_bound = lower_bound
solution = node
if not heap:
return (upper_bound, solution)
node = heappop(heap)


Node = namedtuple('Node', ('lower_bound', 'index', 'maybes', 'yeses', 'variable'))


class UnsolvableException(Exception):
pass


class _Optimizer(_BestFirstSearchDepthFirstBacktracking):
def __init__(self, tuples, min_freq):
self._index = 0
self._tuples = set(tuples)
self._min_freq = min_freq
self._elements = set()
for t in self._tuples:
self._elements.update(t)

def _propagate(self, maybes, yeses):
upper_count = Counter()
for t in maybes:
upper_count.update(t)
for t in yeses:
upper_count.update(t)
if any(upper_count[e] < self._min_freq for e in self._elements):
raise UnsolvableException()
forced_yeses = set()
forced_yeses = {t for t in maybes if any(upper_count[e] - k < self._min_freq for e, k in Counter(t).items())}
maybes = maybes - forced_yeses
yeses = yeses | forced_yeses
lower_count = Counter()
for t in yeses:
lower_count.update(t)
residual = {e for e in self._elements if lower_count[e] < self._min_freq}
maybes = {t for t in maybes if any(e in residual for e in t)}
return (maybes, yeses)

def _compute_heuristic(self, maybes, yeses):
lower_count = Counter()
for t in yeses:
lower_count.update(t)
residual_count = {e: max(self._min_freq - lower_count[e], 0) for e in self._elements}
y = defaultdict(int)
z = defaultdict(int)
variable = None
while True:
slack = {t: 1 + z[t] - sum(y[e] for e in t) for t in maybes}
assert all(s >= 0 for s in slack.values())
inactive_maybes = {t for t, s in slack.items() if s > 0}
if not inactive_maybes:
break
active_maybes = {t for t, s in slack.items() if s == 0}
active_count = Counter()
for t in active_maybes:
active_count.update(t)
dy = {e: 1 for e, k in residual_count.items() if active_count[e] < k}
if not dy:
break
delta_inverse, variable = max(((Fraction(sum(dy.get(e, 0) for e in t), slack[t]), t) for t in inactive_maybes), key=itemgetter(0))
delta = Fraction(1, delta_inverse)
for e, dy_e in dy.items():
y[e] += delta * dy_e
for t in active_maybes:
z[t] += delta * sum(dy.get(e, 0) for e in t)
return (sum(residual_count[e] * y_e for e, y_e in y.items()) - sum(z.values()), variable)

def _make_node(self, maybes, yeses):
maybes, yeses = self._propagate(maybes, yeses)
heuristic, variable = self._compute_heuristic(maybes, yeses)
node = Node(len(yeses) + heuristic, self._index, maybes, yeses, variable)
self._index += 1
return node

def _make_root_node(self):
return self._make_node(self._tuples, set())

def _make_child_nodes(self, node):
if node.variable is None:
return
variable = {node.variable}
maybes = node.maybes - variable
yield self._make_node(maybes, node.yeses)
yield self._make_node(maybes, node.yeses | variable)


def optimize(tuples, min_freq):
optimizer = _Optimizer(tuples, min_freq)
node = optimizer.optimize()[1]
print('Nodes examined:', optimizer._index)
return node.yeses


print(optimize({(2,), (3,), (1, 4), (1, 2, 3), (2, 3), (3, 4), (2, 4)}, 2))
print(optimize({(1, 2, 3, 4, 5, 6, 7), (8, 9, 10, 11, 12, 13, 14), (1, 2, 3, 4, 8, 9, 10, 11), (5, 6, 12, 13), (7, 14)}, 1))

关于python - 在保持最小频率的同时减小集合大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25801269/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com