Dijkstra finding shortest path satisfying given constraints
$begingroup$
The question is originated from Hackerrank.
Suppose there are
1 to N
stores in a city which are connected by
bidirectional roads associated with traveling times. Each store sells
some types of fishes (0 <= type_of_fish_store_i < K
), in totalK
types of fishes are selling in the city. A cat starts at store1
and
traveling along some path puchases fishes at each store on the path.
Calculate minimum time that satisfies the constraints
- the cat has to end at a specified store
X
- the cat has to end with specified types of fishes in the basket
Note: a store including the final destination can be visited more than once
Shop and fish types selling at that shop (
{shop:fish_type}
):
{1: {1}, 2: {2}, 3: {3}, 4: {4}, 5: {5}}
Adjacency matrix with cells filled with time cost (referring to
cost_matrix
):
[[0, 10, 10, 0, 0],
[10, 0, 0, 10, 0],
[10, 0, 0, 0, 10],
[0, 10, 0, 0, 10],
[0, 0, 10, 10, 0]]
Approach I've implemented:
def dijkstra(cost_matrix, start, wanted, end):
"""
:param cost_matrix: adjacency matrix filled with time costs
:param start: the store where the cat starts at
:param wanted: types of fishes the cat wants at the end of journey
:param end: the store where the cat ends at
:return:
"""
fish_basket = shop_and_fish[start]
accum = 0
h =
visited = [start]
while not (wanted.issubset(fish_basket) and start == end):
for i, dist in enumerate(cost_matrix[start - 1]):
if dist > 0:
heapq.heappush(h, [ accum + dist, i + 1, fish_basket | shop_and_fish[i + 1], visited + [i + 1]])
# print("heap: ", h)
accum, start, fish_basket, visited = heapq.heappop(h)
print("Total time: ", accum, ", End store:", start, ", Fish types collected: ", fish_basket, ", Path: ", visited)
return accum
Execute:
dijkstra(cost_matrix, 1, {1,2,3,4,5}, 5)
# this is saying the final accepted state is the cat
# at 5th store with {1,2,3,4,5} types of fishes in hand
Returns
50
# Total time: 50 , End store: 5 , Fish types collected: {1, 2, 3, 4, 5} , Path: [1, 2, 4, 5, 3, 5]
Problem
Efficiency. Expanding unnecessary nodes such as [1,2,1,2,1,2]
, in some other cases [1,2,3,4,3,2,1,2,3,4..]
, maybe some other unforeseen patterns. Any thoughts on how to eliminate them? I've tried palindrome, but it seems to add a lot of complexity as it examines every path and every subpath of that path. Moreover, if you've identified any other improvements, feel free to add to answer.
python performance algorithm programming-challenge graph
$endgroup$
add a comment |
$begingroup$
The question is originated from Hackerrank.
Suppose there are
1 to N
stores in a city which are connected by
bidirectional roads associated with traveling times. Each store sells
some types of fishes (0 <= type_of_fish_store_i < K
), in totalK
types of fishes are selling in the city. A cat starts at store1
and
traveling along some path puchases fishes at each store on the path.
Calculate minimum time that satisfies the constraints
- the cat has to end at a specified store
X
- the cat has to end with specified types of fishes in the basket
Note: a store including the final destination can be visited more than once
Shop and fish types selling at that shop (
{shop:fish_type}
):
{1: {1}, 2: {2}, 3: {3}, 4: {4}, 5: {5}}
Adjacency matrix with cells filled with time cost (referring to
cost_matrix
):
[[0, 10, 10, 0, 0],
[10, 0, 0, 10, 0],
[10, 0, 0, 0, 10],
[0, 10, 0, 0, 10],
[0, 0, 10, 10, 0]]
Approach I've implemented:
def dijkstra(cost_matrix, start, wanted, end):
"""
:param cost_matrix: adjacency matrix filled with time costs
:param start: the store where the cat starts at
:param wanted: types of fishes the cat wants at the end of journey
:param end: the store where the cat ends at
:return:
"""
fish_basket = shop_and_fish[start]
accum = 0
h =
visited = [start]
while not (wanted.issubset(fish_basket) and start == end):
for i, dist in enumerate(cost_matrix[start - 1]):
if dist > 0:
heapq.heappush(h, [ accum + dist, i + 1, fish_basket | shop_and_fish[i + 1], visited + [i + 1]])
# print("heap: ", h)
accum, start, fish_basket, visited = heapq.heappop(h)
print("Total time: ", accum, ", End store:", start, ", Fish types collected: ", fish_basket, ", Path: ", visited)
return accum
Execute:
dijkstra(cost_matrix, 1, {1,2,3,4,5}, 5)
# this is saying the final accepted state is the cat
# at 5th store with {1,2,3,4,5} types of fishes in hand
Returns
50
# Total time: 50 , End store: 5 , Fish types collected: {1, 2, 3, 4, 5} , Path: [1, 2, 4, 5, 3, 5]
Problem
Efficiency. Expanding unnecessary nodes such as [1,2,1,2,1,2]
, in some other cases [1,2,3,4,3,2,1,2,3,4..]
, maybe some other unforeseen patterns. Any thoughts on how to eliminate them? I've tried palindrome, but it seems to add a lot of complexity as it examines every path and every subpath of that path. Moreover, if you've identified any other improvements, feel free to add to answer.
python performance algorithm programming-challenge graph
$endgroup$
add a comment |
$begingroup$
The question is originated from Hackerrank.
Suppose there are
1 to N
stores in a city which are connected by
bidirectional roads associated with traveling times. Each store sells
some types of fishes (0 <= type_of_fish_store_i < K
), in totalK
types of fishes are selling in the city. A cat starts at store1
and
traveling along some path puchases fishes at each store on the path.
Calculate minimum time that satisfies the constraints
- the cat has to end at a specified store
X
- the cat has to end with specified types of fishes in the basket
Note: a store including the final destination can be visited more than once
Shop and fish types selling at that shop (
{shop:fish_type}
):
{1: {1}, 2: {2}, 3: {3}, 4: {4}, 5: {5}}
Adjacency matrix with cells filled with time cost (referring to
cost_matrix
):
[[0, 10, 10, 0, 0],
[10, 0, 0, 10, 0],
[10, 0, 0, 0, 10],
[0, 10, 0, 0, 10],
[0, 0, 10, 10, 0]]
Approach I've implemented:
def dijkstra(cost_matrix, start, wanted, end):
"""
:param cost_matrix: adjacency matrix filled with time costs
:param start: the store where the cat starts at
:param wanted: types of fishes the cat wants at the end of journey
:param end: the store where the cat ends at
:return:
"""
fish_basket = shop_and_fish[start]
accum = 0
h =
visited = [start]
while not (wanted.issubset(fish_basket) and start == end):
for i, dist in enumerate(cost_matrix[start - 1]):
if dist > 0:
heapq.heappush(h, [ accum + dist, i + 1, fish_basket | shop_and_fish[i + 1], visited + [i + 1]])
# print("heap: ", h)
accum, start, fish_basket, visited = heapq.heappop(h)
print("Total time: ", accum, ", End store:", start, ", Fish types collected: ", fish_basket, ", Path: ", visited)
return accum
Execute:
dijkstra(cost_matrix, 1, {1,2,3,4,5}, 5)
# this is saying the final accepted state is the cat
# at 5th store with {1,2,3,4,5} types of fishes in hand
Returns
50
# Total time: 50 , End store: 5 , Fish types collected: {1, 2, 3, 4, 5} , Path: [1, 2, 4, 5, 3, 5]
Problem
Efficiency. Expanding unnecessary nodes such as [1,2,1,2,1,2]
, in some other cases [1,2,3,4,3,2,1,2,3,4..]
, maybe some other unforeseen patterns. Any thoughts on how to eliminate them? I've tried palindrome, but it seems to add a lot of complexity as it examines every path and every subpath of that path. Moreover, if you've identified any other improvements, feel free to add to answer.
python performance algorithm programming-challenge graph
$endgroup$
The question is originated from Hackerrank.
Suppose there are
1 to N
stores in a city which are connected by
bidirectional roads associated with traveling times. Each store sells
some types of fishes (0 <= type_of_fish_store_i < K
), in totalK
types of fishes are selling in the city. A cat starts at store1
and
traveling along some path puchases fishes at each store on the path.
Calculate minimum time that satisfies the constraints
- the cat has to end at a specified store
X
- the cat has to end with specified types of fishes in the basket
Note: a store including the final destination can be visited more than once
Shop and fish types selling at that shop (
{shop:fish_type}
):
{1: {1}, 2: {2}, 3: {3}, 4: {4}, 5: {5}}
Adjacency matrix with cells filled with time cost (referring to
cost_matrix
):
[[0, 10, 10, 0, 0],
[10, 0, 0, 10, 0],
[10, 0, 0, 0, 10],
[0, 10, 0, 0, 10],
[0, 0, 10, 10, 0]]
Approach I've implemented:
def dijkstra(cost_matrix, start, wanted, end):
"""
:param cost_matrix: adjacency matrix filled with time costs
:param start: the store where the cat starts at
:param wanted: types of fishes the cat wants at the end of journey
:param end: the store where the cat ends at
:return:
"""
fish_basket = shop_and_fish[start]
accum = 0
h =
visited = [start]
while not (wanted.issubset(fish_basket) and start == end):
for i, dist in enumerate(cost_matrix[start - 1]):
if dist > 0:
heapq.heappush(h, [ accum + dist, i + 1, fish_basket | shop_and_fish[i + 1], visited + [i + 1]])
# print("heap: ", h)
accum, start, fish_basket, visited = heapq.heappop(h)
print("Total time: ", accum, ", End store:", start, ", Fish types collected: ", fish_basket, ", Path: ", visited)
return accum
Execute:
dijkstra(cost_matrix, 1, {1,2,3,4,5}, 5)
# this is saying the final accepted state is the cat
# at 5th store with {1,2,3,4,5} types of fishes in hand
Returns
50
# Total time: 50 , End store: 5 , Fish types collected: {1, 2, 3, 4, 5} , Path: [1, 2, 4, 5, 3, 5]
Problem
Efficiency. Expanding unnecessary nodes such as [1,2,1,2,1,2]
, in some other cases [1,2,3,4,3,2,1,2,3,4..]
, maybe some other unforeseen patterns. Any thoughts on how to eliminate them? I've tried palindrome, but it seems to add a lot of complexity as it examines every path and every subpath of that path. Moreover, if you've identified any other improvements, feel free to add to answer.
python performance algorithm programming-challenge graph
python performance algorithm programming-challenge graph
edited Feb 20 '18 at 23:38
Logan
asked Feb 20 '18 at 9:16
LoganLogan
18718
18718
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
This is an interesting problem. You hacked Dijsktra's algorithm to make it solve the problem, but this hack has a cost. In Dijkstra's classical algorithm:
- you stop once all nodes have been visited;
- you start an iteration with the best (shortest) candidate path.
In your version:
- you stop once you reach your destination and you have a full basket of fishes;
- same start for iterations.
Basically, you walk until your basket is full and then you rush to the end store. The BFS selection ensure you that you get the effective shortest path (good point: your algorithm is correct).
Of course, without the Dijkstra's stop condition, your algorithm is not O(n lg n) anymore.
Consider the following graph for instance:
A -- B --...-- C
The cost_matrix
is:
cost_matrix = [[0, 1, 0],
[1, 0, 999],
[0, 999, 0]]
If you look for a path from 1
to 3
, your function will play ping-pong between 1
and 2
until the distance reaches 999
and then consider 3
.
A bit of theory now. Take the following instance of your problem: every store (node) has only one type of fish, and two stores do not have the same type of fish. You want a basket filled with all existing types of fish. That means: you have to visit each store at least once. This is a variation of the Travelling Salesman problem because you are allowed to visit every store mutliple times, but the complexity is the same (see https://math.stackexchange.com/questions/1269983/traveling-salesman-problem-why-visit-each-city-only-once and https://en.wikipedia.org/wiki/Travelling_salesman_problem#Computational_complexity).
Hence, I think you won't find any easy answer. It seems to me that your best choice, if you want to improve efficiency, is to "branch and bound".
New contributor
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187939%2fdijkstra-finding-shortest-path-satisfying-given-constraints%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
This is an interesting problem. You hacked Dijsktra's algorithm to make it solve the problem, but this hack has a cost. In Dijkstra's classical algorithm:
- you stop once all nodes have been visited;
- you start an iteration with the best (shortest) candidate path.
In your version:
- you stop once you reach your destination and you have a full basket of fishes;
- same start for iterations.
Basically, you walk until your basket is full and then you rush to the end store. The BFS selection ensure you that you get the effective shortest path (good point: your algorithm is correct).
Of course, without the Dijkstra's stop condition, your algorithm is not O(n lg n) anymore.
Consider the following graph for instance:
A -- B --...-- C
The cost_matrix
is:
cost_matrix = [[0, 1, 0],
[1, 0, 999],
[0, 999, 0]]
If you look for a path from 1
to 3
, your function will play ping-pong between 1
and 2
until the distance reaches 999
and then consider 3
.
A bit of theory now. Take the following instance of your problem: every store (node) has only one type of fish, and two stores do not have the same type of fish. You want a basket filled with all existing types of fish. That means: you have to visit each store at least once. This is a variation of the Travelling Salesman problem because you are allowed to visit every store mutliple times, but the complexity is the same (see https://math.stackexchange.com/questions/1269983/traveling-salesman-problem-why-visit-each-city-only-once and https://en.wikipedia.org/wiki/Travelling_salesman_problem#Computational_complexity).
Hence, I think you won't find any easy answer. It seems to me that your best choice, if you want to improve efficiency, is to "branch and bound".
New contributor
$endgroup$
add a comment |
$begingroup$
This is an interesting problem. You hacked Dijsktra's algorithm to make it solve the problem, but this hack has a cost. In Dijkstra's classical algorithm:
- you stop once all nodes have been visited;
- you start an iteration with the best (shortest) candidate path.
In your version:
- you stop once you reach your destination and you have a full basket of fishes;
- same start for iterations.
Basically, you walk until your basket is full and then you rush to the end store. The BFS selection ensure you that you get the effective shortest path (good point: your algorithm is correct).
Of course, without the Dijkstra's stop condition, your algorithm is not O(n lg n) anymore.
Consider the following graph for instance:
A -- B --...-- C
The cost_matrix
is:
cost_matrix = [[0, 1, 0],
[1, 0, 999],
[0, 999, 0]]
If you look for a path from 1
to 3
, your function will play ping-pong between 1
and 2
until the distance reaches 999
and then consider 3
.
A bit of theory now. Take the following instance of your problem: every store (node) has only one type of fish, and two stores do not have the same type of fish. You want a basket filled with all existing types of fish. That means: you have to visit each store at least once. This is a variation of the Travelling Salesman problem because you are allowed to visit every store mutliple times, but the complexity is the same (see https://math.stackexchange.com/questions/1269983/traveling-salesman-problem-why-visit-each-city-only-once and https://en.wikipedia.org/wiki/Travelling_salesman_problem#Computational_complexity).
Hence, I think you won't find any easy answer. It seems to me that your best choice, if you want to improve efficiency, is to "branch and bound".
New contributor
$endgroup$
add a comment |
$begingroup$
This is an interesting problem. You hacked Dijsktra's algorithm to make it solve the problem, but this hack has a cost. In Dijkstra's classical algorithm:
- you stop once all nodes have been visited;
- you start an iteration with the best (shortest) candidate path.
In your version:
- you stop once you reach your destination and you have a full basket of fishes;
- same start for iterations.
Basically, you walk until your basket is full and then you rush to the end store. The BFS selection ensure you that you get the effective shortest path (good point: your algorithm is correct).
Of course, without the Dijkstra's stop condition, your algorithm is not O(n lg n) anymore.
Consider the following graph for instance:
A -- B --...-- C
The cost_matrix
is:
cost_matrix = [[0, 1, 0],
[1, 0, 999],
[0, 999, 0]]
If you look for a path from 1
to 3
, your function will play ping-pong between 1
and 2
until the distance reaches 999
and then consider 3
.
A bit of theory now. Take the following instance of your problem: every store (node) has only one type of fish, and two stores do not have the same type of fish. You want a basket filled with all existing types of fish. That means: you have to visit each store at least once. This is a variation of the Travelling Salesman problem because you are allowed to visit every store mutliple times, but the complexity is the same (see https://math.stackexchange.com/questions/1269983/traveling-salesman-problem-why-visit-each-city-only-once and https://en.wikipedia.org/wiki/Travelling_salesman_problem#Computational_complexity).
Hence, I think you won't find any easy answer. It seems to me that your best choice, if you want to improve efficiency, is to "branch and bound".
New contributor
$endgroup$
This is an interesting problem. You hacked Dijsktra's algorithm to make it solve the problem, but this hack has a cost. In Dijkstra's classical algorithm:
- you stop once all nodes have been visited;
- you start an iteration with the best (shortest) candidate path.
In your version:
- you stop once you reach your destination and you have a full basket of fishes;
- same start for iterations.
Basically, you walk until your basket is full and then you rush to the end store. The BFS selection ensure you that you get the effective shortest path (good point: your algorithm is correct).
Of course, without the Dijkstra's stop condition, your algorithm is not O(n lg n) anymore.
Consider the following graph for instance:
A -- B --...-- C
The cost_matrix
is:
cost_matrix = [[0, 1, 0],
[1, 0, 999],
[0, 999, 0]]
If you look for a path from 1
to 3
, your function will play ping-pong between 1
and 2
until the distance reaches 999
and then consider 3
.
A bit of theory now. Take the following instance of your problem: every store (node) has only one type of fish, and two stores do not have the same type of fish. You want a basket filled with all existing types of fish. That means: you have to visit each store at least once. This is a variation of the Travelling Salesman problem because you are allowed to visit every store mutliple times, but the complexity is the same (see https://math.stackexchange.com/questions/1269983/traveling-salesman-problem-why-visit-each-city-only-once and https://en.wikipedia.org/wiki/Travelling_salesman_problem#Computational_complexity).
Hence, I think you won't find any easy answer. It seems to me that your best choice, if you want to improve efficiency, is to "branch and bound".
New contributor
New contributor
answered 9 hours ago
jferardjferard
1112
1112
New contributor
New contributor
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187939%2fdijkstra-finding-shortest-path-satisfying-given-constraints%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown