ตัวแปรของสนามแข่งที่มีจุดสิ้นสุดที่แน่นอนและศูนย์ความเร็วเทอร์มินัล


9

บทนำ

ความท้าทายเป็นตัวแปรที่น่าสนใจมากของสนามแข่งเกมและความท้าทายทั้งสอง:

แหล่งที่มาของความท้าทายนี้อยู่ที่นี่ (เป็นภาษาเยอรมัน): c't-Racetrack

ความท้าทายนี้น่าสนใจเป็นพิเศษ (และแตกต่างจากความท้าทายสองข้อที่กล่าวมาแล้ว) เนื่องจากเป็นการรวมพื้นที่การค้นหาขนาดใหญ่เข้ากับเงื่อนไขที่แน่นอนบางประการที่ต้องพบเจอ เนื่องจากเทคนิคการค้นหาที่ละเอียดถี่ถ้วนในการค้นหาพื้นที่ขนาดใหญ่นั้นใช้งานยากเนื่องจากเงื่อนไขที่แน่นอนวิธีการประมาณยังไม่สามารถใช้งานได้ง่าย เนื่องจากชุดค่าผสมที่ไม่ซ้ำกันนี้ (รวมถึงสัญชาตญาณพื้นฐานจากฟิสิกส์) ปัญหาน่าทึ่ง (และทุกอย่างที่เกี่ยวข้องกับรถแข่งเป็นเรื่องที่น่าสนใจอยู่แล้ว ;-)

ท้าทาย

ดูที่สนามแข่งต่อไปนี้ ( แหล่งที่มา ):

ป้อนคำอธิบายรูปภาพที่นี่

คุณต้องเริ่มต้น(120,180)และจบอย่างแน่นอนที่(320,220)("Ziel" ในภาษาเยอรมัน) โดยไม่ต้องแตะผนังด้านใดด้านหนึ่ง

รถถูกควบคุมโดยเวกเตอร์การเร่งความเร็วของแบบฟอร์ม(a_x,a_y)- เป็นตัวอย่าง:

(8,-6)
(10,0)
(1,9)

ตัวเลขแรกคือการเร่งความเร็วสำหรับ x-vector, ที่สองสำหรับ y-vector พวกเขาจะต้องเป็นจำนวนเต็มเพราะคุณได้รับอนุญาตให้ใช้จุดจำนวนเต็มในตาราง นอกจากนี้ต้องปฏิบัติตามเงื่อนไขต่อไปนี้:

a_x^2 + a_y^2 <= 100,

10ซึ่งหมายความว่าการเร่งความเร็วในทิศทางใดจะต้องมีด้านล่างหรือเท่ากับ

เพื่อดูว่ามันทำงานได้อย่างไรดูที่ภาพต่อไปนี้ (ที่มา ):

ป้อนคำอธิบายรูปภาพที่นี่

เป็นตัวอย่าง: เริ่มต้นจาก(120,180)คุณเร่งโดย8ในทิศทาง x และตาม-6ทิศทาง y สำหรับขั้นตอนถัดไปนี่คือความเร็วของคุณที่คุณเพิ่มความเร่ง(10,0)เพื่อรับ (ถูกต้องทางกายภาพ) การเคลื่อนไหวที่เกิดขึ้นครั้งถัดไปของคุณ (ไปยังจุด(146,168)การเคลื่อนไหวที่เกิดขึ้นคือสิ่งที่นับเมื่อมาถึงการตรวจสอบว่าคุณแตะผนังด้านใดด้านหนึ่งหรือไม่ คุณเพิ่มเวกเตอร์การเร่งความเร็วครั้งต่อไปในความเร็วปัจจุบันของคุณเพื่อให้การเคลื่อนที่ครั้งต่อไปเป็นไปเรื่อย ๆ ดังนั้นในทุกขั้นตอนรถของคุณจะมีตำแหน่งและความเร็ว (ในภาพตัวอย่างด้านบนลูกศรสีน้ำเงินเป็นความเร็วลูกศรสีส้ม สำหรับการเร่งความเร็วและลูกศรสีแดงเข้มสำหรับการเคลื่อนไหวที่เกิดขึ้น)

ในฐานะที่เป็นเงื่อนไขเพิ่มเติมที่คุณต้องมีความเร็วปลายเมื่อคุณอยู่ในจุดสิ้นสุด(0,0)(320,220)

ผลลัพธ์จะต้องเป็นรายการของเวกเตอร์ความเร่งในรูปแบบข้างต้น

ผู้ชนะคือผู้ที่เสนอโปรแกรมที่ค้นหาวิธีแก้ปัญหาด้วยเวกเตอร์การเร่งความเร็วที่น้อยที่สุด

Tiebreaker
นอกจากนี้มันจะดีมากถ้าคุณสามารถแสดงให้เห็นว่านี่เป็นทางออกที่ดีที่สุดและไม่ว่าจะเป็นทางออกที่ดีที่สุดเท่านั้นหรือว่ามีวิธีแก้ปัญหาที่ดีที่สุดหลายแห่ง

มันก็จะดีถ้าคุณสามารถให้เค้าร่างทั่วไปของอัลกอริทึมของคุณทำงานและแสดงความคิดเห็นรหัสเพื่อให้เราสามารถเข้าใจได้

ฉันมีโปรแกรมที่ตรวจสอบว่าการแก้ปัญหาใด ๆ ให้ถูกต้องและฉันจะให้ข้อเสนอแนะ

ภาคผนวก
คุณสามารถใช้ภาษาการเขียนโปรแกรมใด ๆ แต่ฉันจะดีใจโดยเฉพาะอย่างยิ่งถ้าใครใช้ R เพราะฉันใช้มันมากในงานประจำวันของฉันและอย่างใดได้คุ้นเคยกับมัน :-)

ภาคผนวก II
เป็นครั้งแรกที่ฉันได้รับรางวัล - หวังว่านี่จะเป็นลูกบอลกลิ้ง (หรือดีกว่า: รับรถขับ :-)


@Mego: แต่ ... มีความคิดเกี่ยวกับมัน: ฉันไม่แน่ใจว่าฉันควรจะเพิ่มโปรแกรมอย่างน้อยสองเหตุผล: ประการแรกในความท้าทายเดิมมันไม่ได้รวมทั้งประการที่สองมันเช่นมีกิจวัตรที่เป็นส่วนหนึ่งของ ความท้าทาย (เช่นการตรวจจับการชนกัน) ดังนั้นมันจะทำให้เสียส่วนหนึ่งของความสนุก ... ฉันจะต้องนอนบนมัน ...
vonjd

1
โปรแกรมจำเป็นต้องคำนวณเส้นทางจริงหรือฉันจะคำนวณเส้นทางที่ดีที่สุดไว้ล่วงหน้าแล้วโพสต์สิ่งที่ต้องการได้print "(10,42)\n(62,64)..."ไหม
Loovjo

@ Loovjo: ไม่โปรแกรมมีการคำนวณเส้นทางของตัวเองดังนั้นปัญญาจะต้องรวมอยู่ในโปรแกรมไม่ใช่เพียงแค่เอาท์พุทรูทีน
vonjd

คำตอบ:


4

Python 24 ขั้นตอน (กำลังดำเนินการ)

แนวคิดคือการแก้ปัญหาอย่างต่อเนื่องก่อนลดพื้นที่ในการค้นหาอย่างมากจากนั้นให้ผลลัพธ์เป็นตาราง (โดยการปัดเศษไปยังจุดที่ใกล้ที่สุดและค้นหา 8 ตารางโดยรอบ)

ฉันกำหนดเส้นทางเป็นผลรวมของฟังก์ชันตรีโกณมิติ (ซึ่งแตกต่างจากชื่อพหุนามพวกเขาไม่แยกออกและง่ายต่อการตรวจสอบ) ฉันยังควบคุมความเร็วโดยตรงแทนการเร่งความเร็วเนื่องจากมันง่ายต่อการบังคับใช้เงื่อนไขขอบเขตโดยเพียงแค่คูณฟังก์ชันการถ่วงน้ำหนักที่มีค่าเป็น 0 ในตอนท้าย
ฟังก์ชั่นวัตถุประสงค์ของฉันประกอบด้วย -
คะแนนเอ็กซ์โพเนนเชียลสำหรับการเร่งความเร็ว> 10 -
คะแนนโพลิโนเมียลสำหรับระยะทางแบบยุคลิดระหว่างจุดสุดท้ายและเป้าหมาย
- คะแนนคงที่สูงสำหรับแต่ละจุดตัดกับกำแพง

เพื่อลดคะแนนฉันโยนมันทั้งหมดลงในการปรับให้เหมาะสมของNelder-Meadและรอสักครู่ อัลกอริทึมประสบความสำเร็จในการไปถึงจุดสิ้นสุดหยุดอยู่ที่นั่นและไม่เกินความเร่งสูงสุด แต่ก็มีปัญหากับผนัง เส้นทางเคลื่อนย้ายผ่านมุมต่างๆและติดที่นั่นหรือหยุดติดกับกำแพงโดยมีเป้าหมายอยู่ฝั่งตรงข้าม (ภาพซ้าย)
ป้อนคำอธิบายรูปภาพที่นี่

