您的位置:首页 > 其它

多层for循环的优化

2012-12-13 23:52 986 查看
简单的描述一下问题:
有7组数字,每组选取一个数字,求7个数字和在2500到2510之间的组合。

由于这7组数据个数比较多,当时我弱弱的写了7层的for循环,运行的时候感觉要算几个月。看来这程序必须优化一下才能跑。单纯的枚举法太耗时间了。

没有优化前的程序片段。
for (int s1 = 0; s1 < juriList.size(); s1++) {
for (int s2 = 0; s2 < chineseList.size(); s2++) {
for (int s3 = 0; s3 < nursList.size(); s3++) {
for (int s4 = 0; s4 < accountList.size(); s4++) {
for (int s5 = 0; s5 < mathList.size(); s5++) {
for (int s6 = 0; s6 < civilList.size(); s6++) {
for (int s7 = 0; s7 < englishList.size(); s7++) {
sum = s1 + s2 + s3 + s4 + s5 + s6 + s7;
if (sum <= max && sum >= min) {
.......
}

}
}
}
}
}
}
}
}


看着7层for,感觉好吓人。。。。
对于最后的结果,只有满足 和 在2500到2510 之间的数据才输出。
大部分的结果都是没有用的,这样如果能在每一层for循环开始时,对for循环的开始和结束做出动态的调整(注:数据存在List中,已经按大小排过序),这样就可以缩小遍历的次数。
下面的程序是改进的方案。

for (int s1 = 0; s1 < juriList.size(); s1++) {
minChine = min - juriNum.get(s1);
i1 = getIndex(minChine, lists, 1);
s2b = 0;
if (i1[0] == 1) {
s2b = i1[1];
}
sum += juriNum.get(s1);
if (sum > max) {
sum -= juriNum.get(s1);
continue;
}
for (int s2 = s2b; s2 < chineseList.size(); s2++) {
minNurs = minChine - chineseNum.get(s2);
i2 = getIndex(minNurs, lists, 2);
s3b = 0;
if (i2[0] == 2) {
s3b = i2[1];
}
sum += chineseNum.get(s2);
if (sum > max) {
sum -= chineseNum.get(s2);
continue;
}
for (int s3 = s3b; s3 < nursList.size(); s3++) {
minAcc = minNurs - nursNum.get(s3);
i3 = getIndex(minAcc, lists, 3);
s4b = 0;
if (i3[0] == 3) {
s4b = i3[1];
}
sum += nursNum.get(s3);
if (sum > max) {
sum -= nursNum.get(s3);
continue;
}
for (int s4 = s4b; s4 < accountList.size(); s4++) {
minMath = minAcc - accountNum.get(s4);
i4 = getIndex(minMath, lists, 4);
s5b = 0;
if (i4[0] == 4) {
s5b = i4[1];
}
sum += accountNum.get(s4);
if (sum > max) {
sum -= accountNum.get(s4);
continue;
}
for (int s5 = s5b; s5 < mathList.size(); s5++) {
minCivil = minMath - mathNum.get(s5);
i5 = getIndex(minCivil, lists, 5);
s6b = 0;
if (i5[0] == 5) {
s6b = i5[1];
}
sum += mathNum.get(s5);
if (sum > max) {
sum -= mathNum.get(s5);
continue;
}
for (int s6 = s6b; s6 < civilList.size(); s6++) {
minEng = minCivil - civilNum.get(s6);
i6 = getIndex(minEng, lists, 6);
s7b = 0;
if (i6[0] == 6) {
s7b = i6[1];
}
sum += civilNum.get(s6);
if (sum > max) {
sum -= civilNum.get(s6);
continue;
}
for (int s7 = s7b; s7 < englishList.size(); s7++) {
sum += englishNum.get(s7);

if (sum <= max && sum >= min) {
......
}

}
sum -= englishNum.get(s7);
}
sum -= civilNum.get(s6);
}
sum -= mathNum.get(s5);
}
sum -= accountNum.get(s4);
}
sum -= nursNum.get(s3);
}
sum -= chineseNum.get(s2);
}
sum -= juriNum.get(s1);
}
private static int[] getIndex(int minValue, List<List<Integer>> lists, int index) {
// 得到 结果 最接近最小值的 元素索引
int total = 0;
int i = 0;
int indexNow = 6;
for (int j = lists.size() - 1; j >= index; j--) {
int nowNum = 0;
for (int z = 0; z < lists.get(j).size(); z++) {
nowNum = lists.get(j).get(z);
total += nowNum;
if (total >= minValue - (7-index)) {
i = z;
indexNow = j;
break;
} else {
total -= nowNum;
}
if (z == lists.get(j).size() - 1) {
total += lists.get(j).get(z);
}
}
}
int[] result = { indexNow, i };
return result;
}


在第一层for循环开始时,会从第一个值遍历所有值,这时我们重新计算一下最小值,即 2500-s1的值 ,
这个时候,我有一个想法
从最里层的for循环开始取最大值,这样从最里层开始都会得到最大的值,其和也最大,当结果接近 2500 - s1 这个值时,再外一层的for循环就不需要去最大值了,而是取数组中间的某一个值,我们返回数组的顺序及 下标。

我可能不能很好的表达清楚,下面借助字母再解释一遍:

我们有7个数组,分别是 s1, s2, s3, s4, s5, s6, s7
在开始循环的时候,s1取第一个元素,即s1的最小值。
这个时候我们想让 从 s7开始都取最大值,这样可以接近 min , 而里层循环的小的值就不用遍历了。
我们假设有这种情况: min - s1[0]-s7max - s6max - s5max - s4[57] ∞7 我们返回 4 和 57 代表s7、s6、s5都取最大值 s4取第57的值。
这时第2层的for循环从第一个元素开始,第3层也是第一个,第4层从第57个开始,第5,6,7层都只取最后一个。
这样我们就把7个数加起来的和小于 min 的值尽可能都过滤掉了。

然后我们再优化一下 值大于max的部分。
对于这部分我的做法是把和已经大于max的continue了。
每深入一层for循环的时候,判断一下目前的和看是否超过max了,如果超过continue。在退出for循环的时候不要忘记把值减掉。

就这么多。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: