【问题标题】:FlagSort - Divide and Conquer - Minor troubleFlagSort - 分而治之 - 小麻烦
【发布时间】:2012-05-27 05:23:23
【问题描述】:

在实现特定的排序算法时,我遇到了一个问题,即使 Google 也不想帮我解决……首先关于算法的一些话

最大的份额是将输入(数组)划分为三个部分:选择两个关键数字(红色和蓝色,断言红色

对数组进行排序应该递归地进行:在对其分区进行分区之前使用任意枢轴对输入进行分区,从而以长度为 1 或 2 的粒子结束,这些粒子按 def 排序。或者说可以很容易地排序。

问题是现在长度 >= 3 的分区由一个键值组成:如果再次分区,无论选择什么枢轴,之后所有元素都会放入同一个分区,最终会遇到堆栈溢出。 这就是为什么我认为你能够帮助我,因为我确信有解决方案。

强制 JavaCode sn-p: Partitioning - 对不起德语调试,也懒得翻译了。 IntTuple 只能容纳两个整数;没什么太荒谬的。

public static IntTuple partition(int[] E, int left, int right, int red, int blue){
    if (red > blue) {
        int v = red;
        red = blue;
        blue = v;
    }
    System.out.println("Partition Eingabe: " + Arrays.toString(E) + " Links=" + left + " Rechts=" + right + " Rot=" + red + " Blau=" + blue);
    /*
     * Es gilt r <= b, also gilt für beliebige x...
     * ... x < r => x < b
     * ... x > b => x > r.
     *
     * Das Gerüst für diesen Algorithmus wurde kopiert von Folie 7-13
     */
    IntTuple result = new IntTuple (left, right); // rote und blaue Regionen sind leer
    int u = left; // weiße Region ist leer, die unbekannte == E[left...right]
    while (u <= result.v2) {
        System.out.print("E[" + u + "]: ");
        if (E[u] < red) {
            System.out.print("rot  ");
            swap(E, result.v1, u);
            result.v1++; // vergrößere die rote Region
            u++; // verkleinere die unbekannte Region
        } else if ((E[u] >= red) && (E[u] <= blue)) {
            System.out.print("weiß ");
            u++; // verkleinere die unbekannte Region
        } else if (E[u] > blue) {
            System.out.print("blau ");
            swap(E, result.v2, u);
            result.v2--; // vergrößere die blaue Region
        }
        System.out.print("Partition Schritt: [");
        for(int i = left; i < right; i++)
            System.out.print("" + E[i] + " ");
        System.out.println("" + E[right] + "]");
    }
    System.out.print("Partition Ausgabe: [");
    for(int i = left; i < right; i++)
        System.out.print("" + E[i] + " ");
    System.out.println("" + E[right] + "]" + " RotG=" + result.v1 + " BlauG=" + result.v2);
    return result;
}

强制 JavaCode sn-p:排序

private static void flagSort(int[] E, int left, int right){
    System.out.println("Sortiere: " + left + " bis " + right);
    if(left < right - 1) {
        Random rnd = new Random();
        IntTuple v = partition(E, left, right, rnd.nextInt(50), rnd.nextInt(50));
        //IntTuple v = partition(E, left, right, E[left], E[left + 1]);
        flagSort(E, left, v.v1 - 1);
        flagSort(E, v.v1, v.v2);
        flagSort(E, v.v2 + 1, right);
    } else if((left == right - 1) && (E[left] > E[right])) {
        swap(E, left, right);
    }
}

非常感谢您的任何想法!

问候,LDer

更多:我想出了这个笨拙的解决方案:

private static void flagSort(int[] E, int left, int right, boolean dual){
    if(left < right) { // Singleton or empty segments are already sorted!
        IntTuple v;
        if(dual) // The last step has produced only a single white partition.
            // Treat this partition with double pivot
            v = partition(E, left, right, E[left], E[left]);
        else    // The last step has produced more than one partition, go on normally.
            v = partition(E, left, right, E[left], E[left + 1]);
        // Analyze partitions
        if((left != v.v1) || (right != v.v2)) {
            // 2 or 3 partitions available. Descend further.
            flagSort(E, left, v.v1 - 1, false);
            flagSort(E, v.v1, v.v2, false);
            flagSort(E, v.v2 + 1, right, false);
        } else if(!dual) {
            // Only the white partition is not empty, partition it with double pivot
            flagSort(E, v.v1, v.v2, true);
        } // Last case: The only not-empty partition is white after partitioning with double pivot.
          // Description of the algorithm immediately implies that this consists of only one key value, thus is sorted.
    }
}

谁能帮忙创建一个更易读的版本?

更多:这个看起来好多了:

private static void flagSort(int[] E, int left, int right, int offset){
    if(left < right) { // Singleton or empty segments are already sorted!
        IntTuple v = partition(E, left, right, E[left], E[left + offset]);
        // Analyze partitions
        if ((left != v.v1) || (right != v.v2)) {
            // 2 or 3 partitions available. Descend further.
            flagSort(E, left, v.v1 - 1, 1);
            flagSort(E, v.v1, v.v2, 1);
            flagSort(E, v.v2 + 1, right, 1);
        } else if (offset > 0)
            // Only the white partition is not empty, partition it with double pivot
            flagSort2(E, v.v1, v.v2, 0);
        // Last case: The only not-empty partition is white after partitioning with double pivot.
        // Description of the algorithm immediately implies that this consists of only one key value, thus is sorted.
    }
}

特别感谢 toto2,即使我没有明确传递红色/蓝色!

MORE:更多随机性,因为toto2又完全有道理:

private static void flagSort(int[] E, int left, int right, int offset){
    if(left < right) {
        IntTuple v = partition(E, left, right, E[left + (offset % (right - left))], 
            E[left + ((2 * offset) % (right - left))]);
        if ((left != v.v1) || (right != v.v2)) {
            int random = rnd.nextInt(right - left);
            flagSort(E, left, v.v1 - 1, random);
            flagSort(E, v.v1, v.v2, random);
            flagSort(E, v.v2 + 1, right, random);
        } else if (offset > 0)
            flagSort(E, v.v1, v.v2, 0);
    }
}

【问题讨论】:

  • 次要评论:Random rnd = new Random(); 应该是类成员,而不是每次调用 flagSort 时都重新初始化。
  • @toto2 你是对的,当然,但这不会影响正确性,我很高兴安排它稍后修复:)

