2017-10-05 226 views
0

我一直在練習算法,我發現一個花了幾周,仍然無法解決。我不能拿出一個完整的算法,但我一直在努力的想法,我寫了到目前爲止的代碼是:在笛卡兒座標系中找到最接近的點,並移動到另一個點上?

注:的我共享綜合性的問題的原因是不恩龍的問題,而我可能首先誤解了問題的主要觀點。

問題

一個PropBot只能作出兩種截然不同的走勢。它可以是移動10釐米轉發,或轉向右邊45度。這些單獨運動中的每一個都需要秒的時間

輸入

你的模塊有兩個輸入:在該PropBot希望得到儘可能接近地平面上的點的笛卡爾座標,可以用來做的最大秒數這個。在導航開始時,機器人位於原點,指向+ x方向。秒數將是0到24之間的整數,包括0和24。期望的目的地點的x和y座標將是-100和100之間的實數,包括端點在內。輸入文件中的第一個條目將是測試 個案的數量,t(0 < t≤100)。在這行之後是t行,每行包含三個由空格分隔的條目。第一個條目將是PropBot必須接近該點的秒數。第二個條目是該點的x座標,第三個條目是該點的y座標。

輸出

你的程序必須返回目標點和最近點的機器人可以在規定時間內到達之間的距離。您的結果應至少包含小數點左側的一位數字,小數點右側的正確六位數字。爲了消除影響結果舍入誤差的機會,我們已經構建了測試數據,以便在第七位的真實結果的小數點右邊是從來沒有一個4或5

採樣輸入

24 5.0 5.0

9 7.0 17.0

樣本輸出

0.502525 < - 如何?

0.100505 OK

Java代碼

枚舉方向

public enum Direction { 

EAST(1), N_EAST(2), NORTH(3), N_WEST(4), WEST(5), S_WEST(6), SOUTH(7), S_EAST(8); 

private int direction; 
private int index; 

Direction(){ 
    direction = 1; 
    index = 0; 
} 

Direction(int dir){ 
    direction = dir; 
} 

int getDirection(){ 
    return direction; 
} 

public int incrementDir(){ 
    if(direction > 1 && direction <= 8){ 
     direction = 8 - index++; 
     // Rotate towards right side 

    } 
    else if(direction == 1){ 
     direction = 8; 
     index = 1; 
    } 
    return direction; 
} 
} 

摘要 - Calculation.java

import java.awt.Point; 

public abstract class Calculation { 

public static Direction getDir(Point p){ 
    int line = getCloseLine(p); 
    switch (line) { 
     case 1: 
      return Direction.EAST; 
     case 2: 
      return Direction.N_EAST; 

      // 2nd Quadrant 
     case 3: 
      return Direction.NORTH; 
     case 4: 
      return Direction.N_WEST; 

      // 3rd Quadrant 
     case 5: 
      return Direction.WEST; 
     case 6: 
      return Direction.S_WEST; 

      // 4th Quadrant 
     case 7: 
      return Direction.SOUTH; 
     case 8: 
      return Direction.S_EAST; 

    default: 
     return Direction.EAST; 
    } 
} 

public static int getSelectedLine(Point p){ 
    int a = getCloseLine(p); 
    return a; 
} 

public static int getQuadrant(Point target) { 
    double x = target.getX(); 
    double y = target.getY(); 
    if (x > 0 && y > 0) 
     return 1; 
    else if (x < 0 && y > 0) 
     return 2; 
    else if (x < 0 && y < 0) 
     return 3; 
    else if (x > 0 && y < 0) 
     return 4; 
    // Means point lies on an Axis not in any Quadrant 
    return -1; 
} 

public static int getAxis(Point target) { 
    double x = target.getX(); 
    double y = target.getY(); 
    if (x > 0 && y == 0) 
     return 1; 
    else if (x == 0 && y > 0) 
     return 2; 
    else if (x < 0 && y == 0) 
     return 3; 
    else if (x == 0 && y < 0) 
     return 4; 
    else if(x == 0 && y == 0) 
     return 0; 

    return -1; 
} 

public static double getAngle(Point v2) { 
    double d = v2.getY()/v2.getX(); 
    double ang = Math.toDegrees(Math.atan(d)); 
    return ang; 
} 

public static int getSector(Point point) { 
    double angle = getAngle(point); 
    int quad = getQuadrant(point); 

    if(quad == -1) 
     return -1;   

    switch (quad) { 
     case 1: 
      if (angle < 45.0) 
       return 1; 
      else 
       return 2; 

     case 2: 
      if (angle < -45.0) 
       return 3; 
      else 
       return 4; 
     case 3: 
      if (angle < 45.0) 
       return 5; 
      else 
       return 6; 
     case 4: 
      if (angle < -45.0) 
       return 7; 
      else 
       return 8; 
    } 

    return -1; 
} 

public static int getCloseLine(Point p) { 
    int sec = getSector(p); 
    double angle = getAngle(p); 
    System.out.println("ANGLE : " + angle); 

    if(sec == -1){ 
     int axis = getAxis(p); 
     switch(axis){ 
     case 1: 
      return 1; 
     case 2: 
      return 3; 
     case 3: 
      return 5; 
     case 4: 
      return 7; 
     case 0: 
      return 0; 
     } 
    } 

    switch (sec) { 

    case 1: 
     if (angle < 22.5) 
      return 1; 
     else 
      return 2; 
    case 2: 
     if (angle < 67.5) 
      return 2; 
     else 
      return 3; 

     // 2nd Quadrant 
    case 3: 
     if (angle < -67.5) 
      return 3; 
     else 
      return 4; 
    case 4: 
     if (angle < -22.5) 
      return 4; 
     else 
      return 5; 

     // 3rd Quadrant 
    case 5: 
     if (angle < 22.5) 
      return 5; 
     else 
      return 6; 
    case 6: 
     if (angle < 67.5) 
      return 6; 
     else 
      return 7; 

     // 4th Quadrant 
    case 7: 
     if (angle < -67.5) 
      return 7; 
     else 
      return 8; 
    case 8: 
     if (angle < -22.5) 
      return 8; 
     else 
      return 1; 

    } 
    return -1; 
} 
} 

