是否可以设计递归来控制输出结果?

时间:2012-03-10 05:11:01

标签: java algorithm recursion

更新:我的麻烦在于从概念上理解如何实现它。例如(不像我下面的解决方案那样精确),我计算出列表中每个值的最大范围偏差,也许是5,3,2,它将是4-6,2-4,1-3..I无法弄清楚如何处理该范围,所以我的递归只会进行。我可以使用嵌套for循环执行此操作,但递归有点棘手。

我的代码有两部分正常工作。一个生成值(递归)和一个返回分数(我只关心超过某个阈值的解决方案)。两者都有效(见下面的代码)。问题在于集成它们,因为我意识到我不能简单地引用控制它的方法,因为递归仍会产生许多结果。我想我需要改变我的递归代码以某种方式结合我的类似方法的逻辑(返回分数的那个)。

递归方法采用一个列表和一个整数,并尝试找出所有唯一的方式,求和中的值可以乘以等于总和(如果你发送一个值列表,5,3,2和一个目标总和为100。公式计算为5x + 3y + 2z = 100,求解x,y和z的所有可能值。在下面的示例代码中就是这种情况,如果你运行它你会得到完整的结果集)。我的问题是我不需要大多数结果只有符合某些特征的结果。我创建了一个方法(并计划创建更多)以限制结果,但我不确定如何设计递归方法,这将允许我节省时间而不是计算我不需要的结果。

这是基于一小部分结果的示例输出。目前,我得到所有结果然后我过滤并删除特定结果,但这意味着我首先必须创建一个非常大的数据集(大多数我不需要)。这是一个可能的输出(不是完整的输出,因为有太多的结果,我手动这样做):

Initial list of values [5, 3, 2]
initial list of quantities: [6.0, 8.0, 23.0]. 

// (5*6)+(3*8)+(2*23)=100 as do all examples below but I only want to include 
//the ones with scores above 90(see similar method in my code to see how I get this score)  

[0.0, 0.0, 50.0] // score = 0.7120763990222406 < -- should not be included
[0.0, 2.0, 47.0] // score = 0.7454415587728428 < -- should not be included
[1.0, 11.0, 31.0] // score = 0.9010050506338834 < -- should be included
[1.0, 13.0, 28.0] // score = 0.9133974596215562 < -- should be included
[1.0, 29.0, 4.0] // score = 0.7124239231090319 < -- should not be included

我想找出一种避免生成不应包含的方法的方法。

希望这是有道理的。这是代码(第一种方法findVariables,使用list / sum生成结果,第二种方法similar是控制函数的一个例子,我不知道如何整合)。如果我没有正确解释它,我认为审查这两种方法将使我正在做的事情清楚。

import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry;

public class findVariables {

public static void findVariables(double[] constants, double sum, ArrayList<ArrayList<Integer>> ranges) {
    findVariables0(constants, sum, new double[constants.length], 0, ranges);
}

private static void findVariables0(double[] constants, double remaining, double[] variables, int n, ArrayList<ArrayList<Integer>> ranges) {
    //System.out.println();
    if(n == constants.length - 1) {
        // solution if the remaining is divisible by the last constant.
        if (remaining % constants[n] == 0) {
            variables[n] = remaining/constants[n];
            System.out.println(Arrays.toString(variables));
        }
    } else {
        for (int i = ranges.get(n).get(0), limit = (int) (remaining/constants[n]); i <= ranges.get(n).get(1); i++) {
            variables[n] = i;
            findVariables0(constants, remaining - i * constants[n], variables, n+1, ranges);
        }
    }
}

private static void similar(HashMap<String, Integer> list1, HashMap<String, Integer> list2) {

    //TODO: This is currently Euclidean Distance, change it to pearson score as it protects from grade inflation, same logic
    //TODO: assess the logic here.  My logic is all the sums are the same, then I can get an accurate difference by simply studying the differences in values they in common
    System.out.println("hello from simlair method. Hopefully I don't crash or worst..turn evil :-)");
    double runsum = 0.0;
    List<String> keys_in_common = new ArrayList<String>();
    for (Entry<String, Integer> entry : list1.entrySet())
    {
        String key = entry.getKey();
        if (list2.containsKey(key)) {
            keys_in_common.add(key);
        }
    }

    Iterator it=keys_in_common.iterator();

    while(it.hasNext())
    {
      String value=(String)it.next();

      //System.out.println("Value :"+value);
      runsum += Math.pow((list1.get(value) - list2.get(value)),2);
      //System.out.println(runsum);
    }
    double score = Math.pow(runsum, .5);
    double score_percent = (100-score)*.01;
    System.out.println(score_percent);


}

public static void main(String... args) {
    HashMap<String, Integer> list1 = new HashMap<String, Integer>();
    HashMap<String, Integer> list2 = new HashMap<String, Integer>();
    list1.put("a", 5);
    list1.put("b", 3);
    list1.put("c", 2);

    list2.put("a", 3);
    list2.put("b", 3);
    list2.put("c", 2);

    //Trying to capture the range around [6.0, 8.0, 23.0] so creating a list of list of values to keep within the range
    ArrayList<ArrayList<Integer>> listOlists = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> singleList1 = new ArrayList<Integer>();
    singleList1.add(4);
    singleList1.add(8);
    listOlists.add(singleList1);
    ArrayList<Integer> singleList2 = new ArrayList<Integer>();
    singleList2.add(6);
    singleList2.add(10);
    listOlists.add(singleList2);
    ArrayList<Integer> singleList3 = new ArrayList<Integer>();
    singleList3.add(20);
    singleList3.add(25);
    listOlists.add(singleList3);

    System.out.println(listOlists);

    similar(list1, list2);
    findVariables(new double[]{5, 3, 2}, 100, listOlists);

}

}

