예제 #1
0
def one_tree_topology(adjacency_matrix: np.ndarray) -> Tuple[float, tuple, tuple, set, Dict[int, int]]:
    """ build One tree
    return: длина one tree; минимальное ребро; пред минмальное ребро; set ребер; словарь son -> dad для MST
    """
    size, k, length = adjacency_matrix.shape[0], 0, 0.0
    topology: Dict[int, int] = {}
    visited = np.zeros(size, dtype=bool)
    heap, edges = Heap(), set()

    def add(idx: int):
        visited[idx] = True
        for idy, value in enumerate(adjacency_matrix[idx]):
            if not value > 0 or visited[idy] or idy == 0:
                continue
            heap.push((value, idx, idy))

    add(1)
    while k < size - 2:
        was, price, src, dst = True, 0.0, 0, 0
        while was:
            price, src, dst = heap.pop()
            was = visited[dst]
        topology[dst] = src
        edges.add(make_pair(dst, src))
        add(dst)
        length += adjacency_matrix[src][dst]
        k += 1

    f_node, s_node, f_min, s_min = __search(adjacency_matrix)

    edges.add(make_pair(0, f_node))
    edges.add(make_pair(0, s_node))
    return length + f_min + s_min, (f_node, f_min), (s_node, s_min), edges, topology
예제 #2
0
def _improve(tour: np.ndarray, matrix: np.ndarray, neighbours: np.ndarray, dlb: np.ndarray,
             it1: int, t1: int, solutions: set, k: int) -> Tuple[float, np.ndarray]:
    """ Последовательный 2-opt для эвристики Лина-Кернига
    tour: список городов
    matrix: матрица весов
    neighbours: набор кандидатов
    dlb: don't look bits
    it1, t1: индекс, значение города, с которого начинать
    solutions: полученные ранее туры
    set_x, set_y: наборы удаленных, добавленных ребер
    k: k-opt, k - кол-во сколько можно сделать последовательных улучшений
    return: выигрыш, новый тур
    """
    around_t1 = around(tour, it1)
    for it2, t2 in around_t1:
        set_x = {make_pair(t1, t2)}

        for t3 in neighbours[t2]:
            gain = matrix[t1][t2] - matrix[t2][t3]
            if t3 == around_t1[0][1] or t3 == around_t1[1][1] or not gain > 1.e-10:
                continue
            set_y = {make_pair(t2, t3)}
            it3 = np.where(tour == t3)[0][0]
            _gain, _tour = __choose_t4(tour, matrix, it1, it2, it3, neighbours, gain, set_x, set_y, dlb, solutions, k)
            if _gain > 1.e-10:
                return _gain, _tour

    return 0., tour
예제 #3
0
def __choose_t4(tour: np.ndarray, matrix: np.ndarray, it1: int, it2: int, it3: int, neighbours: np.ndarray,
                gain: float, set_x: set, set_y: set, dlb: np.ndarray, sol: set, k: int) -> Tuple[float, np.ndarray]:
    """ Выбираем город t2i - город, который создаст ребро на удаление
    tour: список городов
    matrix: матрица весов
    it1, it2, it3: города t1, t2i, t2i+1, их индексы
    neighbours: набор кандидатов
    gain: текущий выигрыш
    set_x, set_y: наборы удаленных, добавленных ребер
    dlb: don't look bits
    sol: существующие решения
    k: k-opt, k - кол-во сколько можно сделать последовательных улучшений
    return: выигрыш, новый тур
    """
    t1, t2, t3 = tour[it1], tour[it2], tour[it3]
    around_t3 = around(tour, it3)

    for it4, t4 in around_t3:
        if len(set_y) == k - 1:  # выбираем длиннейшее ребро на последней итерации
            if matrix[t3][around_t3[0][1]] < matrix[t3][around_t3[1][1]] and around_t3[0][1] == t4:
                break
            if matrix[t3][around_t3[0][1]] > matrix[t3][around_t3[1][1]] and around_t3[0][1] == t4:
                break

        t3t4 = make_pair(t3, t4)
        if t3t4 in set_x or t3t4 in set_y:
            continue
        if not __validation(len(tour), it1, it2, it3, it4):  # проверяем на корректность
            continue

        _set_x = set_x.copy()
        _set_y = set_y.copy()
        _set_x.add(t3t4)
        _set_y.add(make_pair(t1, t4))

        _tour = tour.copy()
        _it1, _it4 = __get_tour(_tour, it1, it2, it3, it4)  # единственное место, где меняется тур

        if generate_hash(_tour) in sol:  # проверяем, был ли такой раньше
            continue

        _gain = gain + (matrix[t3][t4] - matrix[t1][t4])
        if _gain > 1.e-10:
            if len(dlb) != 1:
                dlb[t1] = dlb[t2] = dlb[t3] = dlb[t4] = False
            return _gain, _tour
        elif len(_set_x) <= k:
            _gain, _tour = __choose_t5(_tour, matrix, _it1, _it4, neighbours, _gain, _set_x, _set_y, dlb, sol, k)
            if _gain > 1.e-10:
                if len(dlb) != 1:
                    dlb[t1] = dlb[t2] = dlb[t3] = dlb[t4] = False
                return _gain, _tour
        else:
            break

    return 0., tour