ในระหว่างการทดสอบฉันโชคดีและพบเส้นทางที่ squiggled ในลักษณะที่มีแนวโน้ม (ภาพขวา) และหลังจากปรับเปลี่ยนพารามิเตอร์แล้วฉันก็สามารถใช้มันเป็นคำทำนายเริ่มต้นสำหรับการปรับให้เหมาะสมที่ประสบความสำเร็จ


การหาปริมาณหลังจากหาเส้นทางแบบพารามิเตอร์มันถึงเวลาแล้วที่จะลบจุดทศนิยม การดูพื้นที่ใกล้เคียง 3x3 จะลดพื้นที่การค้นหาจาก 300 ^ N เป็น 9 ^ N แต่ก็ยังใหญ่เกินไปและน่าเบื่อ ก่อนที่ฉันจะเดินไปตามถนนสายนี้ฉันลองเพิ่มคำว่า "Snap to Grid" ลงในฟังก์ชันวัตถุประสงค์ (ส่วนที่แสดงความคิดเห็น) อีกหนึ่งร้อยขั้นตอนของการปรับให้เหมาะสมกับวัตถุประสงค์ที่ได้รับการอัพเดตและการปัดเศษก็เพียงพอที่จะได้คำตอบ

[(9, -1), (4, 0), (1, 1), (2, 2), (-1, 2), (-3, 4), (-3, 3), (-2 , 3), (-2, 2), (-1, 1), (0, 0), (1, -2), (2, -3), (2, -2), (3, -5 ), (2, -4), (1, -5), (-2, -3), (-2, -4), (-3, -9), (-4, -4), (- 5, 8), (-4, 8), (5, 8)]

จำนวนขั้นตอนได้รับการแก้ไขและไม่ได้เป็นส่วนหนึ่งของการเพิ่มประสิทธิภาพ แต่เนื่องจากเรามีคำอธิบายการวิเคราะห์เส้นทาง (และเนื่องจากการเร่งความเร็วสูงสุดต่ำกว่า 10) เราสามารถนำมันกลับมาใช้เป็นจุดเริ่มต้นสำหรับการเพิ่มประสิทธิภาพเพิ่มเติมด้วยจำนวนที่น้อยกว่า timesteps

from numpy import *
from scipy.optimize import fmin
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection as LC

walls = array([[[0,0],[500,0]],   # [[x0,y0],[x1,y1]]
        [[500,0],[500,400]],
        [[500,400],[0,400]],
        [[0,400],[0,0]],

        [[200,200],[100,200]],
        [[100,200],[100,100]],
        [[100,100],[200,100]],

        [[250,300],[250,200]],

        [[300,300],[300,100]],
        [[300,200],[400,200]],
        [[300,100],[400,100]],

        [[100,180],[120, 200]], #debug walls
        [[100,120],[120, 100]],
        [[300,220],[320, 200]],
        #[[320,100],[300, 120]],
])

start = array([120,180])
goal = array([320,220])

###################################
# Boring stuff below, scroll down #
###################################
def weightedintersection2D(L1, L2):
    # http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
    p = L1[0]
    q = L2[0]
    r = L1[1]-L1[0]
    s = L2[1]-L2[0]
    d = cross(r,s)
    if d==0: # parallel
        if cross(q-p,r)==0: return 1 # overlap
    else:
        t = cross(q-p,s)*1.0/d
        u = cross(q-p,r)*1.0/d
        if 0<=t<=1 and 0<=u<=1: return 1-0*abs(t-.5)-1*abs(u-.5) # intersect at p+tr=q+us
    return 0

def sinsum(coeff, tt):
    '''input: list of length 2(2k+1), 
    first half for X-movement, second for Y-movement.
    Of each, the first k elements are sin-coefficients
    the next k+1 elements are cos-coefficients'''
    N = len(coeff)/2
    XS = [0]+list(coeff[:N][:N/2])
    XC =     coeff[:N][N/2:]
    YS = [0]+list(coeff[N:][:N/2])
    YC =     coeff[N:][N/2:]
    VX = sum([XS[i]*sin(tt*ww[i]) + XC[i]*cos(tt*ww[i]) for i in range(N/2+1)], 0)
    VY = sum([YS[i]*sin(tt*ww[i]) + YC[i]*cos(tt*ww[i]) for i in range(N/2+1)], 0)
    return VX*weightfunc, VY*weightfunc

