Xây dựng một điện thoại di động nhỏ và cân bằng


18

Bạn được cung cấp một loạt các trọng lượng, và nhiệm vụ của bạn là xây dựng một điện thoại di động cân bằng nhỏ bằng cách sử dụng các trọng lượng đó.

Đầu vào là một danh sách các trọng số nguyên trong phạm vi từ 1 đến 9, bao gồm. Có thể có sự trùng lặp.

Đầu ra là một hình ảnh ascii của một chiếc điện thoại di động mà khi được treo sẽ cân bằng. Có lẽ tốt nhất được hiển thị bằng ví dụ:

đầu vào

3 8 9 7 5

đầu ra có thể

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

Bạn phải sử dụng các ký tự ascii như được hiển thị. Các phân đoạn ngang và dọc có thể có độ dài bất kỳ. Không một phần nào của điện thoại di động có thể chạm vào (theo chiều ngang hoặc chiều dọc) một phần khác không được kết nối của điện thoại di động. Tất cả các trọng lượng phải được treo từ một đoạn dọc có chiều dài ít nhất là 1 và phải có một đoạn thẳng đứng mà toàn bộ điện thoại di động được treo.

Kích thước của một điện thoại di động là tổng số +, -|ký tự cần thiết để xây dựng nó. Kích thước thấp hơn là tốt hơn.

Bạn có thể đặt nhiều kết nối trên một phân khúc như bạn muốn. Ví dụ:

đầu vào

2 3 3 5 3 9

đầu ra có thể

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

Chương trình chiến thắng là chương trình có thể tạo ra kích thước trung bình di động thấp nhất cho một bộ thử nghiệm đầu vào. Thử nghiệm thực sự là siêu bí mật để ngăn chặn mã hóa cứng, nhưng nó sẽ giống như thế này:

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

Vật lý cũng tham gia?
BẠN

1
@ S.Mark: Tôi đoán bạn có thể nói như vậy. Đối với người khuyết tật, tổng số total_weight_hung_from_point * distance_of_point_from_pivotphải giống nhau ở cả hai phía của điểm trục.
Keith Randall

Có lẽ để làm cho việc kiểm tra các sơ đồ dễ dàng hơn, làm cho nó để một thanh bằng khoảng hai dấu gạch nối? Khi nó đứng, sơ đồ của bạn trông mất cân bằng.
Thomas O

Câu trả lời:


5

Con trăn 2.

Tôi đang lừa dối một chút chút:

  • Tôi chỉ xây dựng điện thoại di động với một chiều ngang. Tôi có một cảm giác (nhưng tôi đã không chứng minh nó) mà tối ưu điện thoại di động theo các điều kiện được thực luôn không chỉ có một ngang. Chỉnh sửa: Không phải lúc nào cũng đúng; với 2 2 9 1Nabb đã tìm thấy một ví dụ ngược trong các ý kiến ​​dưới đây:

    Size 18:                Size 16:
       |                        |
    +-++--+-----+            +--++-+
    | |   |     |            |   | |
    2 9   2     1           -+-  9 1
                            | |
                            2 2
    
  • Tôi chỉ làm vũ phu ngu ngốc:

    1. Các trọng số cho trước được xáo trộn ngẫu nhiên.
    2. Hai trọng lượng tại một thời điểm được đặt trên điện thoại di động ở những vị trí tốt nhất sao cho cân bằng.
    3. Nếu kết quả di động tốt hơn bất kỳ thiết bị nào chúng tôi có trước đây, hãy nhớ nó.
    4. Rửa sạch và lặp lại, cho đến khi hết số giây được xác định trước.

Kết quả của tôi cho đầu vào mẫu của bạn; mỗi cái được chạy trong 5 giây (tôi biết rằng điều này thật vô lý với những người nhỏ bé - chỉ cần trải qua tất cả các hoán vị có thể sẽ nhanh hơn). Lưu ý rằng vì có một yếu tố ngẫu nhiên, các lần chạy tiếp theo có thể tìm thấy kết quả tốt hơn hoặc xấu hơn.

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

Mã (dài dòng, vì đây không phải là mã golf):

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: Trọng lượng cao hơn 9 không thể. Đối với 1 9 2 8nó tạo ra 1-------8+-9--2; từ đỉnh đầu tôi không thể nghĩ ra điều gì tốt hơn (nhưng tôi sẽ không dựa vào điều đó) - bạn có gì không?
balpha

