2

次のようなメソッドのために、Java で Median of Medians を実装しようとしています。

Select(Comparable[] list, int pos, int colSize, int colMed)
  • list指定された位置を見つけるための値のリストです
  • pos指定位置です
  • colSize最初の段階で作成する列のサイズです
  • colMed私がmedXとして使用する列の位置です

どのソートアルゴリズムを使用するのが最適か、またはこれを正確に実装する方法がわかりません..

4

5 に答える 5

11

この問題を解決する必要があるかどうかはわかりませんが、http://www.ics.uci.edu/~eppstein/161/960130.htmlにはアルゴリズムがあります。

select(L,k)
{
    if (L has 10 or fewer elements)
    {
        sort L
        return the element in the kth position
    }

    partition L into subsets S[i] of five elements each
        (there will be n/5 subsets total).

    for (i = 1 to n/5) do
        x[i] = select(S[i],3)

    M = select({x[i]}, n/10)

    partition L into L1<M, L2=M, L3>M
    if (k <= length(L1))
        return select(L1,k)
    else if (k > length(L1)+length(L2))
        return select(L3,k-length(L1)-length(L2))
    else return M
}

幸運を!

于 2009-12-07T05:50:37.557 に答える
5

質問はJavaについて尋ねたので、ここにあります

import java.util.*;

public class MedianOfMedians {
    private MedianOfMedians() {

    }

    /**
     * Returns median of list in linear time.
     * 
     * @param list list to search, which may be reordered on return
     * @return median of array in linear time.
     */
    public static Comparable getMedian(ArrayList<Comparable> list) {
        int s = list.size();
        if (s < 1)
            throw new IllegalArgumentException();
        int pos = select(list, 0, s, s / 2);
        return list.get(pos);
    }

    /**
     * Returns position of k'th largest element of sub-list.
     * 
     * @param list list to search, whose sub-list may be shuffled before
     *            returning
     * @param lo first element of sub-list in list
     * @param hi just after last element of sub-list in list
     * @param k
     * @return position of k'th largest element of (possibly shuffled) sub-list.
     */
    public static int select(ArrayList<Comparable> list, int lo, int hi, int k) {
        if (lo >= hi || k < 0 || lo + k >= hi)
            throw new IllegalArgumentException();
        if (hi - lo < 10) {
            Collections.sort(list.subList(lo, hi));
            return lo + k;
        }
        int s = hi - lo;
        int np = s / 5; // Number of partitions
        for (int i = 0; i < np; i++) {
            // For each partition, move its median to front of our sublist
            int lo2 = lo + i * 5;
            int hi2 = (i + 1 == np) ? hi : (lo2 + 5);
            int pos = select(list, lo2, hi2, 2);
            Collections.swap(list, pos, lo + i);
        }

        // Partition medians were moved to front, so we can recurse without making another list.
        int pos = select(list, lo, lo + np, np / 2);

        // Re-partition list to [<pivot][pivot][>pivot]
        int m = triage(list, lo, hi, pos);
        int cmp = lo + k - m;
        if (cmp > 0)
            return select(list, m + 1, hi, k - (m - lo) - 1);
        else if (cmp < 0)
            return select(list, lo, m, k);
        return lo + k;
    }

    /**
     * Partition sub-list into 3 parts [<pivot][pivot][>pivot].
     * 
     * @param list
     * @param lo
     * @param hi
     * @param pos input position of pivot value
     * @return output position of pivot value
     */
    private static int triage(ArrayList<Comparable> list, int lo, int hi,
            int pos) {
        Comparable pivot = list.get(pos);
        int lo3 = lo;
        int hi3 = hi;
        while (lo3 < hi3) {
            Comparable e = list.get(lo3);
            int cmp = e.compareTo(pivot);
            if (cmp < 0)
                lo3++;
            else if (cmp > 0)
                Collections.swap(list, lo3, --hi3);
            else {
                while (hi3 > lo3 + 1) {
                    assert (list.get(lo3).compareTo(pivot) == 0);
                    e = list.get(--hi3);
                    cmp = e.compareTo(pivot);
                    if (cmp <= 0) {
                        if (lo3 + 1 == hi3) {
                            Collections.swap(list, lo3, lo3 + 1);
                            lo3++;
                            break;
                        }
                        Collections.swap(list, lo3, lo3 + 1);
                        assert (list.get(lo3 + 1).compareTo(pivot) == 0);
                        Collections.swap(list, lo3, hi3);
                        lo3++;
                        hi3++;
                    }
                }
                break;
            }
        }
        assert (list.get(lo3).compareTo(pivot) == 0);
        return lo3;
    }

}

