开发者

bug/error in basis set path algorithm i can't figure out

开发者 https://www.devze.com 2022-12-24 06:05 出处:网络
The following looks through a 2d array to find basis set paths. It is supposed to print out the individual paths but not repeat any and end when all paths are found. It however doesn\'t stop at the la

The following looks through a 2d array to find basis set paths. It is supposed to print out the individual paths but not repeat any and end when all paths are found. It however doesn't stop at the last path and has a bug in it somewhere in which the following happens: It goes halfway through the path and then goes to zero and ends the path for some reason.

For example the table is filled with the following: all 0s, except for

[1][2], [1][3], [2][4], [2][5], [3][5], [4][6], [5][6], [6][0]

which all have a 1 in them. The desired paths are P1: 1 2 4 6 0

P2: 1 3 5 6 0

P3: 1 2 5 6 0.

The output I get when i run the program is 12460

13560

1250

124

Any and all help on this is much appreciated, this is just the function that scans through the array looking for paths, I can add the entire program if that would be helpful. Thanks..

void find_path(int map[][MAX], int x){
 int path =0;
 int m=1;
 int blah=0;
 bool path_found = false;

 do
 {
  for(int n=0;n<(x+1);n++){
   if(map[m][n]==-1){
   开发者_运维问答 blah=(n+1);
    if(blah<(x+1)){
     for(blah;blah<(x+1);blah++){
      if(map[m][blah]==1){
       map[m][blah]=-1;
       path=m;
       path_found = true;
       cout<<path;
       m=blah;
       n=0;
      }
     }
    }
    else{  
    path=m;
    path_found=false;
    cout<<path;
    m=n;
    if(m==0){
     path=0;
     cout<<path<<endl;
     m=1;
     path_found=false;
     }
    } 
   }
   else if(map[m][n]==1){
    map[m][n]=-1;
    path=m;
    path_found = true;
    cout<<path;
    m=n;
    if(m==0){
     path=0;
     cout<<path<<endl;
     m=1;
     path_found=false;
    }
   }

  }
 } 
 while(m<(x+1) && path_found);
}


You have made an all too common mistake. Far too many folks believe the primary goal in writing software is to tell the computer what to do. That is incorrect. The primary goal is to communicate CLEARLY what one is doing to other humans. The secondary goal is telling the computer what to do.

I suggest you don't use hardcoded numbers (-1 in map[][]=-1). Enums are made for this. Variable names could be more descriptive. ("blah"?) You can leave for clauses empty, e.g. for(; blah<(x+1); blah++). (Even for(;;) is valid.) Usually arrays go from 0..LIMIT-1 so you can say for(; blah<x; ++blah). Often ++foo is preferred to foo++, though it won't make much of a difference with ints. Watch out for underscores in names. It's easy to confuse path-found and path_found. ADD COMMENTS!!! Explain what you are doing. Add spaces while you are at it. Compact code is nobody's friend. At the very least, document that you are altering the values in MAP.

All that said... Where to begin...

Well, after changing nodes, I'd have started from the first path option, not at the current node plus 1. (E.g., Jump from 1-to-2, from 2-to-4, and then you start looking at map[4][5] instead of map[4][0] or map[4][1].

I would not have special cased 0 as an exit criteria.

Your line "for(blah;blah<(x+1);blah++){" does not exit (break) when a map[m][blah]==1 match is found, but marks it as -1 and continues looking for other matches. That should create an interesting effect with the prior "for(int n=0;n<(x+1);n++){" line.

Once you hit M=6, N=1 you can never hit your special-cased-0 exit criteria and print. (You do realize you have n=0 followed by n++???)

Once you have a map full of -1's, you can never proceed further. This is a real problem when some of your paths share the same links. E.g. 1-2-4-6-0 and 1-3-5-6-0 share 6-0.

You do not detect a map full of -1's. You have an endless loop. (If you are going to set path_found to false, do it right after "do{", before "for(int n=0;n<(x+1);n++){". Not in some easily missed exit spot deep in nested if statements.

I do not see the output 12460,13560,1250,124 when I run your program. I see 12460,135 and then it hangs. (And that only after I modified your software to flush output after every write.)

You show no scaffolding code. Something like:

void printMap(int theMap[][MAX] )
{
  for( int i=0; i<MAX; ++i )
  {
    cout << "[" << i << "] ";
    for ( int j=0;  j<MAX;  ++j )
      cout << "  " << theMap [i] [j];
    cout << endl;
  }  /* for( int i=0; i<MAX; ++i )  */
  cout << endl;
}

Coupled with a debugger (in gdb use "call printMap(map)") would be helpful to you. (It certainly was to me.)

