小さくバランスの取れたモバイルを構築する


18

あなたにはたくさんの重みが与えられ、あなたの仕事はそれらの重みを使って小さなバランスの取れたモバイルを構築することです。

入力は、1〜9の範囲の整数の重みのリストです。重複する可能性があります。

出力は、吊り下げたときにバランスがとれるモバイルのASCII画像です。おそらく、例によって最もよく示されています:

入力

3 8 9 7 5

可能な出力

         |
   +-----+---------+
   |               |
+--+-+        +----+------+
|    |        |           |
8   ++--+     7           5
    |   |
    9   3

示されているようにASCII文字を使用する必要があります。水平および垂直セグメントの長さは任意です。モバイルのどの部分も、モバイルの接続されていない別の部分に(水平または垂直に)触れることはできません。すべてのウェイトは、少なくとも1の長さの垂直セグメントから吊るす必要があり、モバイル全体を吊るす垂直セグメントが必要です。

モバイルの大きさは、数の合計である+-|の文字がそれを構築するために必要。サイズが小さいほど優れています。

セグメントには、必要なだけ接続を配置できます。例えば:

入力

2 3 3 5 3 9

可能な出力

           |
   +---+---+-----------+
   |   |               |
+--+-+ 5               9
|  | |
2  | 3
   |
  +++
  | |
  3 3

勝者プログラムは、入力のテストセットに対してモバイルサイズの最小平均を生成できるものです。実際のテストはハードコーディングを防ぐために極秘ですが、次のようなものになります。

8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 7
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 7 7
3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7

物理学も関与していますか?
あなた

1
@ S.Mark:それは言えると思います。身体障害者の場合、合計はtotal_weight_hung_from_point * distance_of_point_from_pivotピボットポイントの両側で同じでなければなりません。
キースランドール

おそらく、ダイアグラムの検査を容易にするために、1つのバーが約2つのハイフンに等しくなるようにしますか?現状では、ダイアグラムはバランスが崩れています。
トーマスO

回答:


5

Python 2。

私は少し浮気してます:

  • 私は1つの水平方向のモバイルのみを構築します。私が感じている(しかし、私はそれを証明されていない)与えられた条件の下で最適な携帯電話は、実際に常にことないだけ水平ものを持っています。 編集:常に正しいとは限りません。2 2 9 1Nabbは以下のコメントに反例を発見しました。

    Size 18:                Size 16:
       |                        |
    +-++--+-----+            +--++-+
    | |   |     |            |   | |
    2 9   2     1           -+-  9 1
                            | |
                            2 2
    
  • 私はただ愚かなブルートフォーシングを行います:

    1. 指定されたウェイトはランダムにシャッフルされます。
    2. バランスが保たれるように、一度に2つのウェイトが最適な位置に配置されます。
    3. 結果のモバイルが以前よりも優れている場合は、覚えておいてください。
    4. 事前に定義された秒数が経過するまで、すすぎを繰り返します。

サンプル入力の私の結果。それぞれが5秒間実行されました(これは小さなものにとってはばかげていることを知っています-可能なすべての順列を通過するだけで速くなります)。ランダムな要素があるため、その後の実行でより良いまたはより悪い結果が見つかる場合があることに注意してください。

3 8 9 7 5
Tested 107887 mobiles, smallest size 20:
        |
+-+-----+-+--+
| |     | |  |
5 3     7 9  8

2 3 3 5 3 9
Tested 57915 mobiles, smallest size 23:
      |
+--+-++--+-+---+
|  | |   | |   |
3  5 9   3 3   2

8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 7
Tested 11992 mobiles, smallest size 50:
                |
+-+-+-+--+-+-+-+++-+-+--+-+-+-+-+
| | | |  | | | | | | |  | | | | |
8 8 8 8  8 8 8 8 8 8 8  7 8 8 8 8

1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 7 7
Tested 11119 mobiles, smallest size 62:
                    |
