「サブベクトル反転」を使用してソートを最適化する


23

これは、最小の反転を使用してベクトルを昇順でソートすることを目的とする、チャレンジです。アルゴリズムは「サブベクトル反転」1を使用してのみベクトルをソートできますが、算術演算、ループ、ソートされているかどうかの確認などに他の演算を使用できます。アルゴリズムが実行するサブベクトル反転の数はスコアです。


1 「サブベクトル反転」:

  • ベクトル内の数値の範囲を選択し、その範囲内の要素を逆にします。

簡単な例を挙げると、vector {4,3,2,1}から始める場合、さまざまな方法で並べ替えることができます。

  1. ベクトル全体を反転します。これは、1回の反転のみを必要とするため、明らかに最短のアプローチです。{4,3,2,1} -> {1,2,3,4}
  2. バブルソートのバージョンを実行できます。これには、6回の取り消しが必要です。 {4,3,2,1} -> {3,4,2,1} -> {3,2,4,1} -> {2,3,4,1} -> {2,3,1,4} -> {2,1,3,4} -> {1,2,3,4}
  3. 最初の3つの要素から始めて、最後に3つ、最後に最初の2つと最後の2つで、4つのスワップが必要です。 {4,3,2,1} -> {2,3,4,1} -> {2,1,4,3} -> {1,2,4,3} -> {1,2,3,4}
  4. ... 等々。使用可能なオプションは無限にあります(必要に応じて、任意の操作を繰り返すことができます)。

ルールと要件:

  • 100個の数字を含むリストの場合、コードは1分未満で終了する必要があります。これは自分で時間を計ることができますが、フェア2をプレイしてください。

  • ソリューションを検証できるように、実行するすべてのスワップの開始インデックスと終了インデックスを保存する必要があります。(これの意味を以下で説明します)。

  • コードは確定的でなければなりません。

  • 数値ベクトル、リンクリスト、長さの配列など、任意の形式で入力を取得できます。

  • ベクターのコピーで何でもできます。これには、さまざまな反転の試行と、最も効率的なチェックが含まれます。総当たり攻撃はまったく問題ありませんが、制限時間を守ってください。

  • スコアは、5つのテストベクトルのフリップの総数です。タイブレーカーは日付スタンプになります。


例:

4 1 23 21 49 2 7 9 2 | 初期ベクトル/リスト
4 1 2 9 7 2 49 21 23 | (2、8)(インデックス2と8の間で要素を反転)
4 1 2 2 7 9 49 21 23 | (3、5)
4 1 2 2 7 9 23 21 49 | (6、8)
4 1 2 2 7 9 21 23 49 | (6、7)
 2 2 1 4 7 9 21 23 49 | (0、3)
 1 2 2 4 7 9 21 23 49 | (0、2)

6回の反転を実行したため、スコアは6になります。右側の一覧にあるインデックスは、検証のために簡単に印刷/出力できる適切な形式で保存する必要があります(印刷しないでください)。

テストベクトル:

133, 319, 80, 70, 194, 333, 65, 21, 345, 142, 82, 491, 92, 167, 281, 386, 48, 101, 394, 130, 111, 139, 214, 337, 180, 24, 443, 35, 376, 13, 166, 59, 452, 429, 406, 256, 133, 435, 446, 304, 350, 364, 447, 471, 236, 177, 317, 342, 294, 146, 280, 32, 135, 399, 78, 251, 467, 305, 366, 309, 162, 473, 27, 67, 305, 497, 112, 399, 103, 178, 386, 343, 33, 134, 480, 147, 466, 244, 370, 140, 227, 292, 28, 357, 156, 367, 157, 60, 214, 280, 153, 445, 301, 108, 77, 404, 496, 3, 226, 37

468, 494, 294, 42, 19, 23, 201, 47, 165, 118, 414, 371, 163, 430, 295, 333, 147, 336, 403, 490, 370, 128, 261, 91, 173, 339, 40, 54, 331, 236, 255, 33, 237, 272, 193, 91, 232, 452, 79, 435, 160, 328, 47, 179, 162, 239, 315, 73, 160, 266, 83, 451, 317, 255, 491, 70, 18, 275, 339, 298, 117, 145, 17, 178, 232, 59, 109, 271, 301, 437, 63, 103, 130, 15, 265, 281, 365, 444, 180, 257, 99, 248, 378, 158, 210, 466, 404, 263, 29, 117, 417, 357, 44, 495, 303, 428, 146, 215, 164, 99

