【问题标题】:In-Place Ordering of Elements元素的就地排序
【发布时间】:2014-01-13 22:27:14
【问题描述】:

Phobos 是否有一些可变参数算法来对左值引用参数进行排序?类似的东西

int a=3;
int b=2;
int c=1;

orderInPlace(a,b,c);

// a is now 1
// b is now 2
// c is now 3

还有一个函数变体,比如order(a, b, c),它返回一个元组也很好。

如果没有,我想我们应该使用std.algorithm:swap

另见http://forum.dlang.org/thread/eweortsmcmibppmvtriw@forum.dlang.org#post-eweortsmcmibppmvtriw:40forum.dlang.org

【问题讨论】:

    标签: d in-place phobos


    【解决方案1】:

    Adam 的解决方案有效,尽管它使用了元素的临时副本。使用small modification to std.algorithm,可以编写一个对元素进行就地排序的版本:

    import std.algorithm;
    import std.stdio;
    import std.traits;
    import std.typecons;
    
    struct SortableRef(T)
    {
        private T * _p;
        @property ref T value() { return *_p; }
        alias value this;
        void opAssign(T * value) { _p = value; }
        @disable void opAssign(SortableRef!T value);
        void proxySwap(SortableRef!T other) { swap(*_p, *other._p); }
    }
    
    template PointerTo(T) { alias T* PointerTo; }
    void orderInPlace(T...)(ref T values)
        if (!is(CommonType!(staticMap!(PointerTo, T)) == void))
    {
        alias CommonType!T E;
        SortableRef!E[values.length] references;
        foreach (i, ref v; values)
            references[i] = &v;
        references[].sort();
    }
    
    void main()
    {
        int a=3;
        int b=1;
        int c=2;
        orderInPlace(a, b, c);
        writeln([a, b, c]);
    }
    

    但是,只有当传递给 orderInPlace 的值很大、无法赋值或无法复制时才实用。

    【讨论】:

    • 确实很聪明。谢谢。
    • 也许您应该在 github 问题线程中添加对这个出色答案的引用,以了解有关为什么激发 std.algorithm 拉取请求的更多背景信息?
    • 一个反射虽然...... - 不是所有的元素都必须是相同的类型才能在所有方向上相互isAssignable(组合)吗?我认为!CommonType!T == void 不足以满足此要求。
    • 约束过于宽松是对的,但这是因为每个参数的指针需要有一个共同的类型。我更新了答案。
    【解决方案2】:

    我认为 Phobos 没有,但你可以像这样制作自己的:

    void orderInPlace(T...)(ref T t) {
        import std.algorithm;
        T[0][T.length] buffer;
        foreach(idx, a; t)
            buffer[idx] = a;
        auto sorted = sort(buffer[]);
        foreach(idx, a; t)
            t[idx] = sorted[idx];
    }
    

    std.algorithm,sort 需要一个数组,但这很容易 - 我们将元组复制到堆栈数组中,对其进行排序,然后将信息复制回元组中。所以也许并不完美,但它会起作用。您可以通过仅返回 t 而不是 ref 来使其功能化。

    【讨论】:

    • 与重用qsort相比,如果我们想要获得最佳性能,我认为我们需要为每个特定的 n 定义一个重载(或静态 ifs)。无论如何谢谢。
    • 因为这是一个模板,每组不同的参数都会产生一个专门的函数。
    • 啊哈,所以 Phobos sort 包含这些专业化。太好了。
    【解决方案3】:

    考虑到参数的数量很少,并且它们的数量在编译时已知(无循环条件),这里的排序网络可能是最有效的。

    冒泡排序很适合网络排序。我把这个扔在一起了。它很有效,而且非常简单:

    import std.stdio, std.string;
    
    void bubbleSort(T...)(ref T values)
    {
        static if (T.length > 1)
        {
            foreach(I, _; T[0 .. $ - 1])
            {
                pragma(msg, format("[%s %s]", I, I + 1));
                compareAndSwap(values[I], values[I + 1]);
            }
            bubbleSort(values[0 .. $ - 1]);
        }
    }
    void compareAndSwap(T)(ref T a, ref T b)
    {
        import std.algorithm;
        if(a > b)
            swap(a, b);
    }
    
    void main()
    {
        int a =  10;
        int b =  30;
        int c =  11;
        int d =  20;
        int e =   4;
        int f = 330;
        int g =  21;
        int h = 110;
        shellSort(a, b, c, d, e, f, g, h);
        writefln("%s %s %s %s %s %s %s %s!", a, b, c, d, e, f, g, h);
    }
    

    虽然说实话,如果这是标准库,任何少于 10 个参数的排序网络都应该是手写的。

    编辑:我完全改变了以前的算法,这实际上是非常低效的。冒泡排序不是最佳的,但它实际上对排序算法很有效。那里有一些编译指示可以查看已构建的网络。

    【讨论】:

    • 嗯...我做了一些检查,这个解决方案实际上生成了大量的比较器。它高度效率低下。
    猜你喜欢
    • 2020-06-24
    • 2013-05-11
    • 2017-01-21
    • 2013-10-10
    • 1970-01-01
    • 2010-10-02
    • 2017-09-04
    • 2020-12-28
    • 1970-01-01
    相关资源
    最近更新 更多