【问题标题】:Simulation and Ad-Hoc Optimization Algorithm模拟和 Ad-Hoc 优化算法
【发布时间】:2014-02-09 00:41:38
【问题描述】:

在一个测量 N x M 的矩形场中有镜子 正方形(1 / 表示(镜像连接 左下角到右上角)和\(a 连接左上角和右下角的镜子)。

考虑向这个方格发射光束。您可以沿网格的某列或某行垂直或水平发射光束。 这会导致光束根据排列反射特定顺序的镜子。当一束光射到镜子上时,由于镜子都是对角定向的,被反射的垂直光束将开始向水平方向移动,反之亦然。

可以反射光束的最大镜子数量是多少 如果光可以无限期地反射,答案是-1。因此,考虑到网格的排列,问题是计算这个最大数

例如: 一个 3 x 3 的网格,配置如下:

/\\
\\\
/\/

将输出:

3

约束:网格最大可达 1000 x 1000 大

向中间的柱子照射光束可以得到 3。

我的解决方案:

从每个可能的位置(所有外边缘位置)射出光束。模拟这些光束并在光束退出时完成计数。如果光束再次击中同一位置,则输出 -1。 我的解决方案仅适用于小案例,但不适用于网格超过 100 x 100 的较大案例,完成时间太长。

我想把它降到 O(200 万)以下。

您能否推荐一些算法来提供帮助?

【问题讨论】:

  • 几个想法:1)光束永远不会被无限期地反射。 2) 光束将始终以不同于其起始位置的方格离开网格。这都是因为光束永远不会完全击中镜子。所以不要费心去寻找循环,你最终只需要对 一半 可能的输入光束运行完整的模拟。就是说,您的算法似乎很合理 - 减速可能在您的代码中,而不是您的整体方法。

标签: algorithm optimization adhoc


【解决方案1】:

这听起来是个有趣的问题。 @Xavier Holt 的评论可能非常重要:“光束永远不会被无限期地反射”。每个旨在跟踪已访问字段(即检查某个字段是否被访问两次)的数据结构 - 在最坏的情况下 - 都会显着减慢整个过程。

mcdowella 建议的基于图形的方法在这里可能是可行的。但是由于光束如何穿过镜子网格的规则非常简单(并且,如上所述,循环检测等不是必需的),因此可以将其归结为穿过 2D 阵列。当前状态由当前位置 (x,y) 和当前方向 (dy,dy) 表示,根据遇到的每种镜像类型进行更新。

我刚刚实现了这个(在 Java 中)。 computeResults 方法计算 5 元素 int[] 数组的列表,其中

  • 结果[0] = 输入 x 坐标
  • 结果[1] = 输入 y 坐标
  • 结果[2] = 进入 x 方向
  • 结果[3] = 进入 y 方向
  • result[4] = 离开镜像场之前的步数

“核心逻辑”在simulate 方法中。