132, 167, 361, 145, 36, 56, 343, 330, 14, 412, 345, 263, 306, 462, 101, 453, 364, 389, 432, 32, 200, 76, 268, 291, 35, 13, 448, 188, 11, 235, 184, 439, 175, 159, 360, 46, 193, 440, 334, 128, 346, 192, 263, 466, 175, 407, 340, 393, 231, 472, 122, 254, 451, 485, 257, 67, 200, 135, 132, 421, 205, 398, 251, 286, 292, 488, 480, 56, 284, 484, 157, 264, 459, 6, 289, 311, 116, 138, 92, 21, 307, 172, 352, 199, 55, 38, 427, 214, 233, 404, 330, 105, 223, 495, 334, 169, 168, 444, 268, 248

367, 334, 296, 59, 18, 193, 118, 10, 276, 180, 242, 115, 233, 40, 225, 244, 147, 439, 297, 115, 354, 248, 89, 423, 47, 458, 64, 33, 463, 142, 5, 13, 89, 282, 186, 12, 70, 289, 385, 289, 274, 136, 39, 424, 174, 186, 489, 73, 296, 39, 445, 308, 451, 384, 451, 446, 282, 419, 479, 220, 35, 419, 161, 14, 42, 321, 202, 30, 32, 162, 444, 215, 218, 102, 140, 473, 500, 480, 402, 1, 1, 79, 50, 54, 111, 189, 147, 352, 61, 460, 196, 77, 315, 304, 385, 275, 65, 145, 434, 39

311, 202, 126, 494, 321, 330, 290, 28, 400, 84, 6, 160, 432, 308, 469, 459, 80, 48, 292, 229, 191, 240, 491, 231, 286, 413, 170, 486, 59, 54, 36, 334, 135, 39, 393, 201, 127, 95, 456, 497, 429, 139, 81, 293, 359, 477, 404, 129, 129, 297, 298, 495, 424, 446, 57, 296, 10, 269, 350, 337, 39, 386, 142, 327, 22, 352, 421, 32, 171, 452, 2, 484, 337, 359, 444, 246, 174, 23, 115, 102, 427, 439, 71, 478, 89, 225, 7, 118, 453, 350, 109, 277, 338, 474, 405, 380, 256, 228, 277, 3

最適なソリューションを見つけることはNP困難であることはかなり確信しています(通常のパンケーキソートはそうであるため)。

2はい、1分の時間制限があるため、コンピューターの速度が非常に速い人にはメリットがあります。多くの議論の後、誰もが独自のベンチマークを行うのが最善であると考えましたが、それは最速のコードチャレンジではありません。



1
最適なソリューションは、多くても反転数の挿入ソートと同等である必要があり、各反転で1つの数字を配置できます。
-fəˈnɛtɪk

3
これはパンケーキフリッピングではありません(1つの場所から最後までしかフリップできません)。選択ソートはO(n)で、n-1スワップを使用します。最悪の場合、n-1個のスワップが必要です。選択ソートは漸近的に最適です。
-orlp

1.入力は整数のリスト/ベクトルですか?2.プログラムの出力はどうあるべきですか?3.入力のサブベクトル反転ソートを実行する限り、プログラムは、おそらく異なる方法(クイックソートなど)を使用して、操作を最適化する方法を決定するために、ベクトルまたはその一部を複数回ソートできますか最後にベクトル(要求どおり)?
-aditsu

1
@orlp n-1フリップで最悪のケースがあることを証明できますか?私は約50の下限しか証明できません
。– user202729

回答:


6