我的最终目标是拥有几千个大额的变量,并使用各种类型的方法来控制结果过大。

谢谢!另外,如上所述,我对java非常新,我确信我犯了错误。我欢迎有关如何改进的提示和建议。你可以看到我想要的输出和我当前的代码,但我很乐意改变我的整个方法,如果更好,所以不要觉得你的建议需要在我现有代码的上下文中。

2 个答案:

答案 0 :(得分:2)

您似乎要求的是一种修剪搜索树的整个部分的方法。

如果你认为三联体是3空间中的点,在原点处有目标三联体(你计算距离的那个),你的距离是&#34;标准定义了一些半径的球体。您可以根据点在球体外部的一个或两个坐标修剪树中您可以确定的任何部分。

假设球体半径(最大距离)为10,则任何具有任何坐标&gt; 10的点都必须在球体外部,并且可以修剪递归搜索树的整个分支。对于两个坐标也是如此,其中sqrt(x ^ 2 + y ^ 2)> 10。

请注意,这适用于欧几里德距离。如果您更改距离算法,则必须进行调整,并且根据3空间中距离边界的形状,此方法可能有效,也可能无效。

答案 1 :(得分:2)

修改

离开我们的评论,这是一个想法:

将一个对列表添加到findVariables0函数中,作为一个参数,其范围为两个值 - 例如

(4,6)

表示4-6的范围 - 让我们调用列表“范围”。在循环中

        for (int i = 0, limit = (int) (remaining/constants[n]); i <= limit; i++) {
            variables[n] = i;
            findVariables0(constants, remaining - i * constants[n], variables, n+1);
        }

设置

i = ranges[n].getFirst();" // should be (4)

并添加条件值

&& i < ranges[n].getSecond(); i++) // should be 6

这应该将递归限制在您想要的范围内。

结束修改

据我所知,您现在在代码中说的是:

  • 获得具有n个变量的多项式的所有可能解;
  • 从这组解决方案中,选择几何上接近我提供的点的解决方案。

你想在第一句话中加入第二句话,说:

  • 看看我提供的一点,并获得接近该点的多项式的解。

如果没有,那我就误解了。

如果我对你这么做,那么:至少,在你当前的代码中 - 无论如何你将会运行所有的解决方案,你可能想看看而不是生成靠近你的HashMaps列表建议使用HashMap,然后只检查所有这些。根据你对“关闭”的定义,这可能会削减很多额外的可能性,如果(如果我记得我的数学正确的话)一个3多项式解,总和为100,有100 ^ 3种不同的可能性,90%的100 ^ 3很多 - 尽管你在当前代码中使用“剩余/常量[n]”行切除了很多它们。

可悲的是,我对数学方面的帮助不足以帮助你使用精确的算法,但我希望我能让你走上一条路。请记住,您可能无法通过其他优化来完成所有优化,因此您可能不得不在某些优化之间进行选择而不是其他优化。

值得一提的是,我还有其他一些希望可以提供帮助的说明:

除了你“得分”的地方之外,我真的不认为有必要把所有东西都变成双倍 - 有一次你做分裂,你把它变成了一个Int,所以最好把它们变成所有的整数,我认为 - 节省记忆等等。

为什么数组传递给“类似”的HashMaps?你基本上像数组一样遍历它们,你总是会比较相同大小的列表(对吧?我可能会误解),所以没有必要经历用密钥存储数据的额外麻烦,然后把它拉出来再次按顺序运行所有键。

更容易/更清楚地看到

runsum += Math.pow((list1[n] - list2.get[n]),2);

比所有的HashMapstuff - 它仍然是O1访问时间。我不确定在HashMaps中存储它们对你有什么用。

祝你好运!希望我帮助过。