Thiswikipedia page explains the Floyd Warshall algorithm to find the shortest path between nodes in a graph. The wikipedia page uses the graph on the left of the image as a starting graph (prior to the first iteration when k = 0) and then shows the remaining iterations (k = 1 etc) but it doesn't explain the significance of the numbers between the nodes and how those numbers are calculated. For example, in the starting graph when k = 0 why is there a -2 on the edge between 1 and 3, and why is there a 3 on the edge between 2 and 3. How are those calculated?
Furthermore, when k = 2, the wikipedia page says,
The path [4,2,3] is not considered, because [2,1,3] is the shortest
path encountered so far from 2 to 3.
Why is [2,1,3] shorter than [4,2,3]?
The numbers on the edges are just weights. It's a part of the input. The algorithm doesn't compute them.
[2, 1, 3] is not shorter than [4, 2, 3]. It's shorter than [2, 3], though. That's the only thing that matters.
Related
I am not sure whether this is an appropriate question to post on this platform.
The problem statement is present here:
https://www.interviewbit.com/problems/permutation-swaps/
Other than that, here's a short summary of this graph-based question.
Problem statement with an example:
let N = 4
given permutation A for numbers 1 to 4,
A = [1, 3, 2, 4]
and given,
B = [1, 4, 2, 3]
other than that we are given pairs of positions,
C = [[2, 4]]
We have to find out, whether we can convert permutation A to permutation B if only numbers at pairs of positions(good pairs) mentioned in C can be swapped.
Here, in the above example for pair (2, 4) in C, the number at position 2 in A is = 3 and the number at position 4 is 4. if we, swap the numbers at both these positions, we get [1, 4, 2, 3] which is equal to B. Hence, it is possible.
Your really need to ask an actual question!
Here is a simple algorithm to strt your thinking about what you want.
Add A to L, a list of reachable permutations
Loop P over permutations in L
Loop S over C
Apply C to P, giving R
If R == B
**DONE** TRUE
If R not in L
Add R to L
**DONE** FALSE
Algorithm:
Create a graph whose nodes are the positions 1..n, and with an edge between two positions if and only if you can swap them.
For every position where you have an unwanted number, if the number you want can't be reached through the graph, then no permutation can work and you should return 0.
If every position has its wanted number reachable, then return 1.
But why is it sufficient to have every wanted number reachable?
To start, construct a maximal spanning forest in the graph according to the following pseudo-code:
foreach node in 1..10:
if node not in any tree:
construct maximal tree by breadth-first search
if tree has > 1 element:
add tree to list of trees in our forest
And now we construct our permutation as follows:
while the forest is non-empty:
pick a tree in the forest:
find a leaf in the tree:
With a chain of swaps, get the right value into that node
remove the leaf from the tree
if the tree has only 1 element left, remove the tree from the forest
I will omit the proof that this always produces a permutation that gets to the desired end result. But it does.
I'm looking for an algorithm that can take in a set of natural numbers, for example:
S = {1, 3, 4, 2, 9, 34, 432, 43}
Then divide them into as equal piles as possible. The number of piles are predefined as n.
The goal is to have the sum of the difference between each pile and the lowest pile, to be the smallest.
Here comes an example.
Let's say you have:
S = { 1, 2, 2, 3, 1, 2, 3 }
n = 3
Then a solution could be
N1 = { 1, 2 }
N2 = { 2, 3 }
N3 = { 1, 2, 3 }
The sum of these piles would be 3, 5 and 6. The error would be: (5 - 3) + (6 - 3) = 5.
The algorithm needs to find the solution with the lowest error.
Any help is appreciated. Please comment if something is unclear.
I would argue that there is no efficient way to solve this problem because it is a NP-hard problem.
Proof:
Let's denote the problem you proposed as P*,
We can reduce the partition problem(known NP-hard) into P* by doing the following
Given a arbitrary partition problem P1, we ask the black box which solve P* to solve P1 with N=2(i.e, divide the set into 2 pile that minimize the different).
If the difference return by the black box is zero, -> there is a solution for P1
If the difference return by the black box is non-zero, -> there isn't a solution for P1
Therefore, P* is NP-hard
This sounds like a variation of the https://en.m.wikipedia.org/wiki/Bin_packing_problem. However, the size of the bins is not given thus it is at least as hard as Bin Packing. Thus the problem is NP-hard.
For an approximate solution you could for example calculate the average bin size and perform an adaptation of first-fit or best-fit in order to allow small overpacking.
I have an undirected, unweighted graph. Let us fix a vertex and find all distinct paths from that vertex which covers all vertices of the graph. The task is to find the number of possible such paths from every vertices.
Eg: Let us take a graph of 4 vertices [ 1, 2, 3, 4]. And the edges are (1,2), (2,3), (3,4), (4,2). Here answer is 4. The paths are 1>2>3>4, 1>2>4>3, 3>4>2>1, 4>3>2>1.
I have come with an algorithm which uses brute-force technique to find the number of possible such paths, initialized by each vertex.Eg:
For the above example:
From vertex 1 there is 2 such path;
From vertex 2 there is no such path;
From vertex 3 there is 1 such path;
From vertex 4 there is 1 such path;
So the answer is 2+1+1=4.
Is it possible to solve this problem in a better time complexity?
There's an O(2^n n^2)-time algorithm obtained by modifying the Held--Karp DP. The idea is, for each subset S of vertices paired with some endpoint t in S, compute the number of paths that visit exactly the vertices in S and end at t by summing, for each neighbor u of t that is in S, the count for visiting S - {t} and ending at u. As a base case, the singleton sets all have count 1. In Python 3:
import itertools
def count_hamilton_paths(graph): # graph is a dict of adjacency lists
vertices = frozenset(graph)
table = {(frozenset({t}), t): 1 for t in vertices}
for r in range(2, len(vertices) + 1):
for subset_tuple in itertools.combinations(vertices, r):
subset = frozenset(subset_tuple)
for t in subset:
subset_minus_t = subset - frozenset({t})
table[(subset, t)] = sum(table[(subset_minus_t, u)]
for u in graph[t]
if u in subset_minus_t)
return sum(table[(vertices, t)] for t in vertices)
print(count_hamilton_paths({1: {2}, 2: {1, 3, 4}, 3: {2, 4}, 4: {2, 3}}))
Let us assume that we have a tree consisting on N nodes. The task is to find all longest unique paths in the tree. For example, if the tree looks like following:
Then there are three longest unique paths in the tree: 1 - 2 - 3 - 4 - 5, 6 - 2 - 3 - 4 - 5 and 1 - 2 - 6.
I want to programmatically find and store all such paths for a given tree.
One way to do it would be to compute paths between each pair of node in the tree and then reject the paths which are contained in any other path. However, I am looking for an efficient way to do it. My questions are as follows:
Is it possible to compute this information in less than O(N^2)? I have not been able to think of a solution which would be faster than O(N^2).
If yes, could you be kind enough to guide me towards the solution.
The reason why I want to try it out is because I am trying to solve this problem: KNODES
An algorithm with a time complexity below O(N^2) may only exist, if every solution for a tree with N nodes can be encoded in less than O(N^2) space.
Suppose a complete binary tree with n leaves (N=n log n). The solution to the problem will contain a path for every set of 2 leaves. That means, the solution will have O(n^2) elements. So for this case we can encode the solution as the 2-element sets of leaves.
Now consider a nearly complete binary tree with m leaves, which was created by only removing arbitrary leaves from a complete binary tree with n leaves. When comparing the solution of this tree to that of the complete binary tree, both will share a possibly empty set of paths. In fact for every subset of paths of a solution of a complete binary tree, there will exist at least one binary tree with m leaves as mentioned above, that contains every solution of such a subset. (We intentionally ignore the fact that a tree with m leaves may have some more paths in the solution where at least some of the path ends are not leaves of the complete binary tree.)
Only that part of the solution for a binary tree with m leaves will be encoded by a number with (n^2)/2 bits. The index of a bit in this number represents an element in the upper right half of a matrix with n columns and rows.
For n=4 this would be:
x012
xx34
xxx5
The bit at index i will be set if the undirected path row(i),column(i) is contained in the solution.
As we have already statet that a solution for a tree with m leaves may contain any subset of the solution to the complete binary tree with n>=m leaves, every binary number with (n^2)/2 bits will represent a solution for a tree with m leaves.
Now encoding every possible number with (n^2)/2 bits with less than (n^2)/2 is not possible. So we have shown that solutions at least require O(n^2) space to be represented. Using N=n log n from above we yield a space requirement of at least O(N^2).
Therefore there doens't exist an algorithm with time complexity less than O(N^2)
As far as I could understand, you have a tree without a selected root. Your admissible paths are the paths that do not allow to visit tree nodes twice (you are not allowed to return back). And you need to find all such admissible paths that are not subpaths of any admissible path.
So if I understood right, then if a node has only one edge, than the admissible path either start or stop at this node. If tree is connected, then you can get from any node to any node by one admissible path.
So you select all nodes with one edge, call it S. Then select one of S and walk the whole tree saving the paths to the ends (path, not the walk order). Then you do this with every other item in S and remove duplicated paths (they can be in reverse order: like starting from 1: 1 - 2 - 6 and starting from 6: 6 - 2 - 1).
So here you have to visit all the nodes in the tree as much times as you have leafs in the tree. So complexity depends on the branching factor (in the worst case it is O(n^2). There are some optimizations that can reduce the amount of operations, like you don't have to walk the tree from the last of S.
In this picture the longest paths are {1, 2, 3, 4}, {1, 2, 3, 5}, {1, 2, 3, 6}, {1, 2, 3, 7}, {1, 2, 3, 8}, {1, 2, 3, 9}, {1, 2, 3, 10}
For tree like this storing all longest paths will cost you O(N2)
Let's take the tree which looks like the star with n nodes and n-1 edges.
Then you have got C(n-1, 2) unique longest paths.
So the lower limit of complexity can't be less than O(n^2).
Suppose you have got Binary Tree like on your picture.
class Node(object):
def __init__(self, key):
self.key = key
self.right = None
self.left = None
self.parent = None
def append_left(self, node):
self.left = node
node.parent = self
def append_right(self, node):
self.right = node
node.parent = self
root = Node(3)
root.append_left(Node(2))
root.append_right(Node(4))
root.left.append_left(Node(1))
root.left.append_right(Node(6))
root.right.append_right(Node(5))
And we need to get all paths between all leaves. So in your tree they are:
1, 2, 6
6, 2, 3, 4, 5
1, 2, 3, 4, 5
You can do this in (edit: not linear, quadratic) time.
def leaf_paths(node, paths = [], stacks = {}, visited = set()):
visited.add(node)
if node.left is None and node.right is None:
for leaf, path in stacks.iteritems():
path.append(node)
paths.append(path[:])
stacks[node] = [node]
else:
for leaf, path in stacks.iteritems():
if len(path) > 1 and path[-2] == node:
path.pop()
else:
path.append(node)
if node.left and node.left not in visited:
leaf_paths(node.left, paths, stacks, visited)
elif node.right and node.right not in visited:
leaf_paths(node.right, paths, stacks, visited)
elif node.parent:
leaf_paths(node.parent, paths, stacks, visited)
return paths
for path in leaf_paths(root):
print [n.key for n in path]
An output for your tree will be:
[1, 2, 6]
[6, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
The idea is to track all visited leaves while traversing a tree. And to keep stack of paths for each leaf. So here is memory/performance tradeoff.
Draw the tree. Let v be a vertex and p its parent. The length of the longest path including v but not p = (height of left subtree of v) + (height of right subtree of v).
The maximum over all v is the longest path in the graph. You can calculate this in O(n):
First calculate all the intermediate heights. Start at the leaves and work up: (height below v) = 1 + max(height below left child, height below right child)
Then calculate the sum (height of left subtree of v) + (height of right subtree of v) for each vertex v, and take the maximum. This is the length of longest path in the graph.
The items a-d are to be paired with items 0-3 in such a way that the total distance between all item pairs are minimized. For example, this matrix could describe the distance between each item in the first group and an item in its counterpart group:
[[2, 2, 4, 9],
[4, 7, 1, 1],
[3, 3, 8, 3],
[6, 1, 7, 8]]
This is supposed to mean that the distance 'a' -> '0' is 2, from 'a' -> '1' is 2, from 'a' -> '2' is 4, 'a' -> '3' is 9. From 'b' -> '0' it is 4 and so on.
Is there an algorithm that can match each letter with a digit, so that the total distance is minimized? E.g.:
[('a', 1), ('b', 3), ('c', 0), ('d', 2)]
Would be a legal solution with total distance: 2 + 1 + 3 + 7 = 13. Brute forcing and testing all possible combinations is not possible since the real world has groups with much more than four items in them.
This is a classic optimization task for bipartite graphs and can be solved with the Hungarian algorithm/method.
This can be solved by treating it as an instance of a weighted bipartite matching problem. The idea is to treat the elements a-d and 0-3 as nodes in a graph, where each lettered node is connected to each numbered node with an edge whose weight is specified by the matrix. Once you have this graph, you want to find a set of edges matching letters to numbers in a way where each node is only connected to at most one edge. Such a set of edges is called a matching, and since you want to minimize the distance you are looking for a minimum-cost matching.
As yi_H points out, this problem is well-studied and has many good polynomial-time algorithms. The Hungarian Algorithm is perhaps the most famous algorithm for the problem, but others have been invented since then that are asymptotically (or practically) faster.
This problem is worth remembering, since it arises in many circumstances. Any time you need to assign items in one group to items in another, check whether you can reduce the problem to bipartite matching. If so, you've almost certainly found a fast solution to the initial problem.