有些部分(尤其是渲染字段和解决方案路径到框架中的部分)相当快且非常脏,但也许有人觉得它很有趣。在任何情况下,它都能在几秒钟内在一台非常旧的 PC 上计算出 2000x2000 网格的解。

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class MirrorTracer
{
    public static void main(String[] args)
    {
        //basicTest();
        largerTest();
    }

    private static void basicTest()
    {
        String input =
            "SBB" +
            "BBB" +
            "SBS";
        int sizeX = 3;
        int sizeY = 3;
        int array[][] = createArray(input, sizeX, sizeY);

//        int n = simulate(array, 2, 0, 0, 1);
//        System.out.println(n);

        List<int[]> results = computeResults(array, sizeX, sizeY);
        printResults(array, sizeX, sizeY, results);
    }

    private static void largerTest()
    {
        int sizeX = 60;
        int sizeY = 60;
        int array[][] = createRandomArray(sizeX, sizeY, new Random(0));

        List<int[]> results = computeResults(array, sizeX, sizeY);
        printResults(array, sizeX, sizeY, results);

        showResult(array, sizeX, sizeY, findBestResult(results));
    }



    private static List<int[]> computeResults(int array[][], int sizeX, int sizeY)
    {
        List<int[]> results = new ArrayList<int[]>();
        for (int x=1; x<sizeX+1; x++)
        {
            results.add(compute(array, x, 0, 0, 1));
            //results.add(compute(array, x, sizeY+1, 0, -1));
        }
        for (int y=1; y<sizeY+1; y++)
        {
            results.add(compute(array, 0, y, 1, 0));
            //results.add(compute(array, sizeX+1, y, -1, 0));
        }
        return results;
    }

    private static int[] compute(int array[][], int x, int y, int dx, int dy)
    {
        int nx = x + dx;
        int ny = y + dy;
        int n = simulate(array, x, y, dx, dy);
        return new int[]{ nx-1, ny-1, dx, dy, n };
    }

    private static int simulate(int array[][], int x, int y, int dx, int dy)
    {
        int steps = 0;
        while (true)
        {
            int nx = x + dx;
            int ny = y + dy;
            if (isOnBorder(array, nx, ny))
            {
                break;
            }
            //System.out.println("Move from "+x+" "+y+" in "+dx+" "+dy+" to "+nx+" "+ny);
            int ndx = dy;
            int ndy = dx;
            if (array[nx][ny] == '/')
            {
                ndx = -dy;
                ndy = -dx;
            }
            x = nx;
            y = ny;
            dx = ndx;
            dy = ndy;
            steps++;
        }
        return steps;
    }

    private static boolean isOnBorder(int array[][], int x, int y)
    {
        return 
            x == 0 || x == array.length - 1 ||
            y == 0 || y == array[x].length - 1;
    }







    private static int[][] createArray(String input, int sizeX, int sizeY)
    {
        int array[][] = new int[sizeX+2][sizeY+2];
        for (int y=1; y<sizeY+1; y++)
        {
            for (int x=1; x<sizeX+1; x++)
            {
                char c = input.charAt((x-1) + (y-1) * sizeX);
                array[x][y] = c == 'S' ? '/' : '\\';
            }
        }
        return array;
    }

    private static int[][] createRandomArray(
        int sizeX, int sizeY, Random random)
    {
        int array[][] = new int[sizeX+2][sizeY+2];
        for (int y=1; y<sizeY+1; y++)
        {
            for (int x=1; x<sizeX+1; x++)
            {
                boolean b = random.nextBoolean();
                array[x][y] = b ? '/' : '\\';
            }
        }
        return array;
    }


    private static void printResults(
        int array[][], int sizeX, int sizeY, List<int[]> results)
    {
        print(array, sizeX, sizeY);
        for (int result[] : results)
        {
            printResult(result);
        }

        int bestResult[] = findBestResult(results);
        System.out.println("Longest sequence:");
        printResult(bestResult);
    }

    private static void print(int array[][], int sizeX, int sizeY)
    {
        for (int y=1; y<sizeY+1; y++)
        {
            for (int x=1; x<sizeX+1; x++)
            {
                System.out.print((char)array[x][y]);
            }
            System.out.println();
        }
    }

    private static int[] findBestResult(List<int[]> results)
    {
        int maxLength = -1;
        int maxLengthResult[] = null;
        for (int result[] : results)
        {
            if (result[4] > maxLength)
            {
                maxLength = result[4];
                maxLengthResult = result;
            }
        }
        return maxLengthResult;

    }

    private static void printResult(int result[])
    {
        int x = result[0];
        int y = result[1];
        int dx = result[2];
        int dy = result[3];
        int n = result[4];
        System.out.println("Entering at "+x+" "+y+" in direction "+dx+" "+dy+" does "+n+" steps");
    }





    private static void showResult(
        final int array[][], final int sizeX, final int sizeY, 
        final int bestResult[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI(array, sizeX, sizeY, bestResult);
            }
        });
    }

    private static void createAndShowGUI(
        final int array[][], final int sizeX, final int sizeY, 
        final int bestResult[])
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel resultPanel = new JPanel()
        {
            protected void paintComponent(Graphics g) 
            {
                super.paintComponent(g);

                int cellSizeX = getWidth() / (sizeX+2);
                int cellSizeY = getHeight() / (sizeY+2);

                g.setColor(Color.BLACK);
                for (int y=1; y<sizeY+1; y++)
                {
                    for (int x=1; x<sizeX+1; x++)
                    {
                        int x0 = x * cellSizeX;
                        int y0 = y * cellSizeY;
                        int x1 = x0 + cellSizeX;
                        int y1 = y0 + cellSizeY;
                        if (array[x][y] == '/')
                        {
                            g.drawLine(x0+2, y1-2, x1-2, y0+2);
                        }
                        else
                        {
                            g.drawLine(x0+2, y0+2, x1-2, y1-2);
                        }
                    }
                }
                g.setColor(Color.RED);
                int dx = bestResult[2];
                int dy = bestResult[3];
                int x = bestResult[0]-dx+1;
                int y = bestResult[1]-dy+1;
                paintSimulation(g, array, x, y, dx, dy, cellSizeX, cellSizeY);
            }

            private int paintSimulation(
                Graphics g, int array[][], int x, int y, 
                int dx, int dy, int cellSizeX, int cellSizeY)
            {
                int steps = 0;
                while (true)
                {
                    int nx = x + dx;
                    int ny = y + dy;

                    int x0 = x * cellSizeX + cellSizeX / 2;
                    int y0 = y * cellSizeY + cellSizeY / 2;
                    int x1 = nx * cellSizeX + cellSizeX / 2;
                    int y1 = ny * cellSizeY + cellSizeY / 2;
                    g.drawLine(x0, y0, x1, y1);

                    if (isOnBorder(array, nx, ny))
                    {
                        break;
                    }

                    int ndx = dy;
                    int ndy = dx;
                    if (array[nx][ny] == '/')
                    {
                        ndx = -dy;
                        ndy = -dx;
                    }
                    x = nx;
                    y = ny;
                    dx = ndx;
                    dy = ndy;
                    steps++;
                }
                return steps;
            }

        };

        f.getContentPane().add(resultPanel);
        f.setSize(800,800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }


}

【讨论】:

    【解决方案2】:

    我认为您可以将此视为有关图表的问题。镜子在哪里/左上角有一个节点,右下角有一个节点。镜子在哪里,右上角有一个节点,左下角有一个节点。您可以根据镜像设置将它们连接到相邻网格点中的节点。这为您提供了一个图表,该图表可能由许多不连贯的循环或路径组成,但仍然是一个图表。

    第一个问题 - 图表是否包含任何循环?您可以通过深度优先搜索有效地回答这个问题。如果是这样,根据具体的规则,您可以通过在循环内沿着柱子射击光束来无限期地反射光。

    第二个问题 - 最长的路径不是循环是什么?您可以使用第一次深度优先搜索将图形拆分为连接的组件,并丢弃那些包含循环的组件。其余的组件将是简单的路径,因此应该很容易计算出它们的长度。也有几种方法可以快速为树执行此操作 - 我在搜索中找到的第一个方法是 http://www.geeksforgeeks.org/diameter-of-a-binary-tree/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-03
      • 2021-01-27
      • 2011-07-03
      • 2015-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多