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

Reply via email to