Java、遺伝的アルゴリズム、80 + 81 + 79 + 78 + 80 = 398(以前は418

さまざまなアイデアを試し、ほとんど失敗した後、私はこのアルゴリズムに落ち着きました:入力配列から始め、可能なすべての反転を試行し、最小数の実行で特定の結果を保持し、それらの結果に対して同じことを行いますソートされた配列を取得します。

「実行」とは、ソートされた配列で正確に表示されるか、逆に表示される最大のサブ配列を意味します。基本的に、それらは最大のソートされたサブ配列ですが、要素が繰り返される場合、中央の要素の数は一致する必要があります。例えばソートされた配列がある場合は2, 2, 3, 3, 4, 4、次に4, 3, 3, 2実行されますが、2, 2, 3, 4(ともないではありません2, 3, 2)。

このバージョンでは、実行境界でのみ逆になるようにアルゴリズムを最適化し、逆の実行を新しく隣接する実行と結合できる場合にのみ行いました。また、修正された配列から再計算を回避するために、各ステップで実行が調整および結合されます。これにより、「人口サイズ」を30から約3000に増やし、さまざまなサイズで複数のシミュレーションを実行できました。

import java.io.*;
import java.util.*;

public class SubReversal {
    static int n;
    static int[] a;
    static int[] srt;
    static List<int[]> rev;
    static Map<Integer, Integer> idx;
    static Map<Integer, Integer> count;

    static final int NB = 2000;
    static State[] best = new State[NB + 1];
    static int ns;

    static class Run {
        int start;
        int end;
        int dir;
        int nstart = 1;
        int nend = 1;

        Run(final int start) {
            this.start = start;
        }

        Run(final Run r) {
            start = r.start;
            end = r.end;
            dir = r.dir;
            nstart = r.nstart;
            nend = r.nend;
        }

        Run copy() {
            return new Run(this);
        }

        Run reverse() {
            int t = start;
            start = end;
            end = t;
            t = nstart;
            nstart = nend;
            nend = t;
            dir = -dir;
            return this;
        }

        boolean canJoin(final Run r) {
            if (dir * r.dir == -1) {
                return false;
            }
            final int t = idx.get(a[r.start]) - idx.get(a[end]);
            if (Math.abs(t) > 1) {
                return false;
            }
            if (t != 0 && dir + r.dir != 0 && t != dir && t != r.dir) {
                return false;
            }
            if (t == 0) {
                if (dir * r.dir == 0) {
                    return true;
                }
                return nend + r.nstart == count.get(a[end]);
            }
            return (dir == 0 || nend == count.get(a[end])) && (r.dir == 0 || r.nstart == count.get(a[r.start]));
        }

        Run join(final Run r) {
            if (a[start] == a[r.start]) {
                nstart += r.nstart;
            }
            if (a[end] == a[r.end]) {
                nend += r.nend;
            }
            else {
                nend = r.nend;
            }
            end = r.end;
            if (dir == 0) {
                dir = r.dir;
            }
            if (dir == 0 && a[start] != a[end]) {
                dir = idx.get(a[end]) - idx.get(a[start]);
            }
            return this;
        }

        @Override
        public String toString() {
            return start + "(" + nstart + ") - " + end + '(' + nend + "): " + dir;
        }
    }

    static class State implements Comparable<State> {
        int[] b;
        int[] rv;
        State p;
        List<Run> runs;

        public State(final int[] b, final int[] rv, final State p, final List<Run> runs) {
            this.b = Arrays.copyOf(b, b.length);
            this.rv = rv;
            this.p = p;
            this.runs = runs;
        }

        @Override
        public int compareTo(final State o) {
            return runs.size() - o.runs.size();
        }

        @Override
        public String toString() {
            return Arrays.toString(b) + " - " + Arrays.toString(rv) + " - " + runs.size();
        }

        int getCount() {
            return p == null ? 0 : p.getCount() + 1;
        }
    }

    static void reverse(int x, int y) {
        while (x < y) {
            int t = a[x];
            a[x] = a[y];
            a[y] = t;
            x++;
            y--;
        }
    }

    static List<Run> runs() {
        final List<Run> l = new ArrayList<>();
        Run run = new Run(0);
        for (int i = 1; i < n; ++i) {
            final int t = idx.get(a[i]) - idx.get(a[i - 1]);
            if (Math.abs(t) > 1) {
                run.end = i - 1;
                l.add(run);
                run = new Run(i);
            }
            else if (t == 0) {
                run.nend++;
                if (run.dir == 0) {
                    run.nstart++;
                }
            }
            else {
                if (run.dir == 0) {
                    run.dir = t;
                }
                else if (run.dir != t || run.nend != count.get(a[i - 1])) {
                    run.end = i - 1;
                    l.add(run);
                    run = new Run(i);
                }
                run.nend = 1;
            }
        }
        run.end = n - 1;
        l.add(run);
        return l;
    }

    static void show() {
        if (!Arrays.equals(a, srt)) {
            System.out.println("bug!");
            System.out.println(Arrays.toString(a));
            throw new RuntimeException();
        }
        System.out.println("Sorted: " + Arrays.toString(a));
        System.out.println(rev.size() + " reversal(s):");
        for (int[] x : rev) {
            System.out.println(Arrays.toString(x));
        }
    }

    static void sort() {
        State bestest = null;
        final int[] a1 = Arrays.copyOf(a, n);
        final int[] sizes = {10, 20, 30, 50, 100, 200, 300, 500, 1000, 2000};

        for (int nb : sizes) {
            System.arraycopy(a1, 0, a, 0, n);
            ns = 1;
            best[0] = new State(a, null, null, runs());
            while (best[0].runs.size() > 1) {
                final State[] s = Arrays.copyOf(best, ns);
                ns = 0;
                for (State x : s) {
                    System.arraycopy(x.b, 0, a, 0, n);
                    final int m = x.runs.size();
                    for (int i = 0; i < m; ++i) {
                        for (int j = i; j < m; ++j) {
                            boolean b = false;
                            if (i > 0) {
                                final Run r = x.runs.get(j);
                                r.reverse();
                                b = x.runs.get(i - 1).canJoin(r);
                                r.reverse();
                            }
                            if (!b && j < m - 1) {
                                final Run r = x.runs.get(i);
                                r.reverse();
                                b = r.canJoin(x.runs.get(j + 1));
                                r.reverse();
                            }
                            if (!b) {
                                continue;
                            }
                            final List<Run> l = new ArrayList<>(x.runs);
                            final int rstart = l.get(i).start;
                            final int rend = l.get(j).end;
                            final int t = rstart + rend;
                            reverse(rstart, rend);
                            for (int k = i; k <= j; ++k) {
                                final Run r = x.runs.get(i + j - k).copy().reverse();
                                r.start = t - r.start;
                                r.end = t - r.end;
                                l.set(k, r);
                            }
                            if (j < m - 1 && l.get(j).canJoin(l.get(j + 1))) {
                                l.get(j).join(l.get(j + 1));
                                l.remove(j + 1);
                            }
                            if (i > 0 && l.get(i - 1).canJoin(l.get(i))) {
                                l.set(i - 1, l.get(i - 1).copy().join(l.get(i)));
                                l.remove(i);
                            }

                            if (ns < nb || l.size() < best[ns - 1].runs.size()) {
                                best[ns++] = new State(a, new int[]{rstart, rend}, x, l);
                                Arrays.sort(best, 0, ns);
                                if (ns > nb) {
                                    ns = nb;
                                }
                            }
                            reverse(rstart, rend);
                        }
                    }
                }

                if (ns == 0) {
                    for (State x : s) {
                        System.arraycopy(x.b, 0, a, 0, n);
                        final List<Run> l = new ArrayList<>(x.runs);
                        final int rstart = l.get(0).start;
                        final int rend = l.get(0).end;
                        final int t = rstart + rend;
                        reverse(rstart, rend);
                        final Run r = x.runs.get(0).copy().reverse();
                        r.start = t - r.start;
                        r.end = t - r.end;
                        l.set(0, r);

                        best[ns++] = new State(a, new int[]{rstart, rend}, x, l);
                        reverse(rstart, rend);
                    }
                    Arrays.sort(best, 0, ns);
                }
            }
            State r = null;
            for (int i = 0; i < ns; ++i) {
                if (Arrays.equals(best[i].b, srt)) {
                    r = best[i];
                    break;
                }
            }
            if (r == null) {
                final State x = best[0];
                System.arraycopy(x.b, 0, a, 0, n);
                reverse(0, n - 1);
                r = new State(a, new int[]{0, n - 1}, x, runs());
            }
            if (!Arrays.equals(r.b, srt)) {
                throw new RuntimeException("bug");
            }

            if (bestest == null || r.getCount() < bestest.getCount()) {
                bestest = r;
            }
        }

        while (bestest.p != null) {
            rev.add(bestest.rv);
            bestest = bestest.p;
        }
        Collections.reverse(rev);
        a = a1;
        for (int[] x : rev) {
            reverse(x[0], x[1]);
        }
        if (!Arrays.equals(a, srt)) {
            throw new RuntimeException("bug");
        }
    }

    static void init(final String s) {
        final String[] b = s.split(s.contains(",") ? "," : " ");
        n = b.length;
        a = new int[n];
        count = new HashMap<>();
        for (int i = 0; i < n; ++i) {
            a[i] = Integer.parseInt(b[i].trim());
            final Integer x = count.get(a[i]);
            count.put(a[i], x == null ? 1 : x + 1);
        }
        srt = Arrays.copyOf(a, n);
        Arrays.sort(srt);
        idx = new HashMap<>();
        int j = 0;
        for (int i = 0; i < n; ++i) {
            if (i == 0 || srt[i] != srt[i - 1]) {
                idx.put(srt[i], j++);
            }
        }
        rev = new ArrayList<>();
    }

    static void test5() {
        final String[] t = {"133, 319, 80, 70, 194, 333, 65, 21, 345, 142, 82, 491, 92, 167, 281, 386, 48, 101, 394, 130, 111, 139, 214, 337, 180, 24, 443, 35, 376, 13, 166, 59, 452, 429, 406, 256, 133, 435, 446, 304, 350, 364, 447, 471, 236, 177, 317, 342, 294, 146, 280, 32, 135, 399, 78, 251, 467, 305, 366, 309, 162, 473, 27, 67, 305, 497, 112, 399, 103, 178, 386, 343, 33, 134, 480, 147, 466, 244, 370, 140, 227, 292, 28, 357, 156, 367, 157, 60, 214, 280, 153, 445, 301, 108, 77, 404, 496, 3, 226, 37",
                "468, 494, 294, 42, 19, 23, 201, 47, 165, 118, 414, 371, 163, 430, 295, 333, 147, 336, 403, 490, 370, 128, 261, 91, 173, 339, 40, 54, 331, 236, 255, 33, 237, 272, 193, 91, 232, 452, 79, 435, 160, 328, 47, 179, 162, 239, 315, 73, 160, 266, 83, 451, 317, 255, 491, 70, 18, 275, 339, 298, 117, 145, 17, 178, 232, 59, 109, 271, 301, 437, 63, 103, 130, 15, 265, 281, 365, 444, 180, 257, 99, 248, 378, 158, 210, 466, 404, 263, 29, 117, 417, 357, 44, 495, 303, 428, 146, 215, 164, 99",
                "132, 167, 361, 145, 36, 56, 343, 330, 14, 412, 345, 263, 306, 462, 101, 453, 364, 389, 432, 32, 200, 76, 268, 291, 35, 13, 448, 188, 11, 235, 184, 439, 175, 159, 360, 46, 193, 440, 334, 128, 346, 192, 263, 466, 175, 407, 340, 393, 231, 472, 122, 254, 451, 485, 257, 67, 200, 135, 132, 421, 205, 398, 251, 286, 292, 488, 480, 56, 284, 484, 157, 264, 459, 6, 289, 311, 116, 138, 92, 21, 307, 172, 352, 199, 55, 38, 427, 214, 233, 404, 330, 105, 223, 495, 334, 169, 168, 444, 268, 248",
                "367, 334, 296, 59, 18, 193, 118, 10, 276, 180, 242, 115, 233, 40, 225, 244, 147, 439, 297, 115, 354, 248, 89, 423, 47, 458, 64, 33, 463, 142, 5, 13, 89, 282, 186, 12, 70, 289, 385, 289, 274, 136, 39, 424, 174, 186, 489, 73, 296, 39, 445, 308, 451, 384, 451, 446, 282, 419, 479, 220, 35, 419, 161, 14, 42, 321, 202, 30, 32, 162, 444, 215, 218, 102, 140, 473, 500, 480, 402, 1, 1, 79, 50, 54, 111, 189, 147, 352, 61, 460, 196, 77, 315, 304, 385, 275, 65, 145, 434, 39",
                "311, 202, 126, 494, 321, 330, 290, 28, 400, 84, 6, 160, 432, 308, 469, 459, 80, 48, 292, 229, 191, 240, 491, 231, 286, 413, 170, 486, 59, 54, 36, 334, 135, 39, 393, 201, 127, 95, 456, 497, 429, 139, 81, 293, 359, 477, 404, 129, 129, 297, 298, 495, 424, 446, 57, 296, 10, 269, 350, 337, 39, 386, 142, 327, 22, 352, 421, 32, 171, 452, 2, 484, 337, 359, 444, 246, 174, 23, 115, 102, 427, 439, 71, 478, 89, 225, 7, 118, 453, 350, 109, 277, 338, 474, 405, 380, 256, 228, 277, 3"};
        int r = 0;
        for (String s : t) {
            init(s);
            sort();
            System.out.println(rev.size());
            r += rev.size();
        }
        System.out.println("total: " + r);
    }

    public static void main(final String... args) throws IOException {
        System.out.print("Input: ");
        final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        final String s = br.readLine();
        final long t = System.currentTimeMillis();
        if (s.isEmpty()) {
            System.out.println("Running tests");
            test5();
        }
        else {
            init(s);
            sort();
            show();
        }
        System.out.println("Time: " + (System.currentTimeMillis() - t + 500) / 1000 + " sec");
    }
}

入力は、カンマやスペースで区切られた数値のリストです(stdinから)。入力が空の場合、プログラムは5つのテストを実行します。ここではそれぞれ約40秒かかります。


興味深いことに、5番目のテストケースでの反転回数は、新しいバージョンでは改善されませんでした。その他はかなり改善されています。私はあなたがそれをもう一度やってみることにしたことをうれしく思います:)
Stewie Griffin

