Все возможные комбинации чисел длины 5 в матрице 4X4 - PullRequest
0 голосов
/ 02 декабря 2010

у меня есть матрица

1 9 2 3
5 0 0 6
8 4 4 8
2 3 7 8

Мне нужно найти все возможные комбинации чисел длины 5.

Ограничения:

  1. Начиная с любой позиции в матрице, вы можете перейти только к следующему непосредственному соседу, т. Е. Если вы начинаете с формы (0,0), ваш сосед должен быть (0,1), (1,1), (1,0), и если вы выбираете позицию, то из этой позиции вы можете перейти только к ее ближайшему соседу и так далее.

  2. Длина номера должна составлять 5 цифр, т. Е. Например, если я начинаю с (0,0) со значения 1, я могу создать последовательность 15151 или 19232 или 10063, поэтому вы можете перемещаться в любой последовательности с помощью применяется ограничение 1.

  3. Решение должно выдавать результат в 7 секунд, и Python предпочтителен, так как он мой любимый. ;)

Хорошо, я пропустил некоторые вещи, программа должна использовать все 16 чисел, то есть она должна использовать все 16 чисел в качестве начальной и создать последовательность из 5 цифр.

Ответы [ 5 ]

2 голосов
/ 02 декабря 2010

Вот решение в Scala (для версии 5x5 с 9 цифрами, начиная с середины) (с демонстрацией ideone.com ). На моем (довольно медленном) компьютере потребовалось 5 секунд.

object Main {

    val matrix = List(List("1", "9", "2", "3", "1"),
                      List("5", "0", "0", "6", "1"),
                      List("8", "4", "4", "8", "1"),
                      List("8", "4", "4", "8", "1"),
                      List("2", "3", "7", "8", "1"))

    def main(args: Array[String]) : Unit = nums(2, 2, 9) map println

    def nums(r: Int, c: Int, left: Int) : List[String] =
        if (!(matrix isDefinedAt r) || !(matrix(r) isDefinedAt c)) List()
        else if (left == 0) List("")
        else for ((dr, dc) <- List((0,1), (1,0), (-1,0), (0,-1));
                  tail <- nums(r + dr, c + dc, left - 1))
                yield matrix(r)(c) + tail
}

Вот решение на Java (с демонстрацией ideone.com ).

import java.util.*;

public class Test {

    static String[][] matrix = {{"1", "9", "2", "3"},
                                {"5", "0", "0", "6"},
                                {"8", "4", "4", "8"},
                                {"2", "3", "7", "8"}};

    public static void main(String[] args) {
        for (String i : nums(0, 0, 5))
            System.out.println(i);
    }

    public static List<String> nums(int r, int c, int left) {

        if (r < 0 || r >= matrix.length || c < 0 || c >= matrix[r].length)
            return Collections.emptyList();

        if (left == 1)
            return java.util.Arrays.asList(matrix[r][c]);

        ArrayList<String> result = new ArrayList<String>();
        for (int[] delta : new int[][] {{0,1}, {1,0}, {-1,0}, {0,-1}})
            for (String tail : nums(r + delta[0], c + delta[1], left-1))
                result.add(matrix[r][c] + tail);

        return result;
    }
}

Выполнение заняло 8 мс на моем компьютере.


Если вы хотите ускорить его, вам обязательно следует кэшировать результаты. Есть несколько способов сделать два шага вниз и два шага вправо, используя 4 цифры. Все из которых будут иметь одинаковые возможные хвосты (а именно nums(row + 2, col + 2, x)).

0 голосов
/ 03 декабря 2010

Во-первых, вы должны подумать о том, как начать с одной позиции в матрице и перейти к соседней.

Метод грубой силы состоит в том, чтобы просто перечислить все доступные ячейки и все соседние ячейки для каждой:

nextpos = {
    (0,0): [(1,0), (1,1), (0,1)],
    (0,1): [(0,0), (1,0), (1,1), (1,2), (0,2)],
    (0,2): [(0,1), (1,1), (1,2), (1,3), (0,3)],
    (0,3): [(0,2), (1,2), (1,3)],
    # etc
}
allpos = nextpos.keys()

Для такой маленькой проблемы это довольно просто;Однако всегда есть вероятность опечаток.Другое решение - написать функцию генератора:

def nextpos(p,w=4,h=4):
    """
    @param p     Current position tuple (x,y)
    @param w     Width of matrix
    @param h     Height of matrix

    Generate all matrix cells adjacent to the current one
    """
    rel = ((-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1))

    x,y = p
    for dx,dy in rel:
        nx,ny = x+dx, y+dy
        if 0<=nx<w and 0<=ny<h:
            yield (nx,ny)

Как только мы узнаем, какие ячейки находятся рядом друг с другом, мы можем приступить к поиску действительных путей:

def matrix_path(pathLen, startFrom=None):
    """
    @param pathLen    Length of path to return
    @param startFrom  Initial location

    Generate all adjacency paths through the matrix
    """

    # bad path length - quit gracefully
    if pathLen<1:
        yield []
        return

    # no starting point specified - start at any point
    if startFrom is None:
        for pos in allpos:
            for res in matrix_path(pathLen, pos):
                yield res
        return

    # end of path
    if pathLen==1:
        yield [startFrom]
        return

    # from current point, recurse to find rest of path
    for pos in nextpos[startFrom]:
        for morePath in matrix_path(pathLen-1, pos):
            yield [startFrom]+morePath

