注意:这是this problem的2倍变体形式
给出一个集合A
,该集合由介于0.0和1.0之间的浮点数组成,找到一个最小的集合B
,以便对于a
中的每个A
,都有一个值a == B[x]
,或者a == B[x] * B[y]
处有一对唯一值。
例如,给定
$ A = [0.125, 0.25, 0.5, 0.75, 0.9]
B的一种可能(但可能不是最小)的解决方案是
$ B = solve(A)
$ print(B)
[0.25, 0.5, 0.75, 0.9]
这满足了最初的问题,因为A[0] == B[0] * B[1]
,A[1] == B[1]
等使我们可以重新创建原始集合A
。 B
的长度比A
的长度小,但我猜答案也较小。
我假设B
的解决方案空间很大,如果不是无限的话。如果存在解决方案,将如何找到最小集B
?
注意:
答案 0 :(得分:2)
排序数组。对于每对元素Am,An∈A,m 检查该比率是否等于A中的某个元素,该元素既不等于Am也不等于An。 示例: 分子(0.125)是冗余(= 0.25 * 0.5)或(= 0.5 * 0.25) 我们可以通过引入新元素来做得更好: 另一个例子: 任意两对或多对具有相同比率f的(a1,a2),(a3,a4),(...,...)可用{a1,a3,...,f}替换。 因此,将我们的集合加0.5会使{0.1,0.11,0.12}成为多余。 我们现在(我是一般情况)面临一个优化问题,即选择要删除的元素中的哪些以及要添加的因素中的哪些,以最小化B的基数(我留给读者练习) )。 请注意,不需要引入大于1的数字。B也可以表示为{0.1,0.11,0.12,2},但是该集合具有相同的基数。A = { 0.125, 0.25, 0.5, 0.75, 0.9 }
(0.125, 0.25): 0.5 <--- bingo
(0.125, 0.5 ): 0.25 <--- bingo
(0.125, 0.75): 0.1(6)
(0.125, 0.9 ): 0.13(8)
(0.25 , 0.5 ): 0.5
(0.25 , 0.75): 0.(3)
(0.25 , 0.9 ): 0.2(7)
(0.5 , 0.75): 0.(6)
(0.5 , 0.9 ): 0.(5)
(0.75 , 0.9 ): 0.8(3)
A = { 0.1, 0.11, 0.12, 0.2, 0.22, 0.24 }
(0.1 , 0.11): 0.(90) ***
(0.1 , 0.12): 0.8(3) +++
(0.1 , 0.2 ): 0.5 <--------
(0.1 , 0.22): 0.(45)
(0.1 , 0.24): 0.41(6)
(0.11, 0,12): 0.91(6) ~~~
(0.11, 0.2 ): 0.55
(0.11, 0.22): 0.5 <--------
(0.11, 0.24): 0.458(3)
(0.12, 0.2 ): 0.6
(0.12, 0.22): 0.(54)
(0.12, 0.24): 0.5 <--------
(0.2 , 0.22): 0.(90) ***
(0.2 , 0.24): 0.8(3) +++
(0.22. 0.24): 0.91(6) ~~~
B = (0.2, 0.22, 0.24, 0.5}
答案 1 :(得分:0)
Google's OR-Tools提供了一个不错的CP solver,可用于获取解决方案。您可以将问题编码为一组简单的布尔变量,说明哪些变量或变量组合有效。
我首先拉入库的相关部分并设置一些变量:
from ortools.sat.python import cp_model
A = [0.125, 0.25, 0.5, 0.75, 0.9]
# A = [0.1, 0.11, 0.12, 0.2, 0.22, 0.24]
model = cp_model.CpModel()
然后我们可以定义一些帮助程序函数以根据我们的数字创建变量:
vars = {}
def get_var(val):
assert val >= 0 and val <= 1
if val in vars:
return vars[val]
var = model.NewBoolVar(str(val))
vars[val] = var
return var
pairs = {}
def get_pair(pair):
if pair in pairs:
return pairs[pair]
a, b = pair
av = get_var(a)
bv = get_var(b)
var = model.NewBoolVar(f'[{a} * {b}]')
model.AddBoolOr([av.Not(), bv.Not(), var])
model.AddImplication(var, av)
model.AddImplication(var, bv)
pairs[pair] = var
return var
即get_var(0.5)
将创建一个布尔变量(带有Name='0.5'
),而get_pair(0.5, 0.8)
将创建一个变量并设置约束,以便仅在0.5和0.8均为true时才成立。关于编码boolean logic in ortools
然后我们可以进行A
以确定哪些组合是有效的,并将它们添加为求解器的约束:
for i, a in enumerate(A):
opts = {(a,)}
for a2 in A[i+1:]:
assert a < a2
m = a / a2
if m == a2:
opts.add((m,))
elif m < a2:
opts.add((m, a2))
else:
opts.add((a2, m))
alts = []
for opt in opts:
if len(opt) == 1:
alts.append(get_var(*opt))
else:
alts.append(get_pair(opt))
model.AddBoolOr(alts)
接下来,我们需要一种说法,我们更喜欢变量为false而不是true。最小的版本是:
model.Minimize(sum(vars.values()))
但是,如果我们稍微复杂一点,并优先考虑A
中的值,我们会得到更好的结果:
costsum = 0
for val, var in vars.items():
cost = 1000 if val in A else 1001
costsum += var * cost
model.Minimize(costsum)
最后,我们可以运行求解器并打印出解决方案:
solver = cp_model.CpSolver()
status = solver.Solve(model)
print(solver.StatusName(status))
if status in {cp_model.FEASIBLE, cp_model.OPTIMAL}:
B = [val for val, var in vars.items() if solver.Value(var)]
print(sorted(B))
这给了我预期的以下结果:
[0.125, 0.5, 0.75, 0.9]
和[0.2, 0.22, 0.24, 0.5]
顶部的两个例子
您还可以对以下事实进行编码:您只有在解算器中使用|B| < |A|
时,才认为解决方案有效,但是我很想在外部进行这项工作