web-dev-qa-db-ja.com

パズルを解くPython

パズルが1つあり、Pythonを使用して解決したいと思います。

パズル:

商人は彼が彼の店で使用した40kgの重りを持っています。かつて、それは彼の手から落ちて、4つの部分に砕かれました。しかし、驚くべきことに、彼はこれらの4つの部品の組み合わせで、1kgから40kgまでの任意の重量を量ることができます。

だから問題は、それらの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の標準ライブラリのみを使用する必要があります。

[〜#〜] edit [〜#〜]:情報が遅れてすみません。その答えは(1,3,9,27)で、数年前に見つけました。答えを確認して確認しました。

編集:現在、fraxelの答えはtime = 0.16 msで完璧に機能します。より良い、より速いアプローチはいつでも歓迎です。

よろしく

アーク

18
Abid Rahman K

以前のウォークスルー回答:

0から40までのすべてのxに対してa*A + b*B + c*C + d*D = 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]にまたがる重みの最小数と見なすことができます。各ウェイトで実行できることの数が3値/ 3値であることは明らかです(ウェイトを追加、ウェイトを削除、反対側にウェイトを配置)。したがって、(不明な)重み(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.

事実上3進法(基数3)になっていることを示すために、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]

重量が大きく、ピースの数が不明な場合は、順列を使用してみてください。

24
fraxel

これがブルートフォースの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)

それがどのように機能するかの説明については、 Avarisが与えた答え をチェックしてください。これは同じアルゴリズムの実装です。

8
Andrew Clark

あなたは近くにいます、とても近くにいます:)。

これはあなたが解きたいパズルなので、私はただポインタを与えます。この部分の場合:

たとえば、(a、b、c、d)がcombの最初の値のセットである場合、a、b、c、d、a + b、ab、...........をチェックする必要があります。 ..... a + b + cd、a-b + c + d ........など。

これを考慮してください:各ウェイトは、一方のスケール、もう一方のスケール、またはどちらにも配置できません。したがって、aの場合、これは[a, -a, 0]として表すことができます。他の3つと同じです。ここで、重みごとにこれら3つの可能性を持つすべての可能なペアリングが必要です(ヒント:itertools.product)。次に、ペアリングの可能な測定値(たとえば、(a, -b, c, 0))は、これらの合計(a-b+c+0)にすぎません。

残っているのは、必要なすべての重みを「測定」できるかどうかを確認することだけです。 setはここで便利かもしれません。

PS:コメントで述べられているように、一般的なケースでは、これらの分割された重みを区別する必要はないかもしれません(この問題の場合はそうです)。 itertools.combinationsを再考するかもしれません。

4
Avaris

私は2番目の部分から地獄を総当たり攻撃しました。

答えを見たくない場合は、これをクリックしないでください。明らかに、順列が得意であれば、カット/ペーストの検索/置換がはるかに少なくて済みます。

http://Pastebin.com/4y2bHCVr

2
jgritty

Python構文はわかりませんが、これをデコードできるかもしれませんScalaコード; 2番目の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)
  }
0
user unknown

コンボ/パーマをインポートするためにライブラリをインポートしたくない場合は、これにより、考えられるすべての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()
0
user3821369

ウェイト[2、5、15、18]を使用すると、1〜40kgのすべてのオブジェクトを測定することもできますが、一部のオブジェクトは間接的に測定する必要があります。たとえば、39kgの重さの物体を測定するには、最初に40kgと比較し、天びんは40kg側にぶら下がっています(39 <40であるため)が、2kgの重りを取り除くと、反対側にぶら下がっています( 39> 38)したがって、オブジェクトの重量は39kgであると結論付けることができます。

さらに興味深いことに、ウェイト[2、5、15、45]を使用すると、67kgまでのすべてのオブジェクトを測定できます。

0
Luís Pureza