+-+-+-+-+-+--+-+-+-+++-+-+-+--+-+-+-+-+-+
| | | | | |  | | | | | | | |  | | | | | |
2 7 5 6 6 8  3 2 3 7 9 7 8 1  1 7 9 5 4 4

3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7
Tested 16301 mobiles, smallest size 51:
                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |
4 6 5 7 7 4 6 5 3 5 6 4 7 6 7 5 4

コード(これはゴルフのコードではないため、冗長です):

import time, random

def gcd(a, b):
    while b > 0:
        a, b = b, a % b
    return a

class Mobile(object):
    def __init__(self):
        self.contents = [None];
        self.pivot = 0;

    def addWeights(self, w1, w2):
        g = gcd(w1, w2)
        m1 = w2 / g
        m2 = w1 / g
        mul = 0
        p1 = -1
        while True:
            if p1 < 0:
                mul += 1
                p1 = mul * m1
                p2 = -mul * m2
            else:
                p1 *= -1
                p2 *= -1
            if self.free(p1) and self.free(p2):
                self.add(w1, p1)
                self.add(w2, p2)
                return

    def add(self, w, pos):
        listindex = self.pivot - pos 
        if listindex < 0:
            self.contents = [w] + (abs(listindex) - 1) * [None] + self.contents
            self.pivot += abs(listindex)
        elif listindex >= len(self.contents):
            self.contents += (listindex - len(self.contents)) * [None] + [w]
        else:
            self.contents[listindex] = w

    def at(self, pos):
        listindex = self.pivot - pos
        if 0 <= listindex < len(self.contents):
            return self.contents[listindex]
        return None

    def free(self, pos):
        return all(self.at(pos + d) is None for d in (-1, 0, 1))

    def score(self):
        return 1 + 2 * len(self.contents) - self.contents.count(None)

    def draw(self):
        print self.pivot * " " + "|"
        print "".join("+" if c is not None or i == self.pivot else "-" for i, c in enumerate(self.contents))
        print "".join("|" if c is not None else " " for c in self.contents)
        print "".join(str(c) if c is not None else " " for c in self.contents)

    def assertBalance(self):
        assert sum((i - self.pivot) * (c or 0) for i, c in enumerate(self.contents)) == 0


weights = map(int, raw_input().split())

best = None
count = 0

# change the 5 to the number of seconds that are acceptable
until = time.time() + 5

while time.time() < until:
    count += 1
    m = Mobile()

    # create a random permutation of the weights
    perm = list(weights)
    random.shuffle(perm)

    if len(perm) % 2:
        # uneven number of weights -- place one in the middle
        m.add(perm.pop(), 0)

    while perm:
        m.addWeights(perm.pop(), perm.pop())

    m.assertBalance() # just to prove the algorithm is correct :)
    s = m.score()
    if best is None or s < bestScore:
        best = m
        bestScore = s

print "Tested %d mobiles, smallest size %d:" % (count, best.score())
best.draw()

@Nabb:9を超える重みは不可能です。1 9 2 8それが発生し1-------8+-9--2、私の頭の上から私はより良いものを思い付くことができません(しかし、私はそれに依存しません)-あなたは何かを持っていますか?
-balpha

1
@balpha:気にせず、先ほどコメントしたとき、まっすぐ考えていませんでした。何らかの理由で1-9と2-8に固定できると思いましたが、明らかにそれらのペア自体はバランスが取れていません!
ナブ

さて、実際には複数のレイヤーでより良いかもしれないものがあります:2 2 9 1すなわち、16サイズの代わりに(2 + 2)* 3 = 9 + 1 * 3の2-9+--2----118です。しきい値があると思います)その後、単一の水平行が常に最適です。
ナブ

@ナブ:うん; それは確かに良い反例です。
-balpha

@ Nabb、2-2-+9-113のスコアを持つ、残高のある単一のバー(4*2+2*2 = 9*1+1*3)。だから、これが良い反例だとは思わない。
キースランドール

1

さて、これは古い質問ですが、トップの質問タブに表示されるのを見たので、ここに私の(最適な)解決策があります:

#include <stdio.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>

int main(int argc, const char *const *argv) {
    if(argc < 2) {
        fprintf(stderr,
            "Balances weights on a hanging mobile\n\n"
            "Usage: %s <weight1> [<weight2> [...]]\n",
            argv[0]
        );
        return 1;
    }
    int total = argc - 1;
    int values[total];
    int maxval = 0;
    for(int n = 0; n < total; ++ n) {
        char *check = NULL;
        long v = strtol(argv[n+1], &check, 10);
        if(v <= 0 || v > INT_MAX || *check != '\0') {
            fprintf(stderr,
                "Weight #%d (%s) is not an integer within (0 %d]\n",
                n + 1, argv[n+1], INT_MAX
            );
            return 1;
        }
        values[n] = (int) v;
        if(values[n] > maxval) {
            maxval = values[n];
        }
    }
    int maxwidth = (int) log10(maxval) + 1;
    for(int n = 0; n < total; ++ n) {
        int width = (int) log10(values[n]) + 1;
        fprintf(stdout,
            "%*s\n%*d\n",
            (maxwidth + 1) / 2, "|",
            (maxwidth + width) / 2, values[n]
        );
    }
    return 0;
}

ルールを見ると、それはごまかしているように感じますが、だまされていないことは確かです。これは、2 * number_of_inputsの合計コストで、垂直チェーン内の指定されたすべての数値を出力します(レイアウトに関係なく各数値の上にバーが必要なため、これは最小限に抑えられます)。以下に例を示します。

./mobile 3 8 9 7 5

生産物:

|
3
|
8
|
9
|
7
|
5

もちろん、これは完璧なバランスです。


もともと私はこの挑戦の精神でもっと何かをしようとしていましたが、とにかくこの構造に最適化されていることがすぐに判明しました


おそらく私の説明からは明らかではありません|が、ウェイトの底に接続することはできません。
キースランドール

@KeithRandallああああ; それを念頭に置いて、これを適切に解決する必要があります。
デイブ

1

最小の単一行ソリューションをブルートフォースするソリューションを次に示します。コードはすべての順列を反復処理し、それぞれの重心を計算します。重心に整数座標がある場合、解決策が見つかりました。

すべての順列が試行された後、現在の重みセットでセグメントを混合に追加し(質量0の重みに相当)、再試行します。

プログラムを実行するには、を実行しますpython balance.py 1 2 2 4

#!/usr/bin/env python3
import itertools, sys

# taken from http://stackoverflow.com/a/30558049/436792
def unique_permutations(elements):
    if len(elements) == 1:
        yield (elements[0],)
    else:
        unique_elements = set(elements)
        for first_element in unique_elements:
            remaining_elements = list(elements)
            remaining_elements.remove(first_element)
            for sub_permutation in unique_permutations(remaining_elements):
                yield (first_element,) + sub_permutation

def print_solution(cm, values):
    print(('  ' * cm) + '|')
    print('-'.join(['-' if v == 0 else '+'  for v in values]))
    print(' '.join([' ' if v == 0 else '|'  for v in values]))
    print(' '.join([' ' if v == 0 else str(v) for v in values]))