def makepath(vx, vy):
    # turn coordinates into line segments, to check for intersections
    xx = cumsum(vx)+start[0]
    yy = cumsum(vy)+start[1]
    path = []
    for i in range(1,len(xx)):
        path.append([[xx[i-1], yy[i-1]],[xx[i], yy[i]]])
    return path

def checkpath(path):
    intersections = 0
    for line1 in path[:-1]: # last two elements are equal, and thus wrongly intersect each wall
        for line2 in walls:
            intersections += weightedintersection2D(array(line1), array(line2))
    return intersections

def eval_score(coeff):
    # tweak everything for better convergence
    vx, vy = sinsum(coeff, tt)
    path = makepath(vx, vy)
    score_int = checkpath(path)
    dist = hypot(*(path[-1][1]-goal))
    score_pos = abs(dist)**3
    acc = hypot(diff(vx), diff(vy))
    score_acc = sum(exp(clip(3*(acc-10), -10,20)))
    #score_snap = sum(abs(diff(vx)-diff(vx).round())) + sum(abs(diff(vy)-diff(vy).round()))
    print score_int, score_pos, score_acc#, score_snap
    return score_int*100 + score_pos*.5 + score_acc #+ score_snap

######################################
# Boring stuff above, scroll to here #
######################################
Nw = 4 # <3: paths not squiggly enough, >6: too many dimensions, slow
ww = [1*pi*k for k in range(Nw)]
Nt = 30 # find a solution with tis many steps
tt = linspace(0,1,Nt)
weightfunc = tanh(tt*30)*tanh(30*(1-tt)) # makes sure end velocity is 0

guess = random.random(4*Nw-2)*10-5
guess = array([ 5.72255365, -0.02720178,  8.09631272,  1.88852287, -2.28175362,
        2.915817  ,  8.29529905,  8.46535503,  5.32069444, -1.7422171 ,
       -3.87486437,  1.35836498, -1.28681144,  2.20784655])  # this is a good start...
array([ 10.50877078,  -0.1177561 ,   4.63897574,  -0.79066986,
         3.08680958,  -0.66848585,   4.34140494,   6.80129358,
         5.13853914,  -7.02747384,  -1.80208349,   1.91870184,
        -4.21784737,   0.17727804]) # ...and it returns this solution      

optimsettings = dict(
    xtol = 1e-6,
    ftol = 1e-6,
    disp = 1,
    maxiter = 1000, # better restart if not even close after 300
    full_output = 1,
    retall = 1)

plt.ion()
plt.axes().add_collection(LC(walls))
plt.xlim(-10,510)
plt.ylim(-10,410)
path = makepath(*sinsum(guess, tt))
plt.axes().add_collection(LC(path, color='red'))
plt.plot(*start, marker='o')
plt.plot(*goal, marker='o')
plt.show()

optres = fmin(eval_score, guess, **optimsettings)
optcoeff = optres[0]    

#for c in optres[-1][::optimsettings['maxiter']/10]:
for c in array(optres[-1])[logspace(1,log10(optimsettings['maxiter']-1), 10).astype(int)]:
    vx, vy = sinsum(c, tt)
    path = makepath(vx,vy)
    plt.axes().add_collection(LC(path, color='green'))
    plt.show()

สิ่งที่ต้องทำ: GUI ที่ให้คุณวาดเส้นทางเริ่มต้นเพื่อให้เข้าใจถึงทิศทางได้อย่างคร่าวๆ อะไรที่ดีไปกว่าการสุ่มตัวอย่างจากอวกาศ 14 มิติ


ทำได้ดี! ดูเหมือนว่า 17 ขั้นตอนเป็นขั้นต่ำ - คุณจะเปลี่ยนโปรแกรมของคุณเพื่อค้นหาวิธีแก้ไขด้วยข้อมูลเพิ่มเติมนี้ได้อย่างไร
vonjd

โอ้ที่รัก: โปรแกรมของฉันแสดงให้เห็นว่าคุณไม่ได้จบที่ (320,220) แต่ที่ (320,240) - คุณช่วยตรวจสอบได้
ไหม

1
อ๊ะอัปเดตโซลูชันแล้วลองใหม่อีกครั้งจนถึง 24 ขั้นตอน การปรับจูนด้วยมือทำได้ง่ายนิดเดียวโดยดูที่ภาพโดยอัตโนมัติเพื่อทำงานกับเคสทั่วไป - ไม่มากนัก
DenDenDo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.