Main.java

import java.awt.Point; 
import java.util.Scanner; 


public class Main { 

public static void main(String[] args) 
{ 

    Scanner s = new Scanner(System.in); 

    int time = 0; 

    Point p = new Point(0, 0); 
    System.out.println("Enter time: "); 
    time = s.nextInt(); 
    System.out.println(" X: "); 
    p.x = s.nextInt(); 
    System.out.println(" Y: "); 
    p.y = s.nextInt(); 

    if(!(time > 0 && time <= 24)){ 
     s.close(); 
     System.err.println("INPUT ERROR!"); 
     return; 
    } 
    // Initialize bot facing +x direction 
    Bot b = new Bot(p, Direction.EAST); 

    int line = Calculation.getCloseLine(p); 

    while(time != 0 && b.getDirectionInt() != line){ 
     // Rotate the face towards the point 
     b.rotate(); 
     time--; 
    } 

    s.close(); 
} 

} 

Bot.java

import java.awt.Point; 

public class Bot { 

private Point location; 
private Direction direction; 

public Bot(){ 
    location = new Point(0, 0); 
    direction = Direction.EAST; 
} 

public Bot(Point loc, Direction dir){ 
    location = loc; 
    direction = dir; 
} 

public Point move(){ 

    return location; 
} 

public int rotate(){ 
    direction.incrementDir(); 
    return direction.getDirection(); 
} 

public int getDirectionInt(){ 
    return direction.getDirection(); 
} 
} 

我的方法是笛卡爾平面分成部門,並得到一個壁櫥線輸入點和旋轉機器人和然後繼續前進。

第一期:我得到了如何評估第二個案例輸出,但我對第一個案例輸出沒有任何意見。

Graph of the points

線分佈如下:

Line distribution

第二期:如果機器人移動對角(45度),然後可以水平或垂直移動,之後,它似乎爲如果整個笛卡爾飛機已經移動並且我寫的代碼不再有效。

我的方法是否正確?如果是的話,我如何進一步改進? 如果我的方法錯了?請提出一個更好的選擇。

+0

難道我們假設PropBot開始於(0,0)?我們假設座標是以釐米爲單位的嗎?也就是說,(5,5)是否對應於原點右側5釐米,上面5釐米? –

+0

@JimMischel從(0,0)開始,確實是Bot面向+ x方向。單位是釐米也是,因爲當你嘗試用上述假設評估給定的輸入時,你會得到給定的輸出結果。這證明它以釐米爲單位。 –

+0

你試過回溯嗎? 2^24似乎可以回溯。 – algrid

回答

0

這裏是Java代碼Algrid的回答是:

public class Main { 

static double min_d = 1e+10; 

// diagonal distance factor cos(45), needs to multiply with hypotenuse 
static double c = 0.707110678; 

static double xt = 7.0; 
static double yt = 17.0; 

public static void solve(int time, double x, double y, double dirX, double dirY) { 

    double d = Math.sqrt(Math.pow((x - xt), 2) + Math.pow((y - yt), 2)); 
    if(d < min_d) 
     min_d = d; 

    if(time == 0 || (d-time * 10) >= min_d){ 
     return; 
    } 

    solve(time - 1, x + dirX, y + dirY, dirX, dirY); 
    solve(time - 1, x, y, c * (dirX - dirY), c * (dirX + dirY)); 
} 

public static void main(String[] args) 
{ 

    solve(9, 0.0, 0.0, 10.0, 0.0); 
} 

}// Class END 
1

我想你可以爲機器人的每一個可能的停止點及其方向創建一個圖表。這將是一個三元組(x,y,方向),方向是0,1,...,7中的一個。對於每一個方向我前進由矢量orientation_vec [I]

import networkx as nx 


def one_second(G): 
    # In each step you can either go forward or turn. Create one edge 
    # for each node and these two possible moves. 
    orientation_vec = [(10000000, 0), (7071067, -7071067), (0, -10000000), 
         (-7071067, -7071067), (-10000000, 0), (-7071067, 7071067), 
         (0, 10000000), (7071067, 7071067)] 

    for node in G.nodes(): 
     # edge to next orientation 
     G.add_edge(node, (node[0], node[1], (node[2] + 1)%8)) 
     # edge going forward 
     G.add_edge(node, (node[0] + orientation_vec[node[2]][0], 
          node[1] + orientation_vec[node[2]][1], 
          node[2])) 


def create_graph(max_time): 
    G = nx.Graph() 
    G.add_node(n=(0, 0, 0)) 
    for t in range(max_time): 
     one_second(G) 
    print(len(G.nodes())) 

我們現在可以通過所有節點,並找到最接近目標給出。 創建圖形後,我們可以通過使用dijkstra或A *來找到最短路徑。我爲此使用了networkx軟件包。

import math 

def find_closest_path(paths, end): 
    min_dist = end[0]**2 + end[1]**2 
    best_path = None 
    for key in paths.keys(): 
     end_path = paths[key][-1] 
     path_to_end = (end_path[0] - end[0])**2 + (end_path[1]-end[1])**2 
     if path_to_end < min_dist: 
      min_dist = path_to_end 
      best_path = paths[key] 
    min_dist = math.sqrt(min_dist)/10**6 
    x = [p[0]/10**6 for p in best_path] 
    y = [p[1]/10**6 for p in best_path] 
    return min_dist, x, y 


def robot_path(end, max_time): 
    create_graph(max_time) 
    paths = nx.single_source_shortest_path(G, (0, 0, 0), cutoff=max_time) 
    return find_closest_path(paths, end) 

我繪製函數我複製粘貼從某處stackoverflow。

from pylab import * 
import matplotlib.pyplot as plt 

def plot_robot_path(x,y, end): 
    assert len(x) == len(y) 
    color=['b']*len(x) + ['r'] 

    fig = plt.figure() 
    ax = fig.add_subplot(111) 

    scatter(x+[end[0]], y+[end[1]], s=100 ,marker='o', c=color) 

    [plot([x[i], x[i+1]], [y[i], y[i+1]], '-', linewidth=3) for i in range(len(x)-1)] 

    left,right = ax.get_xlim() 
    low,high = ax.get_ylim() 
    arrow(left, 0, right -left, 0, length_includes_head = True, head_width = 0.15) 
    arrow(0, low, 0, high-low, length_includes_head = True, head_width = 0.15) 

    grid() 

    show() 

如果我們運行它,我得到如下結果:

end1=(5*10**6, 5*10**6) 
max_time = 24 
min_dist1, x1, y1 = robot_path(end1, max_time) 
print(min_dist1) 
plot_robot_path(x1, y1, (5, 5)) 

max_time=9 
end2=(7*10**6, 17*10**6) 
min_dist2, x2, y2 = robot_path(end2, max_time) 
print(min_dist2) 
plot_robot_path(x2, y2, (7, 17)) 

enter image description here

enter image description here

1

這裏的緊湊型解決方案回溯:

import math 

min_d = 1e+10 
c = 0.70710678 
xt = 5.0 
yt = 5.0 

def solve(remaining_t, x, y, dir_x, dir_y): 
    global min_d 
    d = math.sqrt((x - xt)**2 + (y - yt)**2) 
    if d < min_d: 
     min_d = d 
    if remaining_t == 0 or d - remaining_t * 10 >= min_d: 
     return 
    solve(remaining_t - 1, x + dir_x, y + dir_y, dir_x, dir_y) 
    solve(remaining_t - 1, x, y, c * (dir_x - dir_y), c * (dir_x + dir_y)) 

solve(24, 0.0, 0.0, 10.0, 0.0) 
print(min_d) 

需要〜5秒。在我的電腦上。

的解釋的位:

min_d - 在當前最小距離,我們用一個較大的值初始化它(應大於任何距離也可能會被)

solve功能採用以下參數:

remaining_t - 剩餘時間(秒),在每個步驟它減少1

xy - 機器人當前座標

dir_xdir_y - 機器人當前方向矢量的座標。該載體具有長度10的我們用該載體朝向x軸指向啓動:(10,0)

在每個步驟,如果需要的solve函數考慮到目標點和更新min_d當前距離。如果沒有剩餘時間或者離目標點太遠,我們會立即停止前進。否則,我們嘗試1)前進2)轉45度。

  1. 當機器人被向前(考慮電流方向)x變得x+dir_xy變得y+dir_y。方向矢量保持不變。

  2. 當機器人轉彎時,其座標保持不變,但方向矢量改變。見https://en.m.wikipedia.org/wiki/Rotation_matrix(我們的常數c = SIN 45 = COS 45)

+0

感謝它的工作,但我面臨的問題了解代碼,請你簡要解釋一下代碼。只是函數'solve'中的部分,爲什麼'min_d = 1e + 10'? –

+1

@MirwiseKhan我在答案中增加了更多解釋 – algrid