【问题标题】:Permute array elements maximizing the distance vector Java排列数组元素最大化距离向量Java
【发布时间】:2014-02-14 13:09:26
【问题描述】:

假设 v = [0, 1, 2, 3, 4] ,我需要对其进行置换,以便新的索引 每个元素都尽可能远。我的意思是,最小化距离向量的方差,同时,最大化。例如,为 d 距离向量:

Opt 1 -> [0, 4, 1, 3, 2], d = [3, 2, 1, 0] -> 不行!不是统一的。
Opt 2 -> [0, 1, 2, 3, 4], d = [0, 0, 0, 0] -> 不行!它是统一的,但不是格言。
Opt 3 -> [0, 2, 4, 1, 3], d = [1, 1, 2, 1] -> 也许不错的选择,我不知道是不是最好的...

有一些algorithm/procedure/idea 可以做到这一点?我必须用Java来做,也许存在一些 构建方法来做到这一点,但我没有找到它......

【问题讨论】:

  • 我认为这里的主要问题是理解问题。你能改写你的意思吗?你能告诉我们这个数组的最佳值是多少(如果它太复杂而无法解决,也许可以尝试一个更简单的数组)?
  • 事实上在这个例子中第三个选项是最好的。我的意思是它不是唯一的,d = [1,2,1,1] 是另一个......唯一性不是问题。对不起“也许不错的选择”,实际上是最好的选择。
  • @Amanda:所以我同时明白了。虽然我不明白你为什么一直减去1。x和x之间的距离是否等于-1???
  • @Amanda:虽然你现在可能有一个解决方案,但我还有一个问题:是先最大化距离然后处理均匀性,还是反过来?换句话说,哪个更好:|d| = 10, u = 3|d| = 12, u = 7? (因此:|d| 是距离向量的长度,u 是距离向量的方差)。还是您有一些公式可以使这对可测量?
  • 到目前为止,我一直在更多地从理论上思考这个问题。我还没有证明这一点,但对我来说,如果对向量进行排序,|d| 将是最小的。现在,如果您取向量中的最大值并将其放在新向量的中间。然后你取两个最小值并将它们分别放在第一个值的一侧。接下来,您取两个最高值,然后再取最低值。等等。那应该给出一个相当大的|d|。它甚至可能是绝对最大值。但差异将是可怕的。没有权衡取舍的规则,我现在很困。

标签: java arrays algorithm permutation distance


【解决方案1】:

为了能够发布我的小测​​试程序,我现在发布一个答案。

import java.util.*;

class x {
        static final int testseries[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };

        public static void main(String argv[])
        {
                Vector orig = new Vector();
                for (int i = 0; i < testseries.length; ++i) orig.add(new Integer(testseries[i]));

                Vector dist = getD(orig);
                System.out.println("d min = " + getAbsD(dist) + "\tUniformity = " + getUniformity(dist));
                printVector(orig);
                printVector(dist);
                System.out.println();

                Vector v = reorder1(orig);
                dist = getD(v);
                System.out.println("d     = " + getAbsD(dist) + "\tUniformity = " + getUniformity(dist));
                printVector(v);
                printVector(dist);
                System.out.println();

                v = reorder2(orig);
                dist = getD(v);
                System.out.println("d     = " + getAbsD(dist) + "\tUniformity = " + getUniformity(dist));
                printVector(v);
                printVector(dist);
                System.out.println();

                return;
        }

        //
        // This method constructs the Distance Vector from the input
        // 
        public static Vector getD(Vector orig)
        {
                Vector v = new Vector();
                for (int i = 0; i < orig.size() - 1; ++i) {
                        int a = ((Integer) orig.get(i)).intValue();
                        int b = ((Integer) orig.get(i + 1)).intValue();
                        v.add(new Integer(Math.abs(a - b)));
                }
                return v;
        }

        public static double getAbsD(Vector orig)
        {
                double d = 0;
                Vector v = getD(orig);

                for (int i = 0; i < v.size(); ++i) {
                        int a = ((Integer) v.get(i)).intValue();
                        d += a * a;
                }
                return Math.sqrt(d);
        }