Your code replicates itself. That's usually a sign of a mistake. E.g. the "if(m==0){ path=0; cout<<path<<endl; m=1; path_found=false; }" bits. Or the "if(map[m][FOO]==1){ map[m][FOO]=-1; path=m; path_found = true; cout<<path; m=FOO;" part, where FOO is "blah" or "n".


All this said, I've had to work with worse code (in professional situations no less). (Not often, but it does happen.) Keep at it, and you'll get better. We all started in the same bad place. Practice, keep asking questions, and you will improve.



Responding to comment here...

The only reason I had the special 0 case in there is because it was just hanging when it reached 0 because that row is empty and it stayed in an endless loop.

You need a better test for your exit condition.

When I compile and run it i get the output I described, not what you get.

I used g++ version 3.4.4 with:

#define MAX 7
                      /* 0  1  2  3  4  5  6 */
int  map[MAX][MAX] = { { 0, 0, 0, 0, 0, 0, 0 }, /*0*/
                       { 0, 0, 1, 1, 0, 0, 0 }, /*1*/
                       { 0, 0, 0, 0, 1, 1, 0 }, /*2*/
                       { 0, 0, 0, 0, 0, 1, 0 }, /*3*/
                       { 0, 0, 0, 0, 0, 0, 1 }, /*4*/
                       { 0, 0, 0, 0, 0, 0, 1 }, /*5*/
                       { 1, 0, 0, 0, 0, 0, 0 }  /*6*/
                     };

find_path( map, 6 );

Perhaps your input conditions are different?

I don't understand why it would reach m=6 n=1, other than [6][0], the rest of the row is 0s.

The code below is yours (with minor cosmetic changes).

N=0 occurs at Line=25. When the for loop at Line=16 completes, we resume the for loop at Line=9. The first step there is the final iteration (n++) component. Thus the next for loop iteration at Line=10 has m=blah=6 and n=1. At this stage, none of your conditional tests (==-1, ==1) can ever be true.

You really ought to try stepping through your code in a debugger. See what the code is actually doing vs what you expected it to do.

/* 0*/   void find_path( int map[][MAX], int x )
/* 1*/   {
/* 2*/     int  path = 0;
/* 3*/     int  m    = 1;
/* 4*/     int  blah = 0;
/* 5*/     bool path_found = false;
/* 6*/   
/* 7*/     do
/* 8*/     {
/* 9*/       for ( int n=0;  n < (x+1);  n++ )
/*10*/       {
/*11*/         if ( map [m] [n] == -1 )
/*12*/         {
/*13*/           blah = (n+1);
/*14*/           if ( blah < (x+1) )
/*15*/           {
/*16*/             for ( ;  blah < (x+1);  blah++ )
/*17*/             {
/*18*/               if ( map [m] [blah] == 1 )
/*19*/               {
/*20*/                 map [m] [blah] = -1;
/*21*/                 path = m;
/*22*/                 path_found = true;
/*23*/                 cout << path;
/*24*/                 m = blah;
/*25*/                 n = 0;
/*26*/               } /* if ( map [m] [blah] == 1 ) */
/*27*/             } /* for ( ;  blah < (x+1);  blah++ ) */
/*28*/           } /* if ( blah < (x+1) ) */
/*29*/           else
/*30*/           {
/*31*/             path = m;
/*32*/             path_found = false;
/*33*/             cout << path;
/*34*/             m = n;
/*35*/             if ( m == 0 )
/*36*/             {
/*37*/               path = 0;
/*38*/               cout << path << endl;
/*39*/               m = 1;
/*40*/               path_found = false;
/*41*/             } /* if ( m == 0 ) */
/*42*/           } /* if ( blah < (x+1) ) ... ELSE ... */
/*43*/         } /* if ( map [m] [n] == -1 ) */
/*44*/         else if ( map [m] [n] == 1 )
/*45*/         {
/*46*/           map [m] [n] = -1;
/*47*/           path = m;
/*48*/           path_found = true;
/*49*/           cout << path;
/*50*/           m = n;
/*51*/           if ( m == 0 )
/*52*/           {
/*53*/             path = 0;
/*54*/             cout << path << endl;
/*55*/             m = 1;
/*56*/             path_found = false;
/*57*/           } /* if ( m == 0 ) */
/*58*/         } /* if(map[m][n]==-1) ... ELSE IF (map[m][n]==1) ... */
/*59*/         
/*60*/       } /* for ( int n=0;  n < (x+1);  n++ ) */
/*61*/       
/*62*/     } /* do { ... } */ while(m<(x+1) && path_found);
/*63*/     
/*64*/   } /* void find_path( int map[][MAX], int x ) */


