在Python中解决难题

时间:2012-04-30 17:40:51

标签: python permutation combinations puzzle itertools

我有一个谜题,我想用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

9 个答案:

答案 0 :(得分:24)

早些时候的漫游:

对于0到40之间的所有a*A + b*B + c*C + d*D = x,我们知道xa, 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可能会派上用场。

PS:正如评论中所述,对于一般情况,这些划分的权重可能没有必要是不同的(对于这个问题)。您可能会重新考虑itertools.combinations

答案 3 :(得分:2)

我粗暴地迫害了第二部分。

如果您不想看到答案,请不要单击此项。显然,如果我在排列方面做得更好,这将需要更少的剪切/粘贴搜索/替换:

http://pastebin.com/4y2bHCVr

答案 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')