Dijkstra finding shortest path satisfying given constraints












5












$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 total K
types of fishes are selling in the city. A cat starts at store 1 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.










share|improve this question











$endgroup$

















    5












    $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 total K
    types of fishes are selling in the city. A cat starts at store 1 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.










    share|improve this question











    $endgroup$















      5












      5








      5


      3



      $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 total K
      types of fishes are selling in the city. A cat starts at store 1 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.










      share|improve this question











      $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 total K
      types of fishes are selling in the city. A cat starts at store 1 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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Feb 20 '18 at 23:38







      Logan

















      asked Feb 20 '18 at 9:16









      LoganLogan

      18718




      18718






















          1 Answer
          1






          active

          oldest

          votes


















          1












          $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".






          share|improve this answer








          New contributor




          jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.






          $endgroup$














            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
            });


            }
            });














            draft saved

            draft discarded


















            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









            1












            $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".






            share|improve this answer








            New contributor




            jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$


















              1












              $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".






              share|improve this answer








              New contributor




              jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
              Check out our Code of Conduct.






              $endgroup$
















                1












                1








                1





                $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".






                share|improve this answer








                New contributor




                jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






                $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".







                share|improve this answer








                New contributor




                jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.









                share|improve this answer



                share|improve this answer






                New contributor




                jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.









                answered 9 hours ago









                jferardjferard

                1112




                1112




                New contributor




                jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.





                New contributor





                jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






                jferard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






























                    draft saved

                    draft discarded




















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

                    is 'sed' thread safe

                    How to make a Squid Proxy server?