When I compile the function with the code you posted the I get 12460 as output and then it hangs in an endless loop, unless I take out the if ( blah < (x+1) and corresponding else, then I get the output I first described.

I have tried to work on it and came up with the following which gives me 124601356 as output and then ends.

void find_path(int map[][MAX], int x){
    int path =0;
    int m=1;
    int rest_of_row=0;
    bool path_found;
    do
    {
        path_found = false;
        for(int n=0;n<(x+1);n++){
            //check to see if the node is already used on a path
            if(map[m][n]==-1){
                rest_of_row=(n+1);
                path=m;

                //check to see there are any unused nodes for the path from found node +1 to end of row
                do{
                    if(map[m][rest_of_row]==1){
                        map[m][rest_of_row]=-1;
                        path_found = true;
                    }
                    else{
                        ++rest_of_row;
                    }
                }
                while(!path_found);

                m=n;


                if(path_found){
                    m=(rest_of_row);
                }

                cout<<path;
            }
            //else check to see if the node hasn't been used in the path
            else if(map[m][n]==1){
                map[m][n]=-1;
                path=m;
                path_found = true;
                cout<<path;
                m=n;
            }
            //if the node is at 0 path is complete, go back to 1 to check for new path
            if(m==0){
                path=m;
                m=1;
                path_found=false;
                cout<<path;
            }
        }
    }
    while(m<(x+1) && path_found);

}


Here's working code (using sufficiently advanced C++ that you can't get away with handing it in as your own work, also thanks to mrree, from whose answer I "lifted" the initializer for map). I suggest you think about the problem a little more deeply, starting with why my correct solution uses recursion and yours has neither recursion nor a stack. I think it's actually possible to avoid the stack because the path array has all the state needed for backtracking, but you definitely do need some form of backtracking.

// enumpaths.cpp : Prints all longest paths given a digraph connectivity matrix
//

const size_t MAX = 7;

typedef void (*path_completed_cb)( const size_t (&path)[MAX], size_t const currentLength );

void follow_path( size_t (&path)[MAX], size_t const currentLength, const bool (&map)[MAX][MAX], path_completed_cb const callback )
{
 size_t currentEnd = path[currentLength-1];

 /* check for loops, if a loop exists then every finite path is
    a subpath of a longer (looped more often) path */
 for( size_t i = 0; i < currentLength; i++ ) {
  if (map[currentEnd][path[i]]) return; /* found a loop */
 }

 bool extended = false;
 /* look for continuances */
 for( size_t next = 0; next < MAX; next++ ) {
  if (map[currentEnd][next]) {
   extended = true;
   path[currentLength] = next;
   follow_path(path, currentLength+1, map, callback);
  }

 }
 if (!extended) {
  /* there were no outgoing arcs, this is a longest path */
  (*callback)(path, currentLength);
 }
}

void enum_paths( const bool (&map)[MAX][MAX], path_completed_cb const callback )
{
 for ( size_t start = 0; start < MAX; start++ ) {
  bool isSource = true;
  /* if any incoming arcs to node "start", then any paths
     starting at start are subpaths of longer paths */
  for (size_t pred = 0; pred < MAX; pred++ )
   if (map[pred][start]) isSource = false;

  if (isSource) {
   size_t path[MAX] = { start };
   follow_path(path, 1, map, callback);
  }
 }
}

#include <iostream>
#include <iomanip>

void print_path( const size_t (&path)[MAX], size_t const currentLength )
{
 using namespace std;
 cout << path[0];
 for( size_t i = 1; i < currentLength; i++ ) {
  cout << '-' << path[i];
 }
 cout << endl;
}

int main()
{                   
 const bool map[MAX][MAX] =
  /* 0  1  2  3  4  5  6 */
 { { 0, 0, 0, 0, 0, 0, 0 }, /*0*/
   { 0, 0, 1, 1, 0, 0, 0 }, /*1*/
   { 0, 0, 0, 0, 1, 1, 0 }, /*2*/
   { 0, 0, 0, 0, 0, 1, 0 }, /*3*/
   { 0, 0, 0, 0, 0, 0, 1 }, /*4*/
   { 0, 0, 0, 0, 0, 0, 1 }, /*5*/
   { 1, 0, 0, 0, 0, 0, 0 }  /*6*/
 };

 enum_paths(map, &print_path);

 return 0;
}

EDIT: Gave a try at doing it without recursion, and while simply removing the recursion would be just a mechanical transformation, doing it without introducing gotos is not easy. Nor is merging the "start of path" case with the "middle of path" logic.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号