假设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做,也许存在一些
建立的方法来做到这一点,但我找不到它......
答案 0 :(得分:1)
如果我正确理解了问题,您可能需要创建最大且最均匀的距离数组。
不幸的是我认为这个问题是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,并尝试修改其他内容。
我希望有所帮助!
答案 1 :(得分: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()
将是我选择的算法。 (这很简单,快速,并且可以提供可接受的结果)。
使用的测试值是我最喜欢的一些数字。还有一些;-)
答案 2 :(得分:0)
恕我直言,如果向量的维数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