/* hamheuristic.c */ #define USAGE "hamheuristic [-sgu] [-vq] [-L#] [-t#] [infile [outfile]]" #define HELPTEXT \ " Apply a heuristic for finding hamiltonian cycles.\n\ Output those which are unsuccessful.\n\ \n\ -s force output to sparse6 format\n\ -g force output to graph6 format\n\ If neither -s or -g are given, the output format is\n\ determined by the header or, if there is none, by the\n\ format of the first input graph.\n\ -u Suppress output to outfile, give statistics instead.\n\ \n\ The output file will have a header if and only if the input file does.\n\ \n\ -p Be content with a hamiltonian path\n\ -v Give a cycle or path if one is found.\n\ -L# Limit number of sideways steps (default 1000+5*n)\n\ -t# Try # times (default 1)\n\ \n\ -q suppress auxiliary information\n" #define DEBUG(X) X /*************************************************************************/ #include "gtools.h" #define NO 0 #define YES 1 #define TIMEOUT 2 static long seed = 314159265; /**************************************************************************/ static int hamheur(sparsegraph *sg, boolean pathok, unsigned long limit, int *cyc) /* Try up to limit, fill cyc if YES and cyc!=NULL */ /* For pathok = TRUE, return when a hamiltonian path is found */ { DYNALLSTAT(int,path,path_sz); DYNALLSTAT(int,work,work_sz); DYNALLSTAT(int,pos,pos_sz); /* position on path or -1 */ int end0,end1,v0,v1; size_t *v; int n,*e,*d,len,d0,*e0,d1,*e1,dx,*ex; int i,ix,x,j,w,vext,exts; unsigned long count; boolean left,cycle; long ran; SG_VDE(sg,v,d,e); n = sg->nv; if (n < 3) return NO; DYNALLOC1(int,path,path_sz,2*n+4,"malloc"); DYNALLOC1(int,pos,pos_sz,n,"malloc"); DYNALLOC1(int,work,work_sz,n,"malloc"); for (i = 0; i < n; ++i) { if (d[i] < 2) return NO; pos[i] = -1; } count = 0; v0 = KRAN(n); v1 = e[v[v0]+KRAN(d[v0])]; end0 = n+1; end1 = n+2; path[end0] = v0; pos[v0] = end0; path[end1] = v1; pos[v1] = end1; len = 2; for (; count < limit;) { /* First look for an extension, but note if cycle */ if (pathok && len == n) { if (cyc) { j = 0; for (i = end0; i <= end1; ++i) cyc[j++] = path[i]; } return YES; } v0 = path[end0]; v1 = path[end1]; exts = 0; d0 = d[v0]; e0 = e + v[v0]; cycle = FALSE; ran = NEXTRAN; for (i = 0; i < d0; ++i) { w = e0[i]; if (pos[w] < 0) { ++exts; if (ran%exts == 0) {left = TRUE; vext = w;} } else if (w == v1) cycle = TRUE; } d1 = d[v1]; e1 = e + v[v1]; ran = NEXTRAN; for (i = 0; i < d1; ++i) { w = e1[i]; if (pos[w] < 0) { ++exts; if (ran%exts == 0) {left = FALSE; vext = w;} } } if (exts > 0) { if (left) { --end0; path[end0] = vext; pos[vext] = end0; } else { ++end1; path[end1] = vext; pos[vext] = end1; } ++len; continue; } /* Can't extend but a cycle was found */ if (cycle) { if (len == n) { if (cyc) { j = 0; for (i = pos[0]; i <= end1; ++i) cyc[j++] = path[i]; for (i = end0; i < pos[0]; ++i) cyc[j++] = path[i]; } return YES; } ix = end0 + KRAN(len); for (i = 0; i < len; ++i) { x = path[ix]; exts = 0; dx = d[x]; ex = e + v[x]; ran = NEXTRAN; for (j = 0; j < dx; ++j) { w = ex[j]; if (pos[w] < 0) { ++exts; if (ran%exts == 0) vext = w; } } if (exts > 0) break; if (ix == end1) ix = end0; else ++ix; } if (i == len) return NO; /* isolated component */ work[0] = vext; j = 1; for (i = ix; i <= end1; ++i) work[j++] = path[i]; for (i = end0; i < ix; ++i) work[j++] = path[i]; for (i = 0; i < j; ++i) { path[end0+i] = work[i]; pos[work[i]] = end0+i; } ++end1; ++len; continue; } /* In the last resort, do a sideways move. */ exts = 0; d0 = d[v0]; e0 = e + v[v0]; ran = NEXTRAN; for (i = 0; i < d0; ++i) { w = e0[i]; if (pos[w] >= 0 && w != path[end0+1]) { ++exts; if (ran%exts == 0) {left = TRUE; vext = w;} } } d1 = d[v1]; e1 = e + v[v1]; ran = NEXTRAN; for (i = 0; i < d1; ++i) { w = e1[i]; if (pos[w] >= 0 && w != path[end1-1]) { ++exts; if (ran%exts == 0) {left = FALSE; vext = w;} } } if (left) { i = end0; j = pos[vext]-1; } else { i = pos[vext]+1; j = end1; } for (; i < j; ++i, --j) { w = path[i]; path[i] = path[j]; path[j] = w; pos[path[i]] = i; pos[path[j]] = j; } ++count; } return TIMEOUT; } /**************************************************************************/ int main(int argc, char *argv[]) { sparsegraph sg; int n,codetype; int argnum,i,j,outcode,tvalue; char *arg,sw; boolean badargs; boolean pswitch,sswitch,gswitch,qswitch,Lswitch,tswitch,vswitch,uswitch; long Lvalue; double t; char *infilename,*outfilename; FILE *infile,*outfile; nauty_counter nin,nout,nNO,nYES,nTIMEOUT; int status; DYNALLSTAT(int,cyc,cyc_sz); HELP; PUTVERSION; INITSEED; ran_init(seed); uswitch = sswitch = gswitch = Lswitch = qswitch = FALSE; pswitch = vswitch = tswitch = FALSE; infilename = outfilename = NULL; argnum = 0; badargs = FALSE; for (j = 1; !badargs && j < argc; ++j) { arg = argv[j]; if (arg[0] == '-' && arg[1] != '\0') { ++arg; while (*arg != '\0') { sw = *arg++; SWBOOLEAN('s',sswitch) else SWBOOLEAN('g',gswitch) else SWBOOLEAN('u',uswitch) else SWBOOLEAN('p',pswitch) else SWBOOLEAN('q',qswitch) else SWBOOLEAN('v',vswitch) else SWLONG('L',Lswitch,Lvalue,"-L") else SWINT('t',tswitch,tvalue,"-t") else badargs = TRUE; } } else { ++argnum; if (argnum == 1) infilename = arg; else if (argnum == 2) outfilename = arg; else badargs = TRUE; } } if ((sswitch != 0) + (gswitch != 0) + (uswitch != 0) > 1) gt_abort(">E hamheuristic: -s, -g and -u are incompatible\n"); if (!tswitch) tvalue = 1; if (badargs || argnum > 2) { fprintf(stderr,">E Usage: %s\n",USAGE); GETHELP; exit(1); } if (!qswitch) { fprintf(stderr,">A hamheuristic"); if (pswitch || sswitch || gswitch || vswitch || tswitch || Lswitch) fprintf(stderr," -"); if (sswitch) fprintf(stderr,"s"); if (gswitch) fprintf(stderr,"g"); if (uswitch) fprintf(stderr,"u"); if (vswitch) fprintf(stderr,"v"); if (pswitch) fprintf(stderr,"p"); if (Lswitch) fprintf(stderr,"L%ld",Lvalue); if (tswitch) fprintf(stderr,"t%d",tvalue); if (argnum > 0) fprintf(stderr," %s",infilename); if (argnum > 1) fprintf(stderr," %s",outfilename); fprintf(stderr,"\n"); fflush(stderr); } if (infilename && infilename[0] == '-') infilename = NULL; infile = opengraphfile(infilename,&codetype,FALSE,1); if (!infile) exit(1); if (!infilename) infilename = "stdin"; NODIGRAPHSYET(codetype); if (!uswitch) { if (!outfilename || outfilename[0] == '-') { outfilename = "stdout"; outfile = stdout; } else if ((outfile = fopen(outfilename,"w")) == NULL) { fprintf(stderr,"Can't open output file %s\n",outfilename); gt_abort(NULL); } if (sswitch || (!gswitch && (codetype&SPARSE6))) outcode = SPARSE6; else outcode = GRAPH6; if (codetype&HAS_HEADER) { if (outcode == SPARSE6) writeline(outfile,SPARSE6_HEADER); else writeline(outfile,GRAPH6_HEADER); } } nin = nout = 0; nNO = nYES = nTIMEOUT = 0; t = CPUTIME; SG_INIT(sg); while (read_sg(infile,&sg) != NULL) { ++nin; n = sg.nv; if (vswitch) { DYNALLOC1(int,cyc,cyc_sz,n,"malloc"); } else cyc = NULL; status = TIMEOUT; for (i = 0; i < tvalue; ++i) { status = hamheur(&sg,pswitch,(Lswitch?Lvalue:1000+5*n),cyc); if (status != TIMEOUT) break; } if (status == NO) ++nNO; else if (status == YES) ++nYES; else ++nTIMEOUT; if (status != YES && !uswitch) { if (outcode == SPARSE6) writes6_sg(outfile,&sg); else if (outcode == GRAPH6) writeg6_sg(outfile,&sg); ++nout; } if (vswitch) { fprintf(stderr,">H " COUNTER_FMT ":",nin); if (status == TIMEOUT) fprintf(stderr," timed out\n"); else if (status == NO) fprintf(stderr," disconnected or low degree\n"); else { for (i = 0; i < n; ++i) fprintf(stderr," %d",cyc[i]); fprintf(stderr,"\n"); } } } t = CPUTIME - t; if (uswitch) { fprintf(stderr,">Z " COUNTER_FMT " graphs read from %s; " COUNTER_FMT " hamiltonian, " COUNTER_FMT " not, " COUNTER_FMT " timed out; %3.2f sec.\n", nin,infilename,nYES,nNO,nTIMEOUT,t); } else if (!qswitch) { fprintf(stderr, ">Z " COUNTER_FMT " graphs read from %s, " COUNTER_FMT " written to %s; %3.2f sec.\n", nin,infilename,nout,outfilename,t); } exit(0); }