用Python解谜

用Python解谜,python,permutation,combinations,puzzle,itertools,Python,Permutation,Combinations,Puzzle,Itertools,我有一个难题,我想用Python解决它 谜题: 一个商人有一个40公斤的重量,他用在他的商店里。有一次,它掉了下来 从他手上摔下来,摔成4块。但令人惊讶的是,现在他 可称量1 kg至40 kg之间的任何重量 这四件 所以问题是,这4件衣服的重量是多少 现在我想用Python解决这个问题 我从拼图中得到的唯一限制是4块的总和是40。有了它,我可以过滤所有4个值的总和为40的集合 import itertools as it weight = 40 full = range(1,41) comb

我有一个难题,我想用Python解决它

谜题:

一个商人有一个40公斤的重量,他用在他的商店里。有一次,它掉了下来 从他手上摔下来,摔成4块。但令人惊讶的是,现在他 可称量1 kg至40 kg之间的任何重量 这四件

所以问题是,这4件衣服的重量是多少

现在我想用Python解决这个问题

我从拼图中得到的唯一限制是4块的总和是40。有了它,我可以过滤所有4个值的总和为40的集合

import itertools as it

weight = 40
full = range(1,41)
comb = [x for x in it.combinations(full,4) if sum(x)==40]
梳子长度=297

现在我需要检查
comb
中的每一组值,并尝试所有操作的组合

例如,如果
(a,b,c,d)
comb
中的第一组值,我需要检查
a,b,c,d,a+b,a-b

我尝试了很多,但我仍停留在这个阶段,即如何检查所有这些计算组合到每组4个值

问题:

1) 我想我需要得到一个列表,列出所有可能的
[a,b,c,d]和[+,-]
组合

2) 有没有人有更好的想法,告诉我如何从这里开始

另外,我想完全不用任何外部库的帮助,只需要使用python的标准库

编辑:抱歉,信息太晚了。它的答案是(1,3,9,27),这是我几年前发现的。我已经核实了答案

编辑:目前,
fraxel
的答案在
时间=0.16毫秒时运行良好。更好更快的方法总是受欢迎的

问候


ARK

这是一个强力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)

要了解它的工作原理,请查看,这是同一算法的实现。

您很接近,非常接近:)

既然这是一个你想解决的难题,我就给你一些提示。本部分:

如果(a,b,c,d)是comb中的第一组值,我需要检查 a、 b,c,d,a+b,a-b,……a+b+c-d,a-b+c+d。。。。。。。。等等

考虑一下:每个重量可以放在一个秤上,或者放在另一个秤上,或者两者都不放。因此,对于
a
的情况,这可以表示为
[a,-a,0]
。其他三个也一样。现在,您需要所有可能的配对,每个权重都有这3种可能(提示:
itertools.product
)。然后,一个配对的可能度量(比如:
(a,-b,c,0)
)仅仅是这些(
a-b+c+0
)的总和

剩下的只是检查你是否能“测量”所有需要的重量<代码>设置
可能在这里派上用场


PS:正如评论中所述,对于一般情况,可能没有必要区分这些划分的权重(对于这个问题是这样的)。你可以重新考虑一下itertools.compositions

我用蛮力逼出了第二部分

如果不想看到答案,请不要单击此按钮。显然,如果我更擅长排列,这将需要更少的剪切/粘贴搜索/替换:


前面的一个问题:

我们知道
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到9放在旁边,以说明我们有效地处于三元数系统中(以3为基数)。我们的解决方案始终可以写成:

3**0 + 3**1 +3**2 +...+ 3**N >= Weight
对于最小N,这是正确的。最小解决方案将始终采用这种形式

此外,我们可以轻松解决大重量的问题,并找到跨越空间的最小件数:

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]
一个人把一个已知的砝码掉下来,它就会碎裂成碎片。他的新砝码允许他称任何重量到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]

尝试对较大重量和未知数量的碎片使用排列

我不知道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)
  }
def设置为40(a:Int,b:Int,c:Int,d:Int)={
val vec=用于(

fa使用砝码[2,5,15,18],您也可以测量1到40kg之间的所有物体,尽管其中一些物体需要间接测量。例如,要测量一个重量为39kg的物体,您首先将其与40kg进行比较,然后天平将悬挂在40kg一侧(因为39<40),但如果你去掉2kg的重量,它会垂向另一侧(因为39>38),因此你可以得出物体重量为39kg的结论


更有趣的是,使用权重[2,5,15,45],您可以测量高达67kg的所有对象。

如果任何人不想导入库来导入组合/烫发,这将生成所有可能的四步移动策略

# 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()
#生成重复值的排列
def置换SWI
    #!/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()
#!/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')