]> granicus.if.org Git - postgresql/commitdiff
Improve implementation of GEQO's init_tour() function.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Nov 2015 15:46:14 +0000 (10:46 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Nov 2015 15:46:14 +0000 (10:46 -0500)
Rather than filling a temporary array and then copying values to the
output array, we can generate the required random permutation in-place
using the Fisher-Yates shuffle algorithm.  This is shorter as well as
more efficient than before.  It's pretty unlikely that anyone would
notice a speed improvement, but shorter code is better.

Nathan Wagner, edited a bit by me

src/backend/optimizer/geqo/geqo_recombination.c

index 652fadc745963ed8e04e098459117b85f732d11b..ef433e54e58c930f4140b5cf8fd39c52649b66ff 100644 (file)
  *
  *      Randomly generates a legal "traveling salesman" tour
  *      (i.e. where each point is visited only once.)
- *      Essentially, this routine fills an array with all possible
- *      points on the tour and randomly chooses the 'next' city from
- *      this array.  When a city is chosen, the array is shortened
- *      and the procedure repeated.
  */
 void
 init_tour(PlannerInfo *root, Gene *tour, int num_gene)
 {
-       Gene       *tmp;
-       int                     remainder;
-       int                     next,
-                               i;
+       int                     i,
+                               j;
 
-       /* Fill a temp array with the IDs of all not-yet-visited cities */
-       tmp = (Gene *) palloc(num_gene * sizeof(Gene));
-
-       for (i = 0; i < num_gene; i++)
-               tmp[i] = (Gene) (i + 1);
-
-       remainder = num_gene - 1;
+       /*
+        * We must fill the tour[] array with a random permutation of the numbers
+        * 1 .. num_gene.  We can do that in one pass using the "inside-out"
+        * variant of the Fisher-Yates shuffle algorithm.  Notionally, we append
+        * each new value to the array and then swap it with a randomly-chosen
+        * array element (possibly including itself, else we fail to generate
+        * permutations with the last city last).  The swap step can be optimized
+        * by combining it with the insertion.
+        */
+       if (num_gene > 0)
+               tour[0] = (Gene) 1;
 
-       for (i = 0; i < num_gene; i++)
+       for (i = 1; i < num_gene; i++)
        {
-               /* choose value between 0 and remainder inclusive */
-               next = geqo_randint(root, remainder, 0);
-               /* output that element of the tmp array */
-               tour[i] = tmp[next];
-               /* and delete it */
-               tmp[next] = tmp[remainder];
-               remainder--;
+               j = geqo_randint(root, i, 0);
+               /* i != j check avoids fetching uninitialized array element */
+               if (i != j)
+                       tour[i] = tour[j];
+               tour[j] = (Gene) (i + 1);
        }
-
-       pfree(tmp);
 }
 
 /* alloc_city_table