สร้างมือถือขนาดเล็กและสมดุล


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

อาจจะทำให้การตรวจสอบไดอะแกรมง่ายขึ้นทำให้เป็นแท่งเดียวที่มีค่าเท่ากับสองขีดกลาง แผนภาพของคุณนั้นดูไม่สมดุล
โทมัส O

คำตอบ:


5

Python 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

รหัส (verbose เนื่องจากไม่ใช่รหัสกอล์ฟ):

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(2 + 2) * 3 = 9 + 1 * 3 สำหรับ 16 ขนาดแทนที่จะ2-9+--2----1เป็น 18 ฉันเดาว่ามีธรณีประตู (อาจ 5 หรือ 6 ) หลังจากนั้นแถวแนวนอนเดียวจะเหมาะสมที่สุดเสมอ
Nabb

@Nabb: ใช่; นั่นเป็นตัวอย่างที่ดี
balpha

@Nabb, บาร์เดี่ยวที่มี2-2-+9-1ยอดด้วยคะแนน (4*2+2*2 = 9*1+1*3)13 ดังนั้นฉันไม่คิดว่าเป็นตัวอย่างที่ดี
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;
}

จากการดูกฎฉันค่อนข้างแน่ใจว่ามันไม่ได้โกงแม้ว่ามันจะรู้สึกเหมือนมัน นี่จะเอาท์พุทตัวเลขทั้งหมดที่กำหนดใน chain ตามแนวตั้งสำหรับค่าใช้จ่ายทั้งหมด 2 * number_of_inputs (ซึ่งเป็นค่าต่ำสุดที่เป็นไปได้เพราะแต่ละหมายเลขต้องมีแถบด้านบนไม่ว่าโครงร่างใด) นี่คือตัวอย่าง:

./mobile 3 8 9 7 5

ผลิต:

|
3
|
8
|
9
|
7
|
5

ซึ่งแน่นอนว่าอยู่ในสมดุลที่สมบูรณ์แบบ


เดิมทีฉันจะลองอะไรเพิ่มเติมในจิตวิญญาณของความท้าทายนี้ แต่มันกลับกลายเป็นว่ามันเพิ่งปรับให้เหมาะกับโครงสร้างนี้ต่อไป


อาจไม่ชัดเจนจากคำอธิบายของฉัน แต่คุณไม่สามารถเชื่อมต่อ a |กับด้านล่างของน้ำหนักได้
Keith Randall

@ KeithRandall ah ok; ในใจฉันอาจต้องแก้ปัญหานี้ให้ถูกต้อง
เดฟ

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 วินาที

โดยทั่วไปฉันใช้วิธีแถบเดียว ฉันสุ่มสั่งซื้ออินพุตจากนั้นใส่น้ำหนักลงในแถบทีละครั้ง แต่ละองค์ประกอบจะอยู่ในตำแหน่งที่ลดน้ำหนักส่วนเกินทั้งสองข้างหรือตำแหน่งที่ดีที่สุดที่สองจากมุมมองนั้นโดยใช้ 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.