@StewieGriffinありがとう、あなたは私が20kを過ぎて吹き飛ばしてくれたのを助けてくれました:)以前の最後のケースで少しラッキーになったと思います。ランダム化されたアプローチでは、おそらくさらに良い結果が得られます。
-aditsu

5

1回のブルートフォース、選択ソート(単純なソリューション)、90 + 89 + 88 + 87 + 89 = 443の移動

let doReverse = (a, l, r) => {
  a.splice(l, r - l, ...a.slice(l, r).reverse());
};
let selectSubVectorReverseSort = a => {
  let log = [];

  for (let i = 0, l = a.length; i < l; i++) {
    let j, p = i;
    for (j = i; j < l; j++) {
      if (a[j] < a[p]) p = j;
    }
    if (p === i) continue;
    log.push([i, p + 1]);
    doReverse(a, i, p + 1);
  }
  return log;
};

let a = JSON.parse(`[${readline()}]`);
let copiedArray = a => a.map(x => x);
let minLog = selectSubVectorReverseSort(copiedArray(a));
for (let i = 0, l = a.length; i < l; i++) {
  for (let j = i + 1; j < l; j++) {
    let b = copiedArray(a);
    doReverse(b, i, j + 1);
    let log = [[i, j + 1], ...selectSubVectorReverseSort(b)];
    if (log.length < minLog.length) minLog = log;
  }
}

print(minLog.length);

可能な最初の動きごとに、それを試してから、選択ソートを行います。

はい、これは別の単純なソリューションです。

これが編集または別の投稿であるべきかどうかはわかりませんが、解決策は単純すぎるようであるため、編集が選択されます。


選択ソート(単純ソリューション)、92 + 93 + 95 + 93 + 96 = 469移動

let log = [];
let doReverse = (a, l, r) => {
  log.push([l, r]);
  a.splice(l, r - l, ...a.slice(l, r).reverse());
}

let a = JSON.parse(`[${readline()}]`);
for (let i = 0, l = a.length; i < l; i++) {
  let j, p = i;
  for (j = i; j < l; j++) {
    if (a[j] < a[p]) p = j;
  }
  if (p === i) continue;
  doReverse(a, i, p + 1);
}
print(log.length)

単純なソリューションでは、選択ソートを使用します。

より良い解決策がいくつかあるに違いありませんが、より良い解決策を見つけられなかったので(ブルートフォース検索なしで)これを投稿してください。

(上記のコードはJavaScript Shellです

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.