About ranges and iterators again: implementing next_permutation

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Mon Jul 11 18:52:24 PDT 2011


On 07/10/2011 04:13 PM, Max Klyga wrote:
> Several weeks ago Andrei mentioned at #d irc channel that he wanted to
> implement next_permutation STL function in Phobos.
> I thought that would be a nice excercize to implement it myself.
>
> I ported implementation mentioned in this article:
> http://marknelson.us/2002/03/01/next-permutation/
>
> At first it seemed trivial, but i failed to preserve the same
> constraints that STL implementation requires while preserving same
> efficiency.
>
> A straightforward port using random access ranges and indeces as
> iterators: http://pastie.org/2193659
>
> I tried to implement the same algorithm for bidirectional ranges, but it
> is obviously slower, because i have to shrink range's left side all the
> way, while iterators have a very nice ability: two iterators make a
> range on which algorithms can operate. This is imposible with ranges. At
> least I haven't found a way to do it.
>
> An attempt to implement next_permutation for biderectional ranges:
> http://pastie.org/2193686
> This implementation runs about 2-2.5 times slower on my machine.
>
> I think it's a nice challenge for D community to try to adapt this
> algorithm for ranges, allowing it be as fast and as generic as STL version.

I think part of the speed difference is the fact that operations on 
array ranges need to touch two words each so are more expensive. Besides 
there's a fair amount of extra churn. In the loop of the random access 
version we only have one decrement and a couple of tests, but in the 
range version there are several operations.

Here's my implementation that adds just a couple of marginal 
improvements and simplifications:

bool nextPermutation2(T)(T range)
     if (isBidirectionalRange!T && hasSwappableElements!T)
{
     if (range.walkLength(2) <= 1)
         return false;

     auto i = range.save;

     for (T jj;; i.popFront())
     {
         T ii = i.save;
         ii.popFront();

         if (ii.empty)
         {
             if (jj.empty)
             {
                 reverse(range);
                 return false;
             }
             T j = range.save;
             while (jj.front >= j.back)
                 j.popBack();
             swap(jj.front, j.back);
             jj.popFront();
             reverse(jj);
             return true;
         }

         if (i.front < ii.front)
             jj = i.save;
     }
}


Andrei


More information about the Digitalmars-d mailing list