예제 #4
0
def __choose_t5(tour: np.ndarray, matrix: np.ndarray, it1: int, it4: int, neighbours: np.ndarray,
                gain: float, set_x: set, set_y: set, dlb: np.ndarray, sol: set, k: int) -> Tuple[float, np.ndarray]:
    """ Выбираем город t2i+1 - город, который создаст ребро на добавление
    tour: список городов
    matrix: матрица весов
    it1, it4: города t1 и t2i, их индексы
    neighbours: набор кандидатов
    gain: текущий выигрыш
    set_x, set_y: наборы удаленных, добавленных ребер
    dlb: don't look bits
    sol: существующие решения
    k: k-opt, k - кол-во сколько можно сделать последовательных улучшений
    return: выигрыш, новый тур
    """
    t1, t4 = tour[it1], tour[it4]
    around_t1 = around(tour, t1)
    for t5 in neighbours[t4]:
        if t5 == around_t1[0][1] or t5 == around_t1[1][1]:
            continue

        t4t5 = make_pair(t4, t5)

        _gain = gain + (matrix[t1][t4] - matrix[t4][t5])
        if not _gain > 1.e-10 or t4t5 in set_x or t4t5 in set_y:
            continue

        _set_y = set_y.copy()
        _set_y.add(t4t5)

        it5 = np.where(tour == t5)[0][0]
        _gain, _tour = __choose_t4(tour, matrix, it1, it4, it5, neighbours, _gain, set_x, _set_y, dlb, sol, k)
        if _gain > 1.e-10:
            return _gain, _tour

    return 0., tour
예제 #5
0
def __get_tour(tour: np.ndarray, towns: np.ndarray) -> Tuple[bool, np.ndarray]:
    """ Собираем тур, для этого разбираем его на ребра
    tour: текущий список городов
    towns: полученные города
    return: получился ли корректный тур, тур
    """
    t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 = towns[0]

    t1t2, t3t4, t5t6, t7t8, t9t10 = \
        make_pair(t1, t2), make_pair(t3, t4), make_pair(t5, t6), make_pair(t7, t8), make_pair(t9, t10)
    t4t5, t6t7, t8t9, t1t10, t2t3 = \
        make_pair(t4, t5), make_pair(t6, t7), make_pair(t8, t9), make_pair(t1, t10), make_pair(t2, t3)

    edges, size = {(0, 0)}, len(tour)
    edges.clear()
    for i in range(size):
        edges.add(make_pair(tour[i - 1], tour[i]))

    edges = (edges - {t1t2, t3t4, t5t6, t7t8, t9t10}) | {
        t4t5, t6t7, t8t9, t1t10, t2t3
    }
    if len(edges) != size:  # добавили ребро, которое уже существет
        return False, tour

    successors, node = nb.typed.Dict.empty(nb.int64, nb.int64), 0
    while len(edges) > 0:
        x, y = 0, 0
        for x, y in edges:
            if x == node:
                successors[node] = y
                node = y
                break
            elif y == node:
                successors[node] = x
                node = x
                break
        edges.remove((x, y))

    idx, runner = 0, successors[0]
    visited = np.zeros(size, dtype=nb.boolean)
    _tour = np.zeros(size, dtype=nb.int64)

    while idx < size:
        if visited[runner]:
            break
        visited[runner] = True
        _tour[idx] = runner
        runner = successors[runner]
        idx += 1

    if idx < size:  # означает, что у нас цикл оказался
        return False, tour
    return True, _tour