작고 균형 잡힌 모바일 구축


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경우 피벗 포인트의 양쪽면의 합계가 같아야합니다.
Keith Randall

다이어그램을보다 쉽게 ​​검사 할 수있게하려면 하나의 막대가 약 두 개의 하이픈과 같도록 만드십시오. 그대로, 다이어그램의 균형이 맞지 않습니다.
Thomas O

답변:


5

파이썬 2.

나는 약간의 바람을 피우고있다 :

  • 나는 하나의 수평으로 모바일을 구성합니다. 나는 느낌이 (하지만 난 그것을 입증되지 않은) 주어진 조건에서 최적의 모바일 실제로 항상 않습니다 만 수평 하나 있습니다. 편집 : 항상 사실은 아닙니다. 2 2 9 1Nabb 와 함께 아래 주석에서 반례를 찾았습니다.

    Size 18:                Size 16:
       |                        |
    +-++--+-----+            +--++-+
    | |   |     |            |   | |
    2 9   2     1           -+-  9 1
                            | |
                            2 2
    
  • 나는 단지 바보 같은 무차별 강요를한다 :

    1. 주어진 가중치는 무작위로 섞입니다.
    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로 붙일 수 있다고 생각했지만 분명히 그 쌍 자체는 균형이 맞지 않습니다!
Nabb

자, 여기에 여러 레이어에서 실제로 더 나을 수있는 것이 있습니다. 2 2 9 1즉, 16 크기 대신 (2 + 2) * 3 = 9 + 1 * 3 대신 2-9+--2----118입니다. 임계 값이 있다고 추측합니다 (5 또는 6) ) 이후에는 단일 가로 행이 항상 최적입니다.
Nabb

@Nabb : ;; 그것은 실제로 좋은 반례입니다.
balpha

@Nabb, 2-2-+9-113 점의 잔액이 있는 단일 막대 (4*2+2*2 = 9*1+1*3). 그래서 나는 이것이 좋은 반례라고 생각하지 않습니다.
Keith Randall

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

물론 완벽한 균형을 이루고 있습니다.


나는 원래이 도전의 정신으로 더 많은 것을 시도하려고했지만 어쨌든이 구조에 최적화되어 있다는 것이 금방 밝혀졌다


아마 내 설명에서 명확하지 않지만 |가중치의 하단에 a를 연결할 수는 없습니다 .
Keith Randall

@KeithRandall 아 좋아; 그걸 염두에두고 이것을 올바르게 해결해야 할 수도 있습니다.
Dave

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

파이썬 3

이것은 모든 테스트 사례에서 최적보다 1 이상 나쁘지 않으며 5 초 안에 그렇게합니다.

기본적으로 단일 바 접근 방식을 사용합니다. 무작위로 입력을 주문 한 다음 한 번에 하나씩 무게 추를 막대에 삽입하십시오. 각 요소는 시간의 이전 75 %와 시간의 25 %를 사용하여 어느 한 쪽에서 초과 중량을 최소화하는 위치에 놓이거나 그 관점에서 두 번째로 가장 좋은 위치에 놓입니다. 그런 다음 모바일이 마지막에 균형을 잡았는지 확인하고 지금까지 찾은 최고의 모바일보다 낫습니다. 가장 좋은 것을 저장 한 다음 검색 후 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)

내가 차선책이라고 생각하는이 솔루션 중 유일한 것은 가장 긴 솔루션이며,이 솔루션은 10 분 동안 실행 한 후에 발견되었습니다.

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.