用 Python 解决难题
Posted
技术标签:
【中文标题】用 Python 解决难题【英文标题】:Solving Puzzle in Python 【发布时间】:2012-05-10 09:51:54 【问题描述】:我有一个难题,我想用 Python 解决它。
谜题:
一位商人有一个 40 公斤的重量,他在他的商店中使用。有一次,掉了 从他的手中,被分成了4块。但令人惊讶的是,现在他 可以称重 1 公斤到 40 公斤之间的任何重量 这 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
完美搭配。更好更快的方法总是受欢迎的。
问候
方舟
【问题讨论】:
这个谜题比这更棘手;我不确定您是否可以轻松地暴力破解它。诀窍是,为了测量某些重量,他可能需要在秤的两侧添加重量的碎片。考虑一个更简单的版本:将一个 4 公斤重的砝码分成 2 块,可以测量到 4 公斤以下的任何重量。答案是 1 公斤件和 3 公斤件。要测量 2 公斤,您必须在秤的每一侧放一块。 @JacobM 有最好的方法:从一个更简单的问题开始,看看你是否找不到可以解决更复杂问题的模式。另外,请注意,除非您确定每个重量都是独一无二的,否则组合不会给您想要的。 (要看到这一点,请尝试将 weight 更改为 10 并将 full 更改为 range(1,10)。更容易玩弄它的功能。) @JacobM...是的..当然..即问题。您可以在秤的两侧放置砝码以获得所需的重量。即我在问题中提到了negative sign
。即a-b, a-b+c-d ....
。 minus
表示重量放在其他秤上。我想我必须有问题地解释它。感谢您的通知。
阅读问题 andrew,他想用 Python 解决它,我们是谁来阻止他!
好的,我删除了我的评论,以防它是一个不需要的“剧透”(对不起)。如果您想知道如何写下答案,请给我发电子邮件。
【参考方案1】:
之前的演练答案:
我们知道a*A + b*B + c*C + d*D = x
代表介于 0 和 40 之间的所有 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]
尝试对大重量和未知数量的碎片使用排列!
【讨论】:
A + B + C = 39,因此 D = 1,这是必要的。我不太确定。如果你有 14 和 15 的权重,你可以使用这 2 个来做 1,每边一个,因为 15-14 = 1。所以不确定必要性是正确的词。 @jgritty - 但你不能得到 39!如果你想称重 39 的东西,你需要 3 个元素:) 没关系,我有 4 个!但是,您显然最终会遇到问题。 @jgritty - 呃不,因为上面的方法解决了问题:1,3,9,27 :) @Abid Rahman K - 干杯,:) 我将通过一些代码和更多解释为这个答案提供一个非常有趣的更新。这是一个非常有趣的问题。使用这个方法可以让你轻松解决问题:`一个人放下一个 1000000 Kg 的重物,他的新重物可以让他称重到 1000000 Kg 的任何重量!它们是什么,有多少?【参考方案2】:这是一个蛮力的 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,这是相同算法的实现。
【讨论】:
哇!当我在写解释时,你在编写同样的代码:)。【参考方案3】:你很接近,非常接近:)。
既然这是你想解决的难题,我就给点指点。对于这部分:
例如,如果 (a,b,c,d) 是梳子中的第一组值,我需要检查 a,b,c,d,a+b,ab, ........a+b+cd,a-b+c+d...... ..等等。
考虑一下:每个重量都可以放在一个秤上,另一个秤上,或者两者都没有。所以对于a
的情况,这可以表示为[a, -a, 0]
。其他三个也一样。现在,您需要所有可能的配对以及每种重量的这 3 种可能性(提示:itertools.product
)。然后,对配对的可能测量(假设:(a, -b, c, 0)
)仅仅是这些(a-b+c+0
)的总和。
剩下的就是检查您是否可以“测量”所有需要的重量。 set
在这里可能会派上用场。
PS:正如 cmets 中所述,对于一般情况,这些划分的权重可能没有必要区分(对于这个问题)。你可以重新考虑itertools.combinations
。
【讨论】:
+1 -this can be represented as [a, -a, 0]
。我没有想到这个技巧。那个单一的陈述就是转折点。谢谢。【参考方案4】:
第二部分我是蛮拼的。
如果您不想看到答案,请不要单击此按钮。显然,如果我更擅长排列,这将需要更少的剪切/粘贴搜索/替换:
http://pastebin.com/4y2bHCVr
【讨论】:
天哪,我不敢相信你写了这么多行!它适用于time = 0.185 s
。【参考方案5】:
我不知道 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)
【讨论】:
【参考方案6】:使用砝码 [2, 5, 15, 18],您还可以测量 1 到 40 公斤之间的所有物体,尽管其中一些需要间接测量。例如,要测量一个重 39 公斤的物体,您首先将其与 40 公斤进行比较,然后天平将悬垂到 40 公斤的一侧(因为 39 38),因此您可以得出物体重量为 39 公斤。
更有趣的是,使用 [2, 5, 15, 45] 砝码,您可以测量重量不超过 67 公斤的所有物体。
【讨论】:
您好,谢谢您的回答。我的问题不是要找到答案,而是以编程方式解决它,尤其是使用 python。 没关系,但请注意,编写程序来找到这个答案稍微复杂一些,因为您必须考虑间接测量。【参考方案7】:如果有人不想导入库来导入连击/烫发,这将生成所有可能的 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()
【讨论】:
【参考方案8】:我的解决方法如下:
#!/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:01b'.format(options,parts)[pos]) * 2 - 1
use = int('0:01b'.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()
它为您提供给定权重的所有解决方案
【讨论】:
【参考方案9】:比我之前的答案更有活力,因此它也适用于其他数字。但是分成 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:01b'.format(options,nr_of_parts)[pos]) * 2 - 1
use = int('0:01b'.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')
【讨论】:
以上是关于用 Python 解决难题的主要内容,如果未能解决你的问题,请参考以下文章
python 文本处理难题。 只能采用Python解决。要求最优效率最高解法,谢谢。