これが機能することを確認する単体テストです...

import java.util.*;

import junit.framework.TestCase;

public class MedianOfMedianTest extends TestCase {
    public void testMedianOfMedianTest() {
        Random r = new Random(1);
        int n = 87;
        for (int trial = 0; trial < 1000; trial++) {
            ArrayList list = new ArrayList();
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                int v = r.nextInt(256);
                a[i] = v;
                list.add(v);
            }
            int m1 = (Integer)MedianOfMedians.getMedian(list);
            Arrays.sort(a);
            int m2 = a[n/2];
            assertEquals(m1, m2);
        }
    }
}

ただし、上記のコードは実際に使用するには遅すぎます。

k 番目の要素を取得する簡単な方法を次に示します。パフォーマンスは保証されませんが、実際にははるかに高速です。

/**
 * Returns position of k'th largest element of sub-list.
 * 
 * @param list list to search, whose sub-list may be shuffled before
 *            returning
 * @param lo first element of sub-list in list
 * @param hi just after last element of sub-list in list
 * @param k
 * @return position of k'th largest element of (possibly shuffled) sub-list.
 */
static int select(double[] list, int lo, int hi, int k) {
    int n = hi - lo;
    if (n < 2)
        return lo;

    double pivot = list[lo + (k * 7919) % n]; // Pick a random pivot

    // Triage list to [<pivot][=pivot][>pivot]
    int nLess = 0, nSame = 0, nMore = 0;
    int lo3 = lo;
    int hi3 = hi;
    while (lo3 < hi3) {
        double e = list[lo3];
        int cmp = compare(e, pivot);
        if (cmp < 0) {
            nLess++;
            lo3++;
        } else if (cmp > 0) {
            swap(list, lo3, --hi3);
            if (nSame > 0)
                swap(list, hi3, hi3 + nSame);
            nMore++;
        } else {
            nSame++;
            swap(list, lo3, --hi3);
        }
    }
    assert (nSame > 0);
    assert (nLess + nSame + nMore == n);
    assert (list[lo + nLess] == pivot);
    assert (list[hi - nMore - 1] == pivot);
    if (k >= n - nMore)
        return select(list, hi - nMore, hi, k - nLess - nSame);
    else if (k < nLess)
        return select(list, lo, lo + nLess, k);
    return lo + k;
}
于 2014-12-31T10:27:16.380 に答える
2

Chip Uniからの回答/解決策に同意します。ソート部分についてコメントし、さらに説明を加えます。

ソートアルゴリズムは必要ありません。このアルゴリズムはクイックソートに似ていますが、1 つのパーティション (左または右) のみが解決されるという違いがあります。左部分と右部分ができるだけ等しくなるように最適なピボットを見つける必要があるだけです。これは、N/2 + N/4 + N/8 ... = 2N 回の反復を意味し、したがって O(N )。中央値の中央値と呼ばれる上記のアルゴリズムは、5 の中央値の中央値を計算します。これにより、アルゴリズムの線形時間の複雑さが得られます。

ただし、アルゴリズムを高速化するために、範囲が n 番目に小さい/最大の要素 (このアルゴリズムで実装していると思われます) を検索するときに、並べ替えアルゴリズムが使用されます。挿入ソートは、最大 7 ~ 10 要素の小さな配列で特に高速です。

実装上の注意:

M = select({x[i]}, n/10)

実際には、5 要素グループのすべての中央値の中央値を取ることを意味します。サイズの別の配列を作成し(n - 1)/5 + 1、同じアルゴリズムを再帰的に呼び出して、n/10 番目の要素 (新しく作成された配列の中央値) を見つけることで、これを実現できます。

于 2011-02-04T11:49:02.847 に答える
-1

これは非常に古い投稿であり、もう覚えていないかもしれません。しかし、実装時に実装の実行時間を測定したのだろうか?

このアルゴリズムを試して、Java の並べ替えメソッド (Arrays.sort() ) を使用した単純なアプローチと比較し、並べ替えられた配列から k 番目の要素を選択しました。私が受け取った結果は、配列のサイズが約 10 万要素以上の場合にのみ、このアルゴリズムが Java ソート アルゴリズムより優れているということです。そして、それは約 2 倍か 3 倍高速であり、明らかに log(n) 時間高速ではありません。

それについて何かコメントはありますか?

于 2011-09-27T05:27:55.953 に答える