标签: java sorting recursion stack-overflow divide-and-conquer


【解决方案1】:

每次调用分区方法时,都会发生一件简单的事情: 蓝色和红色索引处的项目正是它们所属的位置。 因此,当您调用这部分代码时:

    flagSort(E, left, v.v1 - 1);
    flagSort(E, v.v1, v.v2);
    flagSort(E, v.v2 + 1, right);

这些调用不能包含蓝色和红色, 换句话说:

 flagSort(E, left, v.v1 - 1);
 flagSort(E, v.v1 +1 , v.v2 -1);
 flagSort(E, v.v2 + 1, right);

【讨论】:

  • 我通过查看我的代码并通过实验确认了这一点:最终得到一个包含数组中所有元素的单个白色(不是红色或蓝色)分区是合法的,只要枢轴是所有元素的上限和下限。如果我确实使用了您的 sn-p,那么在这种情况下,我会通过 2 个边缘元素去除白色分区并将它们放入红色/蓝色,即使白色分区尚未排序。
【解决方案2】:
IntTuple v = partition(E, left, right, rnd.nextInt(50), rnd.nextInt(50));

50?您知道当前分区的最小-最大(红-蓝)值,该范围比 0-49 更窄。您应该修改 flagSort 以便不仅传递左右值,还传递红蓝值。

如果使用 red == blue 调用 flagSort,您可以直接返回,因为子数组具有相同的值并且已经排序。

【讨论】:

  • 我想出了一个可行的解决方案,它完全去除了随机部分,但感觉有点可怕。请在几分钟后检查 OP...
  • 用E[left],E[left + 1]调用partition的时候你还在使用随机性,但是你使用的是数组的随机性。
  • 我在您的新解决方案中不太了解您的“双重”。在if (dual) 之后的评论中,您说您使用的是双枢轴,但您称其为单枢轴:E[left],E[left]。如果您只有一个中间(白色)数组,我看不到使用单个枢轴的用途。该数组和其他数组一样只是一个普通数组:它可以包含许多不同的数字。
  • 是的,这就是我称之为尴尬的原因......“双重”/“双重”支点应该只指出它们是明确相同的,而不是(大多数)其他函数调用。相当误导,确实......
  • 您可能仍想传递红蓝值并使用 Random():如果给定一个已排序的数组,您当前的方法将执行得尽可能低。
猜你喜欢
  • 2013-11-09
  • 2013-01-31
  • 2012-03-04
  • 2011-05-22
  • 2014-10-20
  • 2014-12-12
  • 2015-01-25
  • 2013-02-02
  • 2023-03-14
相关资源
最近更新 更多