        public static double getUniformity(Vector dist)
        {
                double u = 0;
                double mean = 0;

                for (int i = 0; i < dist.size(); ++i) {
                        mean += ((Integer) dist.get(i)).intValue();
                }
                mean /= dist.size();

                for (int i = 0; i < dist.size(); ++i) {
                        int a = ((Integer) dist.get(i)).intValue();
                        u += (a - mean) * (a - mean);
                }

                return u / dist.size();
        }

        //
        // This method reorders the input vector to maximize the distance
        // It is assumed that the input is sorted (direction doesn't matter)
        //
        // Note: this is only the basic idea of the distribution algorithm
        //       in this form it only works if (n - 1) mod 4 == 0
        //
        public static Vector reorder1(Vector orig)
        {
                Integer varr[] = new Integer[orig.size()];

                int t, b, lp, rp;
                t = orig.size() - 1;
                b = 0;
                lp = t / 2 - 1;
                rp = t / 2 + 1;
                varr[t/2] = (Integer) orig.get(t); t--;
                while (b < t) {
                        varr[lp] = (Integer) orig.get(b); b++;
                        varr[rp] = (Integer) orig.get(b); b++;
                        lp--; rp++;
                        varr[lp] = (Integer) orig.get(t); t--;
                        varr[rp] = (Integer) orig.get(t); t--;
                        lp--; rp++;
                }

                Vector result = new Vector();
                for (int i = 0; i < orig.size(); ++i) result.add(varr[i]);

                return result;
        }

        //
        // This method reorders the input vector to maximize the distance
        // It is assumed that the input is sorted (direction doesn't matter)
        //
        // Note: this is only the basic idea of the distribution algorithm
        //       in this form it only works if (n - 1) mod 4 == 0
        //
        public static Vector reorder2(Vector orig)
        {
                Integer varr[] = new Integer[orig.size()];

                int t, b, lp, rp;
                t = orig.size() - 1;
                b = orig.size() / 2 - 1;
                lp = t / 2 - 1;
                rp = t / 2 + 1;
                varr[t/2] = (Integer) orig.get(t); t--;
                while (b > 0) {
                        varr[lp] = (Integer) orig.get(b); b--;
                        varr[rp] = (Integer) orig.get(b); b--;
                        lp--; rp++;
                        varr[lp] = (Integer) orig.get(t); t--;
                        varr[rp] = (Integer) orig.get(t); t--;
                        lp--; rp++;
                }

                Vector result = new Vector();
                for (int i = 0; i < orig.size(); ++i) result.add(varr[i]);

                return result;
        }

        //
        // to make everything better visible
        //
        public static void printVector(Vector v)
        {
                String sep = "";
                System.out.print("{");
                for (int i = 0; i < v.size(); ++i) {
                        System.out.print(sep + v.get(i));
                        sep = ", ";
                }
                System.out.println("}");
        }
}

由于算法的复杂度为O(n)(n 是向量大小),这也适用于(非常)大的集合。 (如果必须先对输入进行排序,复杂度为n log(n))。

正如这个小程序所证明的那样,我最初的想法(reorder1)不会给出关于距离的最佳结果。所以reorder2() 将是我选择的算法。 (它看起来简单、快速并且可以提供可接受的结果)。

使用的测试值是我最喜欢的一些数字。还有一些 ;-)

【讨论】:

  • 谢谢@Ronald,reorder2 是一个伟大而简单的解决方案。 (......是的,还有无限多的存在:))
  • reorder1reorder2 存在一些问题,其中最后一个元素没有插入,并且在尝试处理它时会在 getD 中抛出一个 NullPointerException
  • 这就是为什么我在评论中写道这只是算法的想法,仅适用于特定的n ((n - 1) mod 4 == 0)。所以它更像是概念证明而不是生产证明。但是为其他n 实现类似的算法应该相对容易。 (n 甚至,(n - 1) mod 4 == 2)。无论如何,打印的程序在我的环境中工作。
【解决方案2】:

如果我正确理解了这个问题,您希望创建可能的最大和最均匀的距离数组。

蛮力

