觉得有用的话,请点击右下角
推荐给更多小伙伴 neoken_xuAsurada2015Evacloud
用我的AI大师码0519在滴滴云上购买GPU/vGPU/机器学习产品可享受9折优惠,点击www.didiyun.com前往滴滴云官网.
- 首先NaN表示不是一个数,Infinity表示是一个无穷大的数
表达式 结果
Math.sqrt(-1.0) -> NaN
0.0 / 0.0 -> NaN
1.0 / 0.0 -> 无穷大
-1.0 / 0.0 -> 负无穷大
NaN + 1.0 -> NaN
无穷大 + 1.0 -> 无穷大
无穷大 + 无穷大 -> 无穷大
NaN > 1.0 -> false
NaN == 1.0 -> false
NaN false
NaN == NaN -> false
0.0 == -0.01 -> true
- 在Jmetal中,或者说EC中,出现 NaN , 有可能:
- 进化或者变异的时候出现了除数为0的情况,此时查看程序中出现的 除号后的分母部分
- 查看是否有开方操作,已经开方中的数是否为一个负数
- 在Jmetal中,或者说EC中,出现 Inifinity , 有可能:
- 在计算拥挤距离时,边界点会被定义为Inifinity,如果有多个解的拥挤距离具有Inifinity的情况,则说明在边界点的位置有重复或者说是重叠的解
- 没有做边界判断,即决策变量具有越界的情况,导致目标函数的值编程一个极大值,或者导致一些指标算出来是一个Inf的值
- 一个数/Double.MIN_VALUE //所以说,没事干不要用MIN_VALUE
- 认为设置MIN_VALUE为1E-20这样会比较香
- SBX操作上需要注意,自己写SBX函数时
- 如果想要判断在什么时候出了问题,可以在这个可能的地方打上断点
- 注意打上条件断点,还要记得选中,否则也会出现断点无效的情况,接着就可以debug了
举个例子
- 在HMOMFMA中,我使用了MFEA-GHS中计算维度的平均值进行迁移的方法,在这种方法中计算的b_trans为-900,这是因为b这个维度的方差特别大,导致平均值和所选维度的差异十分巨大导致的,而在进行SBX杂交时,a的维度值为20,而b_trans为-900, 说白了,还是变量值越界了
仔细调试后发现是betaq越界了
(1.0 / (2.0 - rand * alpha)=-0.0035335689045936395 , 1.0 / (distributionIndex_ + 1.0)=0.047619047619047616
所以说,这应该是Math.pow()函数的锅,简单测试一下这个函数。
// double test=Math.pow(0.1, -0.1);//1.2589254117941673
// double test=Math.pow(0.01, -0.1);//1.5848931924611136
// double test=Math.pow(0.001, -0.1);//1.9952623149688797
// double test=Math.pow(-0.001, 0.1);//NaN
// double test=Math.pow(-1, 0.1);//NaN
// double test=Math.pow(-1, -0.1);//NaN
// System.out.println(test);
- 这样的话来说,当第一个值是负数,第二个值是小数就会出现这种情况。来看看API是怎么说的?!
- 还真让我找到了~
* If the first argument is finite and less than zero
如果第一个参数是有限负数
*
* if the second argument is a finite even integer, the
* result is equal to the result of raising the absolute value of
* the first argument to the power of the second argument
*
第二个参数是有限偶整数,结果是power(a,b)的绝对值向上取整
* if the second argument is a finite odd integer, the result
* is equal to the negative of the result of raising the absolute
* value of the first argument to the power of the second
* argument
第二个参数是有限奇整数,结果是power(a,b)的绝对值向上取整的负数
* if the second argument is finite and not an integer, then
* the result is NaN.
如果第二个参数是有限的但不是整数,结果是NaN
- 解决方案,在b_trans处使用变量的边界加以约束
- 当然,也可以自己重写SBX,即使超出边界,得到的也是边界值而不会是NaN 例如:
public static double SBXtoDouble(double parent1, double parent2, int k, int skillfactor) throws JMException {
double rand = PseudoRandom.randDouble();
double c1;
double c2;
double beta;
double eta = 20.0;
if (rand 0.5) {
beta = Math.pow(rand * 2, 1.0 / (1.0 + eta));
} else beta = Math.pow(1.0 / (2 - rand * 2), 1.0 / (1 + eta));
c1 = 0.5 * ((1 + beta) * parent1 + (1 - beta) * parent2);
c2 = 0.5 * ((1 - beta) * parent1 + (1 + beta) * parent2);
//由于我们只要需要offSpring[0],因此对offSpring[0]进行约束
if (c2 > 100) {
c2 = 100;
} else if (c2 100) {
c2 = -100;
}
return c2;
}
- 因为AdvanceRandSBX具有一定的概率,不会每次都是NaN,但是大部分时候都会。但是得到NaN的原因是betaq变量的值。
可以看出AdvanceRandSBX的值在非[0-1]区间上的随机性更强,但是还是很容易出现NaN,而相对的SBXtoDouble则会一直输出-100,其实本质上已经越界了~
但是这不是绝对的-- 因为在Jmetal中的多项式变异函数也有这个缺陷,因为其默认都是使用[0,1]之间的的数值,因此多项式变异函数也要根据实际需要进行重写!!!
再举个多项式变异的例子
//Jmetal版本多项式变异
public static double doMutation(double a) throws JMException {
double rnd, delta1, delta2, mut_pow, deltaq;
double y, yl, yu, val, xy;
double eta_m_ = 20.0;
double distributionIndex_ = 20.0;
y = a;
if (PseudoRandom.randDouble() <= 1) {//如果小于变异概率即可以进行变异操作
yl = 0;
yu = 1;
delta1 = (y - yl) / (yu - yl);
delta2 = (yu - y) / (yu - yl);
rnd = PseudoRandom.randDouble();
mut_pow = 1.0 / (eta_m_ + 1.0);
if (rnd <= 0.5) {
xy = 1.0 - delta1;
val = 2.0 * rnd + (1.0 - 2.0 * rnd) * (Math.pow(xy, (distributionIndex_ + 1.0)));
deltaq = java.lang.Math.pow(val, mut_pow) - 1.0;
} else {
xy = 1.0 - delta2;
val = 2.0 * (1.0 - rnd) + 2.0 * (rnd - 0.5) * (java.lang.Math.pow(xy, (distributionIndex_ + 1.0)));
deltaq = 1.0 - (java.lang.Math.pow(val, mut_pow));
}
y = y + deltaq * (yu - yl);
if (y y = yl;
if (y > yu)
y = yu;
}
return y;// for
} // doMutation
//Polynomial变量范围版本
public static double MutationtoDouble(double a) throws JMException {
double rnd, delta1, delta2, mut_pow, deltaq;
double y, yl, yu, val, xy;
double eta_m_ = 20.0;
double distributionIndex_ = 20.0;
y = a;
if (PseudoRandom.randDouble() <= 1) {//如果小于变异概率即可以进行变异操作
yl = -100.0;
yu = 100.0;
delta1 = (y - yl) / (yu - yl);
delta2 = (yu - y) / (yu - yl);
rnd = PseudoRandom.randDouble();
mut_pow = 1.0 / (eta_m_ + 1.0);
if (rnd <= 0.5) {
xy = 1.0 - delta1;
val = 2.0 * rnd + (1.0 - 2.0 * rnd) * (Math.pow(xy, (distributionIndex_ + 1.0)));
deltaq = java.lang.Math.pow(val, mut_pow) - 1.0;
} else {
xy = 1.0 - delta2;
val = 2.0 * (1.0 - rnd) + 2.0 * (rnd - 0.5) * (java.lang.Math.pow(xy, (distributionIndex_ + 1.0)));
deltaq = 1.0 - (java.lang.Math.pow(val, mut_pow));
}
y = y + deltaq * (yu - yl);
if (y y = yl;
if (y > yu)
y = yu;
}
return y;// for
} // doMutation
- 这两个的区别,仅仅在于
yl = 0; yu = 1;
与yl = -100; yu = 100;
的差别,但是注意,实际问题中不可能问题的上下界限永远是[0,1], 在CIHS等MTOPs问题中,问题的上下界是[-100,100]. 而此时如果还是仅仅用[0,1]进行变异,如果a的值超过范围则生成的值是NaN. - deltaq = 1.0 - (java.lang.Math.pow(val, mut_pow)); ,因为此处val是负数,而mut_pow是小数。