2017-10-05 221 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