1
@balpha: Nevermind, đã không suy nghĩ thẳng khi tôi nhận xét trước đó. Tôi đã nghĩ vì một số lý do mà bạn có thể dính chúng như 1-9 và 2-8, nhưng rõ ràng chính những cặp đó không cân bằng!
Nabb

Được rồi, đây là một lớp có thể thực sự tốt hơn với nhiều lớp: 2 2 9 1ví dụ: (2 + 2) * 3 = 9 + 1 * 3 cho kích thước 16 thay vì 2-9+--2----1là 18. Tôi đoán rằng có một ngưỡng (có thể là 5 hoặc 6 ) sau đó một hàng ngang duy nhất luôn luôn là tối ưu.
Nabb

@Nabb: Đúng; Đó thực sự là một ví dụ tốt.
balpha

@Nabb, Một thanh duy nhất có 2-2-+9-1số dư, với số điểm là 13 (4*2+2*2 = 9*1+1*3). Vì vậy, tôi không nghĩ rằng một trong những ví dụ tốt.
Keith Randall

1

Vâng, đây là một câu hỏi cũ, nhưng tôi chỉ thấy nó xuất hiện trong tab câu hỏi hàng đầu nên đây là giải pháp (tối ưu) của tôi:

#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;
}

Từ việc nhìn vào các quy tắc tôi khá chắc chắn rằng nó không gian lận, mặc dù cảm giác như vậy. Điều này sẽ chỉ xuất tất cả các số đã cho trong một chuỗi dọc, với tổng chi phí là 2 * number_of_inputs (là mức tối thiểu có thể vì mỗi số phải có một thanh phía trên nó bất kể bố cục là gì). Đây là một ví dụ:

./mobile 3 8 9 7 5

Sản xuất:

|
3
|
8
|
9
|
7
|
5

Đó là tất nhiên trong sự cân bằng hoàn hảo.


Ban đầu tôi định thử một thứ gì đó nhiều hơn theo tinh thần của thử thách này, nhưng nó nhanh chóng nhận ra rằng dù sao nó cũng chỉ được tối ưu hóa cho cấu trúc này


Có thể không rõ ràng từ mô tả của tôi, nhưng bạn không thể kết nối a |với đáy của trọng lượng.
Keith Randall

@KeithRandall ah ok; với ý nghĩ đó tôi có thể phải giải quyết vấn đề này một cách đúng đắn.
Dave

1

Đây là một giải pháp mà vũ phu buộc giải pháp hàng đơn nhỏ nhất. Mã lặp trên tất cả các hoán vị và tính toán trọng tâm của mỗi khối. Nếu tâm khối lượng có tọa độ nguyên, chúng tôi đã tìm ra giải pháp.

Sau khi thử tất cả các hoán vị, chúng tôi thêm một phân đoạn vào hỗn hợp (tương đương với trọng lượng khối lượng 0) trong tập trọng lượng và thử lại hiện tại của chúng tôi.

Để chạy chương trình, làm 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)

nơi tạo ra những giải pháp tốt nhất:

    |
+-+-+-+-+
| | | | |
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

Con trăn 3

Điều này không tệ hơn 1 so với tối ưu trên bất kỳ trường hợp thử nghiệm nào, tôi tin, và làm như vậy trong 5 giây.

Về cơ bản, tôi sử dụng một cách tiếp cận thanh duy nhất. Tôi ngẫu nhiên đặt hàng đầu vào, sau đó chèn trọng lượng lên thanh một lần. Mỗi phần tử được đặt ở vị trí giảm thiểu trọng lượng vượt quá ở hai bên hoặc vị trí tốt thứ hai theo quan điểm đó, sử dụng 75% trước đây và 25% sau. Sau đó, tôi kiểm tra xem điện thoại di động có cân bằng ở cuối không, và tốt hơn so với điện thoại di động tốt nhất được tìm thấy cho đến nay. Tôi lưu trữ cái tốt nhất, sau đó dừng lại và in nó ra sau 5 giây tìm kiếm.

Kết quả, trong 5 giây chạy:

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

Mã số:

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)

Một trong những giải pháp duy nhất mà tôi tin là không tối ưu là giải pháp dài nhất, có giải pháp này, mà tôi đã tìm thấy sau 10 phút chạy:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.