input = list(map(int, sys.argv[1:]))
mass = sum(input)
while True:
    n = len(input)
    permutations = filter(lambda p: p[0] != 0 and p[n-1] != 0, unique_permutations(input))
    for p in permutations:
        cm = 0
        for i in range(n):
            cm += p[i] * i;
        if (cm % mass == 0):
            print_solution(cm//mass, p)
            sys.exit(0)
    input.append(0)

これらの最高のソリューションを生成します:

    |
+-+-+-+-+
| | | | |
8 3 9 5 7


    |
+-+-+-+-+-+
| | | | | |
9 2 3 5 3 3

                |
+-+-+-+-+-+-+---+-+-+-+-+-+-+-+-+
| | | | | | |   | | | | | | | | |
8 8 8 8 8 8 8   8 8 8 8 8 8 8 8 7


                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | |
1 1 2 2 3 3 4 4 8 8 5 5 6 6 7 7 7 7 9 9


                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |
3 4 4 4 4 5 5 5 5 6 7 6 7 7 7 6 6

0

Python 3

これは、どのテストケースでも1を超えて最適なものではなく、5秒以内に最適化されます。

基本的に、私はシングルバーアプローチを使用します。入力をランダムに並べてから、ウェイトを1つずつバーに挿入します。各要素は、前者の75%の時間と後者の25%の時間を使用して、両側の過剰重量を最小化する位置、またはその観点から2番目に良い位置に配置されます。次に、モバイルが最後にバランスが取れているかどうかを確認し、これまでに見つかった最高のモバイルよりも優れています。最適なものを保存し、5秒の検索後に停止して印刷します。

結果、5秒実行:

py mobile.py <<< '3 8 7 5 9'
Best mobile found, score 15:
    |    
+-+-+-+-+
| | | | |
8 7 3 5 9
py mobile.py <<< '2 2 1 9'
Best mobile found, score 13:
   |    
+-++-+-+
| |  | |
1 9  2 2
py mobile.py <<< '2 3 3 5 3 9'
Best mobile found, score 18:
      |    
+-+-+-+-+-+
| | | | | |
2 3 3 5 9 3
py mobile.py <<< '8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 7'
Best mobile found, score 49:
                |               
+-+--+-+-+-+-+-+++-+-+-+-+-+-+-+
| |  | | | | | | | | | | | | | |
7 8  8 8 8 8 8 8 8 8 8 8 8 8 8 8
\py mobile.py <<< '1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 7 7'
Best mobile found, score 61:
                    |                   
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+
| | | | | | | | | | | | | | | | | | |  |
1 7 7 5 4 3 1 9 6 7 8 2 2 9 3 7 6 5 8  4
py mobile.py <<< '3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7'
Best mobile found, score 51:
                |                
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |
4 4 6 7 7 4 5 7 6 6 5 4 6 3 5 5 7

コード:

import random
import time

class Mobile:
    def __init__(self):
        self.contents = {}
        self.lean = 0

    def usable(self, loc):
        return not any(loc + k in self.contents for k in (-1,0,1))
    def choose_point(self, w):
        def goodness(loc):
            return abs(self.lean + w * loc)
        gl = sorted(list(filter(self.usable,range(min(self.contents.keys() or [0]) - 5,max(self.contents.keys() or [0]) + 6))), key=goodness)
        return random.choice((gl[0], gl[0], gl[0], gl[1]))

    def add(self, w, loc):
        self.contents[loc] = w
        self.lean += w*loc

    def __repr__(self):
        width = range(min(self.contents.keys()), max(self.contents.keys()) + 1)
        return '\n'.join((''.join(' ' if loc else '|' for loc in width),
                          ''.join('+' if loc in self.contents or loc == 0 else '-' for loc in width),
                          ''.join('|' if loc in self.contents else ' ' for loc in width),
                          ''.join(str(self.contents.get(loc, ' ')) for loc in width)))

    def score(self):
        return max(self.contents.keys()) - min(self.contents.keys()) + len(self.contents) + 2

    def my_score(self):
        return max(self.contents.keys()) - min(self.contents.keys()) + 1

best = 1000000
best_mob = None
in_weights = list(map(int,input().split()))
time.clock()
while time.clock() < 5:
    mob = Mobile()
    for insert in random.sample(in_weights, len(in_weights)):
        mob.add(insert, mob.choose_point(insert))
    if not mob.lean:
        if mob.score() < best:
            best = mob.score()
            best_mob = mob

print("Best mobile found, score %d:" % best_mob.score())
print(best_mob)

私が信じているこれらの解決策の中で唯一最適とは言えないものは、最長の解決策です。

Best mobile found, score 60:
                   |                   
+-+-+-+-+-+-+-+-+-+++-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | |
3 2 9 4 7 8 1 6 9 8 7 1 6 2 4 5 7 3 5 7
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.