Esempio n. 1
0
 def __new__(cls, comp, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     obj.items = BinaryHeap()
     obj.items._comp = comp
     return obj
Esempio n. 2
0
 def __new__(cls, elements=None, heap_property="min", d=4, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     obj = Heap.__new__(cls)
     obj.heap_property = heap_property
     obj.d = d
     if heap_property == "min":
         obj._comp = lambda key_parent, key_child: key_parent <= key_child
     elif heap_property == "max":
         obj._comp = lambda key_parent, key_child: key_parent >= key_child
     else:
         raise ValueError("%s is invalid heap property" % (heap_property))
     if elements is None:
         elements = DynamicOneDimensionalArray(TreeNode, 0)
     elif _check_type(elements, (list, tuple)):
         elements = DynamicOneDimensionalArray(TreeNode, len(elements),
                                               elements)
     elif _check_type(elements, Array):
         elements = DynamicOneDimensionalArray(TreeNode, len(elements),
                                               elements._data)
     else:
         raise ValueError(
             f'Expected a list/tuple/Array of TreeNode got {type(elements)}'
         )
     obj.heap = elements
     obj._last_pos_filled = obj.heap._last_pos_filled
     obj._build()
     return obj
Esempio n. 3
0
 def __new__(cls, comp, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     obj.items = SinglyLinkedList()
     obj.comp = comp
     return obj
Esempio n. 4
0
def strongly_connected_components(graph, algorithm, **kwargs):
    """
    Computes strongly connected components for the given
    graph and algorithm.

    Parameters
    ==========

    graph: Graph
        The graph whose minimum spanning tree
        has to be computed.
    algorithm: str
        The algorithm which should be used for
        computing strongly connected components.
        Currently the following algorithms are
        supported,

        'kosaraju' -> Kosaraju's algorithm as given in [1].
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    components: list
        Python list with each element as set of vertices.

    Examples
    ========

    >>> from pydatastructs import Graph, AdjacencyListGraphNode
    >>> from pydatastructs import strongly_connected_components
    >>> v1, v2, v3 = [AdjacencyListGraphNode(i) for i in range(3)]
    >>> g = Graph(v1, v2, v3)
    >>> g.add_edge(v1.name, v2.name)
    >>> g.add_edge(v2.name, v3.name)
    >>> g.add_edge(v3.name, v1.name)
    >>> scc = strongly_connected_components(g, 'kosaraju')
    >>> scc == [{'2', '0', '1'}]
    True

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm

    """
    raise_if_backend_is_not_python(
        strongly_connected_components, kwargs.get('backend', Backend.PYTHON))
    import pydatastructs.graphs.algorithms as algorithms
    func = "_strongly_connected_components_" + algorithm + "_" + graph._impl
    if not hasattr(algorithms, func):
        raise NotImplementedError(
        "Currently %s algoithm for %s implementation of graphs "
        "isn't implemented for finding strongly connected components."
        %(algorithm, graph._impl))
    return getattr(algorithms, func)(graph)
Esempio n. 5
0
def is_ordered(array, **kwargs):
    """
    Checks whether the given array is ordered or not.

    Parameters
    ==========

    array: OneDimensionalArray
        The array which is to be checked for having
        specified ordering among its elements.
    start: int
        The starting index of the portion of the array
        under consideration.
        Optional, by default 0
    end: int
        The ending index of the portion of the array
        under consideration.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for specifying the desired ordering.
        Optional, by default, less than or
        equal to is used for comparing two
        values.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    True if the specified ordering is present
    from start to end (inclusive) otherwise False.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, is_ordered
    >>> arr = OneDimensionalArray(int, [1, 2, 3, 4])
    >>> is_ordered(arr)
    True
    >>> arr1 = OneDimensionalArray(int, [1, 2, 3])
    >>> is_ordered(arr1, start=0, end=1, comp=lambda u, v: u > v)
    False
    """
    raise_if_backend_is_not_python(is_ordered,
                                   kwargs.get('backend', Backend.PYTHON))
    lower = kwargs.get('start', 0)
    upper = kwargs.get('end', len(array) - 1)
    comp = kwargs.get("comp", lambda u, v: u <= v)

    for i in range(lower + 1, upper + 1):
        if array[i] is None or array[i - 1] is None:
            continue
        if comp(array[i], array[i - 1]):
            return False
    return True
Esempio n. 6
0
 def __new__(cls, **kwargs):
     raise_if_backend_is_not_python(
         cls, kwargs.get('backend', Backend.PYTHON))
     obj = LinkedList.__new__(cls)
     obj.head = None
     obj.tail = None
     obj.size = 0
     return obj
Esempio n. 7
0
 def __new__(cls, array, func, **kwargs):
     raise_if_backend_is_not_python(
         cls, kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     sparse_table = SparseTable(array, func)
     obj.bounds = (0, len(array))
     obj.sparse_table = sparse_table
     return obj
Esempio n. 8
0
 def __new__(cls, *vertices, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     for vertex in vertices:
         obj.__setattr__(vertex.name, vertex)
     obj.vertices = [vertex.name for vertex in vertices]
     obj.edge_weights = {}
     return obj
Esempio n. 9
0
 def __new__(cls, **kwargs):
     raise_if_backend_is_not_python(
         cls, kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     obj.head, obj.tail = None, None
     obj._num_nodes = 0
     obj._levels = 0
     obj._add_level()
     return obj
Esempio n. 10
0
def all_pair_shortest_paths(graph: Graph, algorithm: str,
                            **kwargs) -> tuple:
    """
    Finds shortest paths between all pairs of vertices in the given graph.

    Parameters
    ==========

    graph: Graph
        The graph under consideration.
    algorithm: str
        The algorithm to be used. Currently, the following algorithms
        are implemented,

        'floyd_warshall' -> Floyd Warshall algorithm as given in [1].
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    (distances, predecessors): (dict, dict)

    Examples
    ========

    >>> from pydatastructs import Graph, AdjacencyListGraphNode
    >>> from pydatastructs import all_pair_shortest_paths
    >>> V1 = AdjacencyListGraphNode("V1")
    >>> V2 = AdjacencyListGraphNode("V2")
    >>> V3 = AdjacencyListGraphNode("V3")
    >>> G = Graph(V1, V2, V3)
    >>> G.add_edge('V2', 'V3', 10)
    >>> G.add_edge('V1', 'V2', 11)
    >>> G.add_edge('V3', 'V1', 5)
    >>> dist, _ = all_pair_shortest_paths(G, 'floyd_warshall')
    >>> dist['V1']['V3']
    21
    >>> dist['V3']['V1']
    5

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
    """
    raise_if_backend_is_not_python(
        all_pair_shortest_paths, kwargs.get('backend', Backend.PYTHON))
    import pydatastructs.graphs.algorithms as algorithms
    func = "_" + algorithm + "_" + graph._impl
    if not hasattr(algorithms, func):
        raise NotImplementedError(
        "Currently %s algorithm isn't implemented for "
        "finding shortest paths in graphs."%(algorithm))
    return getattr(algorithms, func)(graph)
Esempio n. 11
0
def linear_search(array, value, **kwargs):
    """
    Implements linear search algorithm.

    Parameters
    ==========

    array: OneDimensionalArray
        The array which is to be searched.
    value:
        The value which is to be searched
        inside the array.
    start: int
        The starting index of the portion
        which is to be searched.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be searched.
        Optional, by default the index
        of the last position filled.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    output: int
        The index of value if found.
        If not found, returns None.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, linear_search
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> linear_search(arr, 2)
    1

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Linear_search
    """
    raise_if_backend_is_not_python(linear_search,
                                   kwargs.get('backend', Backend.PYTHON))
    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)

    for i in range(start, end + 1):
        if array[i] == value:
            return i

    return None
Esempio n. 12
0
 def __new__(cls, items=None, dtype=NoneType, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if items is None:
         items = DynamicOneDimensionalArray(dtype, 0)
     else:
         items = DynamicOneDimensionalArray(dtype, items)
     obj = object.__new__(cls)
     obj.items = items
     return obj
Esempio n. 13
0
 def __new__(cls, implementation='array', **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if implementation == 'array':
         return ArrayStack(kwargs.get('items', None),
                           kwargs.get('dtype', int))
     if implementation == 'linked_list':
         return LinkedListStack(kwargs.get('items', None))
     raise NotImplementedError("%s hasn't been implemented yet." %
                               (implementation))
Esempio n. 14
0
def topological_sort_parallel(graph: Graph, algorithm: str, num_threads: int,
                              **kwargs) -> list:
    """
    Performs topological sort on the given graph using given algorithm using
    given number of threads.

    Parameters
    ==========

    graph: Graph
        The graph under consideration.
    algorithm: str
        The algorithm to be used.
        Currently, following are supported,

        'kahn' -> Kahn's algorithm as given in [1].
    num_threads: int
        The maximum number of threads to be used.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    list
        The list of topologically sorted vertices.

    Examples
    ========

    >>> from pydatastructs import Graph, AdjacencyListGraphNode, topological_sort_parallel
    >>> v_1 = AdjacencyListGraphNode('v_1')
    >>> v_2 = AdjacencyListGraphNode('v_2')
    >>> graph = Graph(v_1, v_2)
    >>> graph.add_edge('v_1', 'v_2')
    >>> topological_sort_parallel(graph, 'kahn', 1)
    ['v_1', 'v_2']

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
    """
    raise_if_backend_is_not_python(
        topological_sort_parallel, kwargs.get('backend', Backend.PYTHON))
    import pydatastructs.graphs.algorithms as algorithms
    func = "_" + algorithm + "_" + graph._impl + '_parallel'
    if not hasattr(algorithms, func):
        raise NotImplementedError(
        "Currently %s algorithm isn't implemented for "
        "performing topological sort on %s graphs."%(algorithm, graph._impl))
    return getattr(algorithms, func)(graph, num_threads)
Esempio n. 15
0
 def __new__(cls, root_list=None, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if root_list is None:
         root_list = []
     if not all((_check_type(root, BinomialTree)) for root in root_list):
         raise TypeError("The root_list should contain "
                         "references to objects of BinomialTree.")
     obj = Heap.__new__(cls)
     obj.root_list = root_list
     return obj
Esempio n. 16
0
 def __new__(cls, items=None, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     obj.stack = SinglyLinkedList()
     if items is None:
         pass
     elif type(items) in (list, tuple):
         for x in items:
             obj.push(x)
     else:
         raise TypeError("Expected type: list/tuple")
     return obj
 def __new__(cls, segs, **kwargs):
     raise_if_backend_is_not_python(
         cls, kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     if any((not isinstance(seg, (tuple, list, set)) or len(seg) != 2)
             for seg in segs):
                 raise ValueError('%s is invalid set of intervals'%(segs))
     for i in range(len(segs)):
         segs[i] = list(segs[i])
         segs[i].sort()
     obj.segments = list(segs)
     obj.tree, obj.root_idx, obj.cache = [], None, False
     return obj
Esempio n. 18
0
 def __new__(cls, implementation='binary_heap', **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     comp = kwargs.get("comp", lambda u, v: u < v)
     if implementation == 'linked_list':
         return LinkedListPriorityQueue(comp)
     elif implementation == 'binary_heap':
         return BinaryHeapPriorityQueue(comp)
     elif implementation == 'binomial_heap':
         return BinomialHeapPriorityQueue()
     else:
         raise NotImplementedError(
             "%s implementation is not currently supported "
             "by priority queue.")
Esempio n. 19
0
    def __new__(cls, array, func, data_structure='sparse_table', **kwargs):
        raise_if_backend_is_not_python(
            cls, kwargs.get('backend', Backend.PYTHON))
        if len(array) == 0:
            raise ValueError("Input %s array is empty."%(array))

        if data_structure == 'array':
            return RangeQueryStaticArray(array, func)
        elif data_structure == 'sparse_table':
            return RangeQueryStaticSparseTable(array, func)
        else:
            raise NotImplementedError(
                "Currently %s data structure for range "
                "query without updates isn't implemented yet."
                % (data_structure))
Esempio n. 20
0
 def __new__(cls, root=None, order=None, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if root is not None and \
         not _check_type(root, BinomialTreeNode):
         raise TypeError("%s i.e., root should be of "
                         "type BinomialTreeNode." % (root))
     if order is not None and not _check_type(order, int):
         raise TypeError("%s i.e., order should be of "
                         "type int." % (order))
     obj = object.__new__(cls)
     if root is not None:
         root.is_root = True
     obj.root = root
     obj.order = order
     return obj
Esempio n. 21
0
 def __new__(cls, items=None, dtype=NoneType, double_ended=False, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if items is None:
         items = DynamicOneDimensionalArray(dtype, 0)
     else:
         dtype = type(items[0])
         items = DynamicOneDimensionalArray(dtype, items)
     obj = object.__new__(cls)
     obj.items, obj._front = items, -1
     if items.size == 0:
         obj._front = -1
         obj._rear = -1
     else:
         obj._front = 0
         obj._rear = items._num - 1
     obj._double_ended = double_ended
     return obj
Esempio n. 22
0
 def __new__(cls,
             key=None,
             root_data=None,
             comp=None,
             is_order_statistic=False,
             max_children=2,
             **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     obj = object.__new__(cls)
     if key is None and root_data is not None:
         raise ValueError('Key required.')
     key = None if root_data is None else key
     root = MAryTreeNode(key, root_data)
     root.is_root = True
     obj.root_idx = 0
     obj.max_children = max_children
     obj.tree, obj.size = ArrayForTrees(MAryTreeNode, [root]), 1
     obj.comparator = lambda key1, key2: key1 < key2 \
                     if comp is None else comp
     obj.is_order_statistic = is_order_statistic
     return obj
Esempio n. 23
0
    def __new__(cls, array, func, **kwargs):
        raise_if_backend_is_not_python(
            cls, kwargs.get('backend', Backend.PYTHON))

        if len(array) == 0:
            raise ValueError("Input %s array is empty."%(array))

        obj = object.__new__(cls)
        size = len(array)
        log_size = int(math.log2(size)) + 1
        obj._table = [OneDimensionalArray(int, log_size) for _ in range(size)]
        obj.func = func

        for i in range(size):
            obj._table[i][0] = func((array[i],))

        for j in range(1, log_size + 1):
            for i in range(size - (1 << j) + 1):
                obj._table[i][j] = func((obj._table[i][j - 1],
                                         obj._table[i + (1 << (j - 1))][j - 1]))

        return obj
Esempio n. 24
0
    def __new__(cls, dtype: type = NoneType, *args, **kwargs):
        raise_if_backend_is_not_python(
            cls, kwargs.get('backend', Backend.PYTHON))
        if dtype is NoneType:
            raise ValueError("Data type is not defined.")
        elif not args:
            raise ValueError("Too few arguments to create a "
                             "multi dimensional array, pass dimensions.")
        if len(args) == 1:
            obj = Array.__new__(cls)
            obj._dtype = dtype
            obj._sizes = (args[0], 1)
            obj._data = [None] * args[0]
            return obj

        dimensions = args
        for dimension in dimensions:
            if dimension < 1:
                raise ValueError("Size of dimension cannot be less than 1")
        n_dimensions = len(dimensions)
        d_sizes = []
        index = 0
        while n_dimensions > 1:
            size = dimensions[index]
            for i in range(index+1,  len(dimensions)):
                size = size * dimensions[i]
            d_sizes.append(size)
            n_dimensions -= 1
            index += 1
        d_sizes.append(dimensions[index])
        d_sizes.append(1)
        obj = Array.__new__(cls)
        obj._dtype = dtype
        obj._sizes = tuple(d_sizes)
        obj._data = [None] * obj._sizes[1] * dimensions[0]
        return obj
Esempio n. 25
0
def quick_sort(array: Array, **kwargs) -> Array:
    """
    Performs quick sort on the given array.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.
    pick_pivot_element: lambda/function
        The function implementing the pivot picking
        logic for quick sort. Should accept, `low`,
        `high`, and `array` in this order, where `low`
        represents the left end of the current partition,
        `high` represents the right end, and `array` is
        the original input array to `quick_sort` function.
        Optional, by default, picks the element at `high`
        index of the current partition as pivot.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    output: Array
        The sorted array.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray as ODA, quick_sort
    >>> arr = ODA(int, [5, 78, 1, 0])
    >>> out = quick_sort(arr)
    >>> str(out)
    '[0, 1, 5, 78]'
    >>> arr = ODA(int, [21, 37, 5])
    >>> out = quick_sort(arr)
    >>> str(out)
    '[5, 21, 37]'

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Quicksort
    """
    raise_if_backend_is_not_python(quick_sort,
                                   kwargs.get('backend', Backend.PYTHON))
    from pydatastructs import Stack
    comp = kwargs.get("comp", lambda u, v: u <= v)
    pick_pivot_element = kwargs.get("pick_pivot_element",
                                    lambda low, high, array: array[high])

    def partition(low, high, pick_pivot_element):
        i = (low - 1)
        x = pick_pivot_element(low, high, array)
        for j in range(low, high):
            if _comp(array[j], x, comp) is True:
                i = i + 1
                array[i], array[j] = array[j], array[i]
        array[i + 1], array[high] = array[high], array[i + 1]
        return (i + 1)

    lower = kwargs.get('start', 0)
    upper = kwargs.get('end', len(array) - 1)
    stack = Stack()

    stack.push(lower)
    stack.push(upper)

    while stack.is_empty is False:
        high = stack.pop()
        low = stack.pop()
        p = partition(low, high, pick_pivot_element)
        if p - 1 > low:
            stack.push(low)
            stack.push(p - 1)
        if p + 1 < high:
            stack.push(p + 1)
            stack.push(high)

    if _check_type(array, DynamicArray):
        array._modify(force=True)

    return array
Esempio n. 26
0
def longest_common_subsequence(seq1: OneDimensionalArray,
                               seq2: OneDimensionalArray,
                               **kwargs) -> OneDimensionalArray:
    """
    Finds the longest common subsequence between the
    two given sequences.

    Parameters
    ========

    seq1: OneDimensionalArray
        The first sequence.
    seq2: OneDimensionalArray
        The second sequence.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    output: OneDimensionalArray
        The longest common subsequence.

    Examples
    ========

    >>> from pydatastructs import longest_common_subsequence as LCS, OneDimensionalArray as ODA
    >>> arr1 = ODA(str, ['A', 'B', 'C', 'D', 'E'])
    >>> arr2 = ODA(str, ['A', 'B', 'C', 'G' ,'D', 'E', 'F'])
    >>> lcs = LCS(arr1, arr2)
    >>> str(lcs)
    "['A', 'B', 'C', 'D', 'E']"
    >>> arr1 = ODA(str, ['A', 'P', 'P'])
    >>> arr2 = ODA(str, ['A', 'p', 'P', 'S', 'P'])
    >>> lcs = LCS(arr1, arr2)
    >>> str(lcs)
    "['A', 'P', 'P']"

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Longest_common_subsequence_problem

    Note
    ====

    The data types of elements across both the sequences
    should be same and should be comparable.
    """
    raise_if_backend_is_not_python(longest_common_subsequence,
                                   kwargs.get('backend', Backend.PYTHON))
    row = len(seq1)
    col = len(seq2)
    check_mat = {0: [(0, []) for _ in range(col + 1)]}

    for i in range(1, row + 1):
        check_mat[i] = [(0, []) for _ in range(col + 1)]
        for j in range(1, col + 1):
            if seq1[i - 1] == seq2[j - 1]:
                temp = check_mat[i - 1][j - 1][1][:]
                temp.append(seq1[i - 1])
                check_mat[i][j] = (check_mat[i - 1][j - 1][0] + 1, temp)
            else:
                if check_mat[i - 1][j][0] > check_mat[i][j - 1][0]:
                    check_mat[i][j] = check_mat[i - 1][j]
                else:
                    check_mat[i][j] = check_mat[i][j - 1]

    return OneDimensionalArray(seq1._dtype, check_mat[row][col][-1])
Esempio n. 27
0
def lower_bound(array, value, **kwargs):
    """
    Finds the the index of the first occurence of an element which is not
    less than the given value according to specified order,
    in the given OneDimensionalArray using a variation of binary search method.

    Parameters
    ==========

    array: OneDimensionalArray
        The array in which the lower bound has to be found.
    start: int
        The staring index of the portion of the array in which the upper bound
        of a given value has to be looked for.
        Optional, by default 0
    end: int, optional
        The ending index of the portion of the array in which the upper bound
        of a given value has to be looked for.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for specifying the desired ordering.
        Optional, by default, less than or
        equal to is used for comparing two
        values.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    index: int
        Index of the lower bound of the given value in the given OneDimensionalArray

    Examples
    ========

    >>> from pydatastructs import lower_bound, OneDimensionalArray as ODA
    >>> arr1 = ODA(int, [4, 5, 5, 6, 7])
    >>> lb = lower_bound(arr1, 5, end=4, comp=lambda x, y : x < y)
    >>> lb
    1
    >>> arr = ODA(int, [7, 6, 5, 5, 4])
    >>> lb = lower_bound(arr, 5, start=0, comp=lambda x, y : x > y)
    >>> lb
    2

    Note
    ====

    DynamicOneDimensionalArray objects may not work as expected.
    """
    raise_if_backend_is_not_python(lower_bound,
                                   kwargs.get('backend', Backend.PYTHON))
    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array))
    comp = kwargs.get('comp', lambda x, y: x < y)
    index = end
    inclusive_end = end - 1
    if not comp(array[start], value):
        index = start
    while start <= inclusive_end:
        mid = (start + inclusive_end) // 2
        if comp(array[mid], value):
            start = mid + 1
        else:
            index = mid
            inclusive_end = mid - 1
    return index
Esempio n. 28
0
def minimum_spanning_tree_parallel(graph, algorithm, num_threads, **kwargs):
    """
    Computes a minimum spanning tree for the given
    graph and algorithm using the given number of threads.

    Parameters
    ==========

    graph: Graph
        The graph whose minimum spanning tree
        has to be computed.
    algorithm: str
        The algorithm which should be used for
        computing a minimum spanning tree.
        Currently the following algorithms are
        supported,

        'kruskal' -> Kruskal's algorithm as given in [1].

        'prim' -> Prim's algorithm as given in [2].
    num_threads: int
        The number of threads to be used.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    mst: Graph
        A minimum spanning tree using the implementation
        same as the graph provided in the input.

    Examples
    ========

    >>> from pydatastructs import Graph, AdjacencyListGraphNode
    >>> from pydatastructs import minimum_spanning_tree_parallel
    >>> u = AdjacencyListGraphNode('u')
    >>> v = AdjacencyListGraphNode('v')
    >>> G = Graph(u, v)
    >>> G.add_edge(u.name, v.name, 3)
    >>> mst = minimum_spanning_tree_parallel(G, 'kruskal', 3)
    >>> u_n = mst.neighbors(u.name)
    >>> mst.get_edge(u.name, u_n[0].name).value
    3

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Kruskal%27s_algorithm#Parallel_algorithm
    .. [2] https://en.wikipedia.org/wiki/Prim%27s_algorithm#Parallel_algorithm

    Note
    ====

    The concept of minimum spanning tree is valid only for
    connected and undirected graphs. So, this function
    should be used only for such graphs. Using with other
    types of graphs will lead to unwanted results.
    """
    raise_if_backend_is_not_python(
        minimum_spanning_tree_parallel, kwargs.get('backend', Backend.PYTHON))
    import pydatastructs.graphs.algorithms as algorithms
    func = "_minimum_spanning_tree_parallel_" + algorithm + "_" + graph._impl
    if not hasattr(algorithms, func):
        raise NotImplementedError(
        "Currently %s algoithm for %s implementation of graphs "
        "isn't implemented for finding minimum spanning trees."
        %(algorithm, graph._impl))
    return getattr(algorithms, func)(graph, num_threads)
Esempio n. 29
0
def shortest_paths(graph: Graph, algorithm: str,
                   source: str, target: str="",
                   **kwargs) -> tuple:
    """
    Finds shortest paths in the given graph from a given source.

    Parameters
    ==========

    graph: Graph
        The graph under consideration.
    algorithm: str
        The algorithm to be used. Currently, the following algorithms
        are implemented,

        'bellman_ford' -> Bellman-Ford algorithm as given in [1].

        'dijkstra' -> Dijkstra algorithm as given in [2].
    source: str
        The name of the source the node.
    target: str
        The name of the target node.
        Optional, by default, all pair shortest paths
        are returned.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    (distances, predecessors): (dict, dict)
        If target is not provided and algorithm used
        is 'bellman_ford'/'dijkstra'.
    (distances[target], predecessors): (float, dict)
        If target is provided and algorithm used is
        'bellman_ford'/'dijkstra'.

    Examples
    ========

    >>> from pydatastructs import Graph, AdjacencyListGraphNode
    >>> from pydatastructs import shortest_paths
    >>> V1 = AdjacencyListGraphNode("V1")
    >>> V2 = AdjacencyListGraphNode("V2")
    >>> V3 = AdjacencyListGraphNode("V3")
    >>> G = Graph(V1, V2, V3)
    >>> G.add_edge('V2', 'V3', 10)
    >>> G.add_edge('V1', 'V2', 11)
    >>> shortest_paths(G, 'bellman_ford', 'V1')
    ({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
    >>> shortest_paths(G, 'dijkstra', 'V1')
    ({'V2': 11, 'V3': 21, 'V1': 0}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
    .. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
    """
    raise_if_backend_is_not_python(
        shortest_paths, kwargs.get('backend', Backend.PYTHON))
    import pydatastructs.graphs.algorithms as algorithms
    func = "_" + algorithm + "_" + graph._impl
    if not hasattr(algorithms, func):
        raise NotImplementedError(
        "Currently %s algorithm isn't implemented for "
        "finding shortest paths in graphs."%(algorithm))
    return getattr(algorithms, func)(graph, source, target)
Esempio n. 30
0
def depth_first_search(
    graph, source_node, operation, *args, **kwargs):
    """
    Implementation of depth first search (DFS)
    algorithm.

    Parameters
    ==========

    graph: Graph
        The graph on which DFS is to be performed.
    source_node: str
        The name of the source node from where the DFS is
        to be initiated.
    operation: function
        The function which is to be applied
        on every node when it is visited.
        The prototype which is to be followed is,
        `function_name(curr_node, next_node,
                       arg_1, arg_2, . . ., arg_n)`.
        Here, the first two arguments denote, the
        current node and the node next to current node.
        The rest of the arguments are optional and you can
        provide your own stuff there.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Note
    ====

    You should pass all the arguments which you are going
    to use in the prototype of your `operation` after
    passing the operation function.

    Examples
    ========

    >>> from pydatastructs import Graph, AdjacencyListGraphNode
    >>> V1 = AdjacencyListGraphNode("V1")
    >>> V2 = AdjacencyListGraphNode("V2")
    >>> V3 = AdjacencyListGraphNode("V3")
    >>> G = Graph(V1, V2, V3)
    >>> from pydatastructs import depth_first_search
    >>> def f(curr_node, next_node, dest_node):
    ...     return curr_node != dest_node
    ...
    >>> G.add_edge(V1.name, V2.name)
    >>> G.add_edge(V2.name, V3.name)
    >>> depth_first_search(G, V1.name, f, V3.name)

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Depth-first_search
    """
    raise_if_backend_is_not_python(
        depth_first_search, kwargs.get('backend', Backend.PYTHON))
    import pydatastructs.graphs.algorithms as algorithms
    func = "_depth_first_search_" + graph._impl
    if not hasattr(algorithms, func):
        raise NotImplementedError(
        "Currently depth first search isn't implemented for "
        "%s graphs."%(graph._impl))
    return getattr(algorithms, func)(
           graph, source_node, operation, *args, **kwargs)