我有一个谜题,我想用Python解决它。
益智:
商人的重量为40公斤,他在店里使用。有一次,它倒下了 从他的手,并分成4件。但令人惊讶的是,现在他 可以称重1 kg到40 kg之间的任何重量 这4件。
所以问题是,这4件的重量是多少?
现在我想用Python解决这个问题。
我从拼图中获得的唯一约束是4个总和是40.我可以过滤掉总和为40的所有4个值。
import itertools as it
weight = 40
full = range(1,41)
comb = [x for x in it.combinations(full,4) if sum(x)==40]
length of comb = 297
现在我需要检查comb
中的每组值并尝试所有操作组合。
例如,如果(a,b,c,d)
是comb
中的第一组值,我需要检查a,b,c,d,a+b,a-b, .................a+b+c-d,a-b+c+d........
,依此类推。
我尝试了很多,但我在这个阶段陷入困境,即如何将所有这些计算组合检查到每组4个值。
问题:
1)我想我需要列出[a,b,c,d] and [+,-]
的所有可能组合的列表。
2)有没有人有更好的想法,告诉我如何从这里前进?
另外,我想完全没有任何外部库的帮助,只需要使用python的标准库。
编辑:对于迟到的信息感到抱歉。答案是(1,3,9,27),这是我几年前发现的。我检查并验证了答案。
编辑:目前,fraxel
的答案与time = 0.16 ms
完美配合。总是欢迎更好,更快的方法。
此致
ARK
答案 0 :(得分:24)
早些时候的漫游:
对于0到40之间的所有a*A + b*B + c*C + d*D = x
,我们知道x
,a, b, c, d
仅限于-1, 0, 1
。显然A + B + C + D = 40
。下一个案例是x = 39
,所以显然最小的举动是移除一个元素(这是唯一可能导致成功平衡39的可能移动):
A + B + C = 39
,所以D = 1
,是必要的。
下:
A + B + C - D = 38
下:
A + B + D = 37
,所以C = 3
然后:
A + B = 36
然后:
A + B - D = 35
A + B - C + D = 34
A + B - C = 33
A + B - C - D = 32
A + C + D = 31
,所以A = 9
因此B = 27
因此权重为1, 3, 9, 27
实际上,这可以立即推断出它们必须都是3的倍数。
有趣的更新:
所以这里有一些python代码,可以找到跨越空间的任何掉落权重的最小权重集:
def find_weights(W):
weights = []
i = 0
while sum(weights) < W:
weights.append(3 ** i)
i += 1
weights.pop()
weights.append(W - sum(weights))
return weights
print find_weights(40)
#output:
[1, 3, 9, 27]
为了进一步说明这种解释,可以将问题视为跨越数字空间[0, 40]
的最小权重数。很明显,每个重量可以做的事情是三元/三元(增加重量,减轻重量,在另一侧增加重量)。因此,如果我们按降序编写我们的(未知)权重(A, B, C, D)
,我们的行动可以概括为:
ABCD: Ternary:
40: ++++ 0000
39: +++0 0001
38: +++- 0002
37: ++0+ 0010
36: ++00 0011
35: ++0- 0012
34: ++-+ 0020
33: ++-0 0021
32: ++-- 0022
31: +0++ 0100
etc.
我已将三元计数从0到9并列,以说明我们实际上处于三元数系统(基数为3)。我们的解决方案始终可以写成:
3**0 + 3**1 +3**2 +...+ 3**N >= Weight
对于最小N,这是正确的。最小的解决方案总是这种形式。
此外,我们可以轻松解决大重量的问题,并找到跨越空间的最小件数:
一名男子掉落一个已知重量的W,它会碎成碎片。他的新重量允许他称重任何重量达到W。有多少重量,它们是什么?
#what if the dropped weight was a million Kg:
print find_weights(1000000)
#output:
[1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049, 177147, 531441, 202839]
尝试使用大量重量和未知数量的排列!!
答案 1 :(得分:8)
这是一个蛮力的itertools解决方案:
import itertools as it
def merchant_puzzle(weight, pieces):
full = range(1, weight+1)
all_nums = set(full)
comb = [x for x in it.combinations(full, pieces) if sum(x)==weight]
funcs = (lambda x: 0, lambda x: x, lambda x: -x)
for c in comb:
sums = set()
for fmap in it.product(funcs, repeat=pieces):
s = sum(f(x) for x, f in zip(c, fmap))
if s > 0:
sums.add(s)
if sums == all_nums:
return c
>>> merchant_puzzle(40, 4)
(1, 3, 9, 27)
有关其工作原理的说明,请查看the answer Avaris gave,这是同一算法的实现。
答案 2 :(得分:4)
你很近,非常接近:)。
由于这是一个你想要解决的难题,我只想指点一下。对于这部分:
例如,如果(a,b,c,d)是梳子中的第一组值,我需要检查 a,b,c,d,a + b,ab,................. a + b + cd,a-b + c + d ...... ..等等。
考虑一下:每个重量可以放在一个刻度上,另一个或两个都没有。因此,对于a
的情况,这可以表示为[a, -a, 0]
。与其他三个相同。现在,您需要为每个重量提供所有这三种可能性的配对(提示:itertools.product
)。然后,对配对的可能测量(假设:(a, -b, c, 0)
)仅仅是这些(a-b+c+0
)的总和。
剩下的只是检查您是否可以“测量”所有必需的重量。 set
可能会派上用场。
itertools.combinations
。
答案 3 :(得分:2)
答案 4 :(得分:0)
我不懂Python语法,但也许你可以解码这个Scala代码;从第二个for循环开始:
def setTo40 (a: Int, b: Int, c: Int, d: Int) = {
val vec = for (
fa <- List (0, 1, -1);
fb <- List (0, 1, -1);
fc <- List (0, 1, -1);
fd <- List (0, 1, -1);
prod = fa * a + fb * b + fc * c + fd * d;
if (prod > 0)
) yield (prod)
vec.toSet
}
for (a <- (1 to 9);
b <- (a to 14);
c <- (b to 20);
d = 40-(a+b+c)
if (d > 0)) {
if (setTo40 (a, b, c, d).size > 39)
println (a + " " + b + " " + c + " " + d)
}
答案 5 :(得分:0)
使用砝码[2,5,15,18],您还可以测量1到40公斤之间的所有物体,尽管其中一些物体需要间接测量。例如,要测量一个重量为39千克的物体,你首先将其与40千克进行比较,天平将与40千克侧面相比(因为39 <40),但是如果你移除2千克重物,它会挂在另一侧(因为39> 38)因此你可以得出39千克的物体重量。
更有趣的是,使用重量[2,5,15,45],您可以测量最大67公斤的所有物体。
答案 6 :(得分:0)
如果有人不想导入库来导入组合/烫发,这将产生所有可能的4移动策略......
# generates permutations of repeated values
def permutationsWithRepeats(n, v):
perms = []
value = [0] * n
N = n - 1
i = n - 1
while i > -1:
perms.append(list(value))
if value[N] < v:
value[N] += 1
else:
while (i > -1) and (value[i] == v):
value[i] = 0
i -= 1
if i > -1:
value[i] += 1
i = N
return perms
# generates the all possible permutations of 4 ternary moves
def strategy():
move = ['-', '0', '+']
perms = permutationsWithRepeats(4, 2)
for i in range(len(perms)):
s = ''
for j in range(4):
s += move[perms[i][j]]
print s
# execute
strategy()
答案 7 :(得分:0)
我的解决方法如下:
#!/usr/bin/env python3
weight = 40
parts = 4
part=[0] * parts
def test_solution(p, weight,show_result=False):
cv=[0,0,0,0]
for check_weight in range(1,weight+1):
sum_ok = False
for parts_used in range(2 ** parts):
for options in range(2 ** parts):
for pos in range(parts):
pos_neg = int('{0:0{1}b}'.format(options,parts)[pos]) * 2 - 1
use = int('{0:0{1}b}'.format(parts_used,parts)[pos])
cv[pos] = p[pos] * pos_neg * use
if sum(cv) == check_weight:
if show_result:
print("{} = sum of:{}".format(check_weight, cv))
sum_ok = True
break
if sum_ok:
continue
else:
return False
return True
for part[0] in range(1,weight-parts):
for part[1] in range(part[0]+1, weight - part[0]):
for part[2] in range( part[1] + 1 , weight - sum(part[0:2])):
part[3] = weight - sum(part[0:3])
if test_solution(part,weight):
print(part)
test_solution(part,weight,True)
exit()
它为您提供给定权重的所有解决方案
答案 8 :(得分:0)
比我之前的答案更具动态性,因此它也适用于其他数字。但是分成 5 个和平需要一些时间:
#!/usr/bin/env python3
weight = 121
nr_of_parts = 5
# weight = 40
# nr_of_parts = 4
weight = 13
nr_of_parts = 3
part=[0] * nr_of_parts
def test_solution(p, weight,show_result=False):
cv=[0] * nr_of_parts
for check_weight in range(1,weight+1):
sum_ok = False
for nr_of_parts_used in range(2 ** nr_of_parts):
for options in range(2 ** nr_of_parts):
for pos in range(nr_of_parts):
pos_neg = int('{0:0{1}b}'.format(options,nr_of_parts)[pos]) * 2 - 1
use = int('{0:0{1}b}'.format(nr_of_parts_used,nr_of_parts)[pos])
cv[pos] = p[pos] * pos_neg * use
if sum(cv) == check_weight:
if show_result:
print("{} = sum of:{}".format(check_weight, cv))
sum_ok = True
break
if sum_ok:
continue
else:
return False
return True
def set_parts(part,position, nr_of_parts, weight):
if position == 0:
part[position] = 1
part, valid = set_parts(part,position+1,nr_of_parts,weight)
return part, valid
if position == nr_of_parts - 1:
part[position] = weight - sum(part)
if part[position -1] >= part[position]:
return part, False
return part, True
part[position]=max(part[position-1]+1,part[position])
part, valid = set_parts(part, position + 1, nr_of_parts, weight)
if not valid:
part[position]=max(part[position-1]+1,part[position]+1)
part=part[0:position+1] + [0] * (nr_of_parts - position - 1)
part, valid = set_parts(part, position + 1, nr_of_parts, weight)
return part, valid
while True:
part, valid = set_parts(part, 0, nr_of_parts, weight)
if not valid:
print(part)
print ('No solution posible')
exit()
if test_solution(part,weight):
print(part,' ')
test_solution(part,weight,True)
exit()
else:
print(part,' ', end='\r')