Мы можем выяснитьсколько времени занимает профилирование:

import cProfile

def test():
    sols = [i for i in matrix_path(5)]
    print len(sols), "paths found"

cProfile.run("test()")

, которое возвращает

16860 найденных путей 121497 вызовов функций (16865 примитивных вызовов) за 0,678 секунд ЦП

Возвращает список списков позиций ячеек;мы хотим преобразовать это в фактические значения из матрицы,

def path_vals(mx, path):
    """
    @param mx    Matrix data
    @param path  List of cell positions

    Return list of values from list of cell positions
    """

    return tuple([mx[x][y] for x,y in path])

Затем

mx = [
    [1,9,2,3],
    [5,0,0,6],
    [8,4,4,8],
    [2,3,7,8]
]

def test():
    sols = [path_vals(mx, i) for i in matrix_path(5)]

Мы также хотим сократить список до уникальных результатов:

def test():
    usol = list(set([path_vals(mx, i) for i in matrix_path(5)]))
    print len(usol),"unique results"

cProfile.run("test()")

, что дает нам

8651 уникальных результатов 138357 вызовов функций (33725 примитивных вызовов) за 0,845 секунды ЦП

0 голосов
/ 03 декабря 2010

Редактировать def foo (x, y, глубина, сек):

def search(x,y,depth,seq):

if depth == 8: 

    return [seq + str(matrix[x][y])]

cur = []

seq = seq + str(matrix[x][y])

if (y - 1) >= 0: 

    cur.extend(search(x,y-1,depth+1,seq))

if (x + 1) < len(matrix[0]): 

    cur.extend(search(x+1,y,depth+1,seq))

if (y + 1) < len(matrix): 

    cur.extend(search(x,y+1,depth+1,seq))

if (x - 1) >= 0: 

    cur.extend(search(x-1,y,depth+1,seq))

if (x + 1) < len(matrix[0]) and (y + 1) < len(matrix): 

    cur.extend(search(x+1,y+1,depth+1,seq))

if (x - 1) >= 0 and (y - 1) >=0: 

    cur.extend(search(x-1,y-1,depth+1,seq))

if (x + 1) < len(matrix[0]) and (y - 1) >= 0: 

    cur.extend(search(x+1,y-1,depth+1,seq))

if (x - 1) >= 0 and (y + 1) < len(matrix): 

    cur.extend(search(x-1,y+1,depth+1,seq))

return cur

matrix = [[1,1,2,3,5],

    [2,3,4,5,5],

    [5,5,2,3,3],

    [9,9,5,4,2],

    [9,9,5,4,2],

    ]

if name == " main ":

print search(0,0,0,"")

Я запустил этот код, чтобы остановиться, потребовалось 19 ~ 20 секунд.это тоже только для одного начального местоположения, и это дало много дублирующих результатов.Я запустил это на 2,2 ГГц процессоре Core 2 Duo, мой старый компьютер завис, и произошел сбой python.exe: p.

0 голосов
/ 02 декабря 2010

Посмотрите на itertools комбинаторные генераторы для вдохновения:

  • продукт ()
  • перестановок ()
  • комбинаций ()
  • combinations_with_replacement ()
0 голосов
/ 02 декабря 2010

Вы можете написать это довольно легко с помощью рекурсии и возврата.

Иметь рекурсивную функцию в соответствии с:

Foo (х, у, глубина)

и затем рекурсивно попытайтесь переместить каждое направление, которое является внутренним и допустимым. Когда максимальная глубина (5) достигнута, у вас есть номер. Тогда это просто бухгалтерия для создания и объединения списков.

1009 ** * Редактировать 1010 ** * 1011

Немного больше информации ..

foo( x, y, depth, seq )

где seq - строка последовательности до этой точки. Возвращает список строк, которые являются решениями.

Базовый случай: глубина равна 5, в этом случае просто вернуть список с текущим числом, объединенным в seq.

В противном случае проверьте границу для каждого направления и, если она действительна, вызовите foo с правильными координатами, глубиной +1 и текущим числом, объединенным в seq. Объедините все полученные списки вместе и верните это.

Вызовите эту функцию для каждого начального пятна с глубиной один, последовательностью "", а затем объедините все 16 из этих результатов вместе.

Обратите внимание, что, поскольку это рекурсивное решение, оно прекрасно работает для этой проблемы, но может очень медленно работать. Хотя, поскольку он ограничен по глубине, я бы не ожидал, что это станет большой проблемой.

1503 уникальные решения

Редактировать 2

моя функция поиска очень похожа на ту, что была у вас, но посмотрите, какие границы проверяются и какие значения возвращаются и т. Д.

def foo(x,y,depth,seq):
    if depth == 5: return [seq + str(matrix[x][y])]

    cur = []
    seq = seq + str(matrix[x][y])
    if (y - 1) >= 0: cur.extend(search(x,y-1,depth+1,seq))
    if (x + 1) < len(matrix[0]): cur.extend(search(x+1,y,depth+1,seq))
    if (y + 1) < len(matrix): cur.extend(search(x,y+1,depth+1,seq))
    if (x - 1) >= 0: cur.extend(search(x-1,y,depth+1,seq))

    return cur  

То, как я проверяю четыре направления, можно очистить и сделать более «питоническим», но это было больше, чем просто подтверждение концепции, что вы можете решить проблему достаточно быстро.

...