Question

There are mirrors in a rectangular field measuring N by M squares (1 <= N, M <= 1,000). In each square, there are double sided mirror between two opposite corners. These two possible configurations are represented by / (a mirror connecting the lower-left corner to the upper-right corner) and \ (a mirror connecting the upper-left corner to the lower-right corner).

Consider shooting a beam into this square. You are allowed to shoot the beam either vertically or horizontally along either some column or row of the grid. This causes the beam to bounce of a certain order of mirrors based on the arrangement. When a beam of light hits a mirror, because the mirrors are all diagonally oriented, a vertical beam of light that is reflected will begin to go in the horizontal direction, and vice versa.

What is the maximum number of mirrors of which the beam of light can be reflected If the light can be reflected indefinitely the answer is -1. Thus, given the arrangement of the grid the question is to compute this maximum number

For example : A grid that is 3 x 3 with a configuration like this:

/\\
\\\
/\/

will have an output of :

3

Constraints: The Grid can be up to 1000 x 1000 big

You get 3 by shining a beam down the middle column.

My Solution:

Shoot the beam from each possible locations (all the outer edge locations). Simulate these beams and finish count when beam exits. If the beam hits the same location again output -1. My solution only works on small cases but not on bigger cases where the grid is over 100 x 100, it takes too long to finish.

I want to get it down to under O( 2 million ).

Could you please suggest some algorithms to help?

Was it helpful?

Solution

That sounded like an interesting problem. The commment from @Xavier Holt could be pretty important: "A beam can never be reflected indefinitely". Every data structure that aims at tracking the visited fields (that is, checking whether a certain field was visited twice) could - in the worst case - significantly slow down the whole thing.

A graph-based approach, as suggested by mcdowella, could be feasible here. But since the rules of how the beam moves through the grid of mirrors are quite simple (and, as mentioned above, cycle detection etc. are not necessary), one can boil this down do walking through a 2D array. The current state is then represented by the current position (x,y), and the current direction (dy,dy), which is updated according to each mirror type that is encountered.

I just implemented this (in Java). The computeResults method computes a list of 5-element int[] arrays, where

  • result[0] = Entering x-coordiante
  • result[1] = Entering y-coordiante
  • result[2] = Entering x-direction
  • result[3] = Entering y-direction
  • result[4] = Number of steps until the mirror field is left

The "core logic" is in the simulate method.

Some parts (especially the part that renders the field and the solution path into a frame) are rather quick & very dirty, but maybe someone finds it interesting anyhow. In any case, it computes the solution of a 2000x2000 grid in a few seconds on a very old PC.

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);
    }


}

OTHER TIPS

I think you can treat this as a problem about graphs. Where the mirror is / you have a node in the top left and a node in the bottom right. Where the mirror is \ you have a node in the top right and a node in the bottom left. You can connect these up to nodes in neighboring grid points depending on their mirror settings. This gives you a graph which probably consists of a number of disconnected cycles or paths, but is still a graph.

First question - does the graph contain any cycles? You can answer this efficiently with depth first search. If so, depending on exactly what the rules are, you may be able to get the light reflected indefinitely by shooting the beam along a column within the cycle.

Second question - what is the longest path not a cycle? You can use the first depth first search to split the graph into connected components, and discard those containing cycles. The remaining components will be simple paths, so it should be easy to work out their length. There are a couple of ways to do this quickly for trees, as well - the first one I found in a search was at http://www.geeksforgeeks.org/diameter-of-a-binary-tree/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top