不幸的是,我认为这个问题是 NP 难的,这意味着如果它绝对必须是最佳解决方案,那么最好循环遍历数组的所有可能排列并选择最好的。如果你有一个相对较小的数组,这实际上可能是你最好的选择。

使用蛮力寻找最佳解决方案的伪代码类似于:

var max = MIN;
for each permutation of array 
   var score = getScore(permutation)
   if(score > max) 
      max = score;
end

getScore 表示您如何确定构成“最佳数组”的内容。我看到在您提供的最佳解决方案中,在其他距离值 1 中有一个“2”,这意味着您可以容忍答案不一致的解决方案。我的建议是将所有距离相加并减去每个距离的惩罚,这与最常见的距离不同。你减去多少将决定它们是否一致的重要性(进行一些试验和错误以了解最有效的方法)。

遗传算法

如果您想要一个非常好的解决方案,但不一定是最好的,那么您应该考虑使用genetic algorithms

如果您是 Java 新手,我深表歉意!如果您是 Java 新手,这绝对不是最好的开始。

遗传算法背后的想法是创建一组列表排序(可能是索引列表)。它不必包含所有可能的组合,只需大约 50 个左右。随着算法的每一轮,您评估每个解决方案的“分数”(相当于上面提到的getScore)。然后,50/2 次,您随机选择两个具有加权概率的解决方案,支持得分较高的解决方案,并从两个父解决方案创建两个新解决方案。然后你有一个新的人口,然后你可以执行另一个回合,依此类推。

如果您继续这样下去,通常会出现这样一种趋势,即您会看到总体得分有所提高,如果做得好,这些解决方案也会得到改善。考虑始终直接包含每轮中得分最高的解决方案,否则您可能会在每轮中失去最佳解决方案。

模拟退火

Simulated annealing 是稍微修改解决方案以改进或恶化解决方案的过程。如果情况恶化,那么您保留之前的解决方案。如果情况有所改善,您将保留新的解决方案。无论哪种情况,您都将继续修改解决方案,直到对解决方案的任何更改都不会带来更好的解决方案。这是一个非常简单的算法,但可以保证至少找到一个局部最大值。

在您的情况下,您将进行的更改将是列表排序。说而不是使用列表排序 0,1,2,3,你尝试 0,2,1,3,你会发现它的分数更好。您保留 0,2,1,3,然后尝试修改其他内容。

希望对您有所帮助!

【讨论】:

  • 谢谢@Neil,我必须在大学编写一些遗传算法适应度,这不是我的问题的解决方案,我的意思是,它是解决方案,但我只想弄乱一个列表,不值得它! :D 蛮力根本不是选项,弄乱列表只是一个必须快速的大过程的一小部分……也许这不是最好的办法。
  • @AmandaGarci 我扩展了我的答案,包括模拟退火。请看一下。这可能不是最好的解决方案,但这是一种快速的方法。
  • 我相信你的意思是 NP-complete 或 NP-hard。 NP 只是意味着在多项式时间内可验证。
  • @Dukeling 你说得对,我的意思是 NP-hard。我认为不可能验证一个解决方案在多项式时间内是最优的。进行了适当的调整。
  • 谢谢@Neil,模拟退火可能是最好的解决方案。
【解决方案3】:

恕我直言,如果向量的维度 n 是奇数,问题就很容易了。然后 d = (n -1)/2 是与 n 的素数,您只需要构建一个星形多边形 (d, n)(参见维基百科上的星形多边形或星形)。同样的事情是添加距离d(模n)。如果维度是偶数并且如果 d = n/2 - 1 是与 n 的素数,则相同的方法。如果没有更多的并发症。但我承认这是循环问题的解决方案(其中最后一个元素和第一个元素之间的距离也被考虑在内)。 示例:对于 1 à 9,距离 4,我们得到:1,5,9,4*,8,3*,7,2*,5 (* 4 = 13 (模 9),id 为其他 *)。 距离是恒定的并且是最大的(在圆形的角度),

brg

【讨论】:

    猜你喜欢
    • 2021-12-18
    • 2021-03-09
    • 1970-01-01
    • 2016-08-13
    • 2020-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多