设计这种算法更好的方法?

时间:2010-03-02 21:14:43

标签: java algorithm ipc

我正在研究一个更为复杂的版本(车辆在X和Y方向上移动)

我做了这个例子来获得有关实现这一目标的更好方法的想法。

  1. 我有一辆车在X方向以一定速度(24.5872 mps)移动
  2. 我是通过使用执行器每100毫秒递增X值来模拟这一点(为了保持其X位置更准确和实时)
  3. 每一秒之后,我向另一个进程发送一条消息,其中包含我刚才所涉及的行的xMin和xMax值
  4. 另一个进程将以JMS消息(通常是即时消息)响应,告诉我如果前一个X区域中存在“Pothole”,则会停止(消息回调消息到一个linkblockingqueue)。
  5. 我遇到的问题是“通常是即时”部分。如果我没有足够快地得到响应,我认为它会摒弃我的算法的整个时间。处理这种情况的更好方法是什么?

    以下是我要做的一些基本代码:

    public class Mover implements MessageHandler {
    
        private static final long CAR_UPDATE_RATE_IN_MS = 100;
        private static double currX = 0;
        private static double CONSTANT_SPEED_IN_MPS = 24.5872; // 55 mph
        private static double increment = CONSTANT_SPEED_IN_MPS / (1000 / CAR_UPDATE_RATE_IN_MS);
        static LinkedBlockingQueue<BaseMessage> messageQueue = new LinkedBlockingQueue<BaseMessage>(); // ms
    
        private static int incrementor = 0;
    
        public static void main(String[] args) {
            startMoverExecutor();
        }
    
        private static void startMoverExecutor() {
    
            ScheduledExecutorService mover = Executors.newSingleThreadScheduledExecutor();
            mover.scheduleAtFixedRate((new Runnable() {
    
                @Override
                public void run() {
                    currX = incrementor * increment;
    
                    if (incrementor % (1000 / CAR_UPDATE_RATE_IN_MS) == 0) {
                        System.out.println(currX);
    
                        sendMessage(currX - CONSTANT_SPEED_IN_MPS, currX);
    
                        // do something
                        try {
                            messageQueue.poll(1000, TimeUnit.MILLISECONDS);
    
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
    
                    }
                    incrementor++;
                }
    
            }), 0, CAR_UPDATE_RATE_IN_MS, TimeUnit.MILLISECONDS);
    
        }
    
        @Override
        public void handleMessage(BaseMessage msg) {
            messageQueue.add(msg);
    
        }
    
        protected static void sendMessage(double firstX, double secondX) {
            // sendMessage here
    
        }
    
    }
    

13 个答案:

答案 0 :(得分:6)

我建议对上面的算法进行更改,如下面的步骤所示。

JMS调用其他进程


1a上。首先发送车辆的当前位置。

1b中。另一个过程将使用JMS消息进行响应,该消息包含车辆位置可见区域中所有“Pot hole位置”的列表。在客户端保留此“可见的坑洞位置”列表,以便在下面的步骤中使用。

1c上。我们将可见区域定义为车辆的邻近区域,即使使用JMS调用其他过程的(1秒延迟+网络滞后),车辆的移动也不应越过该区域。

1D。每隔一秒后,重复步骤1a和1b,并在客户端替换相对于车辆当前位置的坑洞位置列表。

车辆运动观察员


2a上。实施观察员模式,可以接收车辆运动的通知。

2B。每次生成事件时,观察者都会检查车辆的位置是否与步骤1b中获得的可见坑洞列表中的一个条目匹配。

2c中。如果找到匹配,宾果游戏!你必须停止车辆。

车辆运动


3A。注册步骤2a观察员观察车辆的运动

3B。等到你从步骤1b得到至少第一个可见的坑洞清单。

3c上。通过每100毫秒递增X值开始移动车辆。每次移动时,都应通知步骤2a观察者。

下图的传说:


o - Instance of each pot hole somewhere on map 
X - Moving vehical
. - Path followed by vehical
Circle - Visible area of the vehical driver
+---------------------------------------------+
|                                             |
|                    o                o       |
|    o                                        |
|                                             |
|                                             |
|                _.-''''`-._                  |
|    o         ,'           `.             o  |
|            ,'  o            `.              |
|           .'    .            `.             |
|           |      . .          |             |
|           |         .         |   o         |
|           |         X         |             |
|   o       \                o  /             |
|            \                 /              |
|             `.             ,'               |
|               `-._     _.-'                 |
|                   `''''                     |
|                                             |
|                  o                          |
|                                    o        |
|                                             |
|                                             |
|     o                        o              |
+---------------------------------------------+

答案 1 :(得分:3)

除非您在提供实时保证的网络和操作系统上运行系统,否则 偶尔会出现延迟。因此,您必须能够检测到这些延迟并决定如何响应 - 在汽车发现地图如何展开之前,时间是否停留在模拟的一侧?或者时间是否继续流动但是迟到的通知坑洞在路上比其可能的更远?或者是迟到的坑洞被发现为迟到而被忽略了?

我不太熟悉Java消息传递的当前状态。你能否澄清messageQueue.poll是否阻止?如果您正在发送消息然后阻止答案,则会引发一个问题:为什么您不使用同步方法,例如对远程对象的方法调用,因为这肯定会帮助基础架构毫不拖延地向您发送消息。

答案 2 :(得分:2)

我会节省currX计算的时间和位置(currX)

下次计算currX时,你会看到自上次以来经过了多少毫秒(System.currMillisec() - lastCalc),将其乘以速度并将其添加到currX。然后将最后的计算日期设置为现在。

编辑: - 小心你的单位(常数名称:MPS,评论:mph)

将其添加到声明:

private static long compDate = System.currentTimeMillis();
private static long lastNotifDate = System.currentTimeMillis();

和run方法的开始:

currX += (System.currentTimeMillis() - compDate) * CONSTANT_SPEED_IN_MPS / 1000;
compDate = System.currentTimeMillis();

if (compDate - lastNotifDate > 1000) {
    lastNotifDate = System.currentTimeMillis();
...

答案 3 :(得分:1)

也许您不需要代码实时运行,只是模拟它并计算实时值?

答案 4 :(得分:1)

好吧,如果这是一个模拟,那么你就不会知道任何坑洼。 到目前为止,我最好的选择是搬家:

                // do something
                try {
                    messageQueue.poll(1000, TimeUnit.MILLISECONDS);

                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }'

之前或之后:

            if (incrementor % (1000 / CAR_UPDATE_RATE_IN_MS) == 0) {
            .. code ..
            }

并将民意调查中的参数从1000更改为1(如果这意味着民意调查不会等待,则暂时退出)

答案 5 :(得分:1)

正如你所说,

  

我遇到的问题是“通常是即时”部分。如果我没有足够快地得到响应,我认为它会摒弃我的算法的整个时间。处理这种情况的更好方法是什么?

在一个理想的世界中,你的计算机时钟是完美的,垃圾收集是原子的,瞬时的,在O(1)中,网络没有延迟,操作系统没有中断,墨菲睡着了。

由于您正在处理现实世界的情况,因此您需要针对其典型的不确定性进行调整。首先,您需要统计信息。当然,Java GC永远不能保证是实时的,但你可以有90%的时间使用相当好的近似值。剩余的10%可以由另一个'计划B'处理,依此类推。

换句话说:运行你的系统并试图尽可能地阻止它;收集使用统计;为这些情况制定最佳解决方案。例如,

  • 在模拟模拟中插入随机延迟,看看它是如何响应的(单元测试!);也许你想每500ms运行一次run()方法?
  • 使用观察者模式(如其他地方所建议的);
  • 在run()方法中运行尽可能少的代码;也许每隔1sec - epsilon运行一次,其中 epsilon 是一个足够小的区间,可以解释足够大的样本中具有最高方差的延迟
  • 有两个独立的线程同时运行,使用锁保持同步,平均运行时间以获得更好的时钟

在紧要关头,没有确切的解决方案,因为没有确切的“真实”世界。添加噪音,为更糟糕的情况做好准备,平衡其余部分。

答案 6 :(得分:1)

碰撞检测空间的粒度在我看来太小而不能可靠地依赖于JMS。

我会改变这一点,以便 Mover 收到一个合理的地图块,它可以在本地使用,例如如果整个地图是100 x 100,那么 Mover 应至少接收10 x 10的网格部分。

网格的这一部分可以在每次移动时被局部查询以获得坑洼,并且当该位置接近10×10部分的边界时,则可以请求下一个方格。

这将为您提供一种双缓冲窗口,可以加载新的方块,同时继续对旧方块评估剩余的移动。

此外,您可能希望能够侦听对方块的更改,以便当有人向先前已加载的方块添加新的坑洞时,将广播该新方块以及具有该方块的任何客户端当前加载可以重新加载它。

祝你好运。

答案 7 :(得分:0)

让计算机B在检测到坑洞时发回坑洞的位置,然后计算机A可以将车辆移动到该位置。如果计算机A上的车辆做了其他事情,而不仅仅是在撞到坑洞时坐在那里,那么这篇文章可能会帮助你减少位置/方向/速度的突然变化: http://www.gamedev.net/reference/programming/features/cubicsplines/

  • 计算机A:计算机 发送它的位置
  • 电脑B:电脑 检查坑洼的计算机

答案 8 :(得分:0)

是不是可以使用并发或某些预读技术?

我的意思是你的问题正在等待messageQueue。如果你可以异步它,它不会有帮助吗?也许使用回调?

您可以在调用进程B并继续进程A时保存状态。如果进程B回复了一些错误,则停止并将状态恢复为已保存的值。

答案 9 :(得分:0)

您对离散事件模拟有多少经验吗?这个概念是您在日历上预先安排事件,跟踪这些事件在列表中发生的时间,并在到达具有一组规则的任何给定事件时更新系统的状态。您可以有效地在列表中安排未来,而不必担心运行被调用子例程所需的时间。这有意义吗?如果您需要更多信息/参考,请告诉我。

答案 10 :(得分:0)

可能使用Observer而不是消息传递来快速响应?

答案 11 :(得分:0)

我想说应该做的最大改变是 -

删除异步通信即JMS并放置一些同步通信机制。

这可能是RPC调用/ WebService调用。

<强> --- ---更新

刚刚看到你的评论,你无法删除作为更大系统的一方的JMS部分。

然后我们必须接受在JMS消息未到达之前我们无法做出决定。可能在这种情况下可以做的很少......

答案 12 :(得分:0)

使用JMS实现几乎即时的消息传递在这里将是一个艰难的过程。基本上JMS的设计更注重交付保证而不是交付速度。

Here是一些可以帮助您加快JMS交付的点,但在JMS世界中,只能保证交付而不是速度。

我不禁提到你还应该考虑一个缓存解决方案。对于我的一个问题,我对这种类型的缓存有一个很好的answer。 ......它摇滚!