简体   繁体   English

将python代码转换为java以计算简单连接图的数量的未知问题

[英]Unknown issue converting python code to java to calculate number of simple connected graphs

The goal of this code: Number of simple connected graphs with N labeled vertices and K unlabeled edges. 该代码的目标:具有N个标记顶点和K个未标记边的简单连接图的数量。

Note: This might be considered as a Code Review problem, but after repeated tries, I think that the python and java code have the same functionality. 注意:这可能被认为是代码审查问题,但是经过反复尝试,我认为python和java代码具有相同的功能。 I'm not sure if there is something wrong with the code, or something to do with language intricacies, or my error in overlooking something. 我不确定代码是否存在问题,还是与语言复杂性有关,还是我在忽略某些内容时出错。

This was for a Google Foobar challenge. 这是针对Google Foobar的挑战。 I completed it using the above method. 我用上面的方法完成了它。 I have posted links to all the source code, that tests all possible cases. 我已经发布了所有源代码的链接,这些链接可以测试所有可能的情况。

The first method works completely. 第一种方法完全有效。 Only issue - it makes O(NK) recursive calls and K is on average quadratic in N. [Full source] 唯一的问题-它使O(NK)递归调用,K是在N.平均二次[全文源]

A friend came up with an algorithm to do the same thing with a bottom-up approach. 一个朋友想出了一种算法,可以使用自下而上的方法来完成相同的任务。 The main functionalities: 主要功能:

def answerHelper(n,k):
    totalGraphs = 0

    for s in range(1,n):
        graphs = 0
        for t in range(0,k+1):
            graphs += answer(s, t) * answer(n - s, k - t)

        graphs = choose(n, s)*s*(n - s) * graphs
        totalGraphs+= graphs

    return totalGraphs/2

F = {}
def answer(n, k):
    if (n, k) in F:
        return F[n, k]

    N = n * (n - 1)/2

    if k is n - 1:
        return int(n ** (n-2))
    if k < n or k > N:
        return 0
    if k == N:
        return 1

    result = ((N - k + 1) * answer(n, k - 1) + answerHelper(n, k - 1)) / k
    F[n, k] = result
    return result

The python fails in 4 cases in comparison with the original working Java code [diffchecker] . 与原始Java代码[diffchecker]相比,python在4种情况下失败。 I presume this is because of some sort of overflow(?). 我认为这是由于某种溢出(?)。 [Full python source] [完整的python来源]

I am trying to convert this python code to Java. 我正在尝试将此python代码转换为Java。 This is what I have come up with. 这就是我想出的。

static Map<List<Integer>, String> resultMap = new HashMap<List<Integer>, String>();
public static String answer(int N, int K) {
    /* for the case where K > N-1 */
    // check if key is present in the map
    List<Integer> tuple = Arrays.asList(N, K);
    if( resultMap.containsKey(tuple) )
        return resultMap.get(tuple);

    // maximum number of edges in a simply 
    // connected undirected unweighted graph 
    // with n nodes = |N| * |N-1| / 2
    int maxEdges = N * (N-1) / 2;   

    /* for the case where K < N-1 or K > N(N-1)/2 */
    if(K < N-1 || K > maxEdges)
        return BigInteger.ZERO.toString();

    /* for the case where K = N-1 */
    // Cayley's formula applies [https://en.wikipedia.org/wiki/Cayley's_formula].
    // number of trees on n labeled vertices is n^{n-2}.
    if(K == N-1)
        return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();

    /* for the case where K = N(N-1)/2 */
    // if K is the maximum possible 
    // number of edges for the number of 
    // nodes, then there is only one way is 
    // to make a graph (connect each node
    // to all other nodes)
    if(K == maxEdges)
        return BigInteger.ONE.toString();

    // number of edges left from maxEdges if I take away K-1 edges
    BigInteger numWays = BigInteger.valueOf(maxEdges - K + 1);

    // number of graphs possible for each of the numWays edges for a graph that has 1 less edge
    BigInteger numGraphsWithOneLessEdge = new BigInteger( answer(N,  K-1) );

    // number of all possible subgraphs with K-1 edges
    BigInteger subGraphs = answerHelper(N, K-1);

    // numWays*numGraphsWithOneLessEdge + subGraphs
    BigInteger result = subGraphs.add(numWays.multiply(numGraphsWithOneLessEdge));

    // this contains repeats for each of the K edges
    result = result.divide(BigInteger.valueOf(K));

    // add to cache
    resultMap.put(Collections.unmodifiableList(Arrays.asList(N, K)), result.toString());
    return resultMap.get(tuple);
}

private static BigInteger answerHelper(int N, int K) {

    BigInteger totalGraphs = BigInteger.ZERO;

    for(int n = 1 ; n < N ; n++) {
        BigInteger graphs = BigInteger.ZERO;
        for(int k = 0 ; k <= K ; k++) {
            // number of graphs with n nodes and k edges
            BigInteger num = new BigInteger( answer(n, k) );

            // number of graphs with N-n nodes and K-k edges
            BigInteger num2 = new BigInteger( answer(N-n, K-k) );

            graphs = graphs.add( num.multiply(num2) );
        }

        // number of ways to choose n nodes from N nodes
        BigInteger choose = choose(N, n);

        // this is repeated for each of the n chosen nodes
        // and the N-n unchosen nodes
        choose = choose.multiply(BigInteger.valueOf(n)).multiply(BigInteger.valueOf(N-n));

        totalGraphs = totalGraphs.add( choose.multiply(graphs) );

    }

    // now, consider the case where N = 20
    // when n = 2, we compute for N-n = 18
    // when n = 18, we do the same thing again
    // hence, contents of totalGraphs is 2 times
    // of what it should be
    return totalGraphs.divide(BigInteger.valueOf(2));
}

[Full source] [全文]

This code, that I intended to function the same as Python, fails multiple cases with respect to the working java code [diffchecker] 此代码(我打算与Python发挥相同的功能)针对有效的Java代码[diffchecker]导致多种情况失败

I would be very grateful if I can get some guidance. 如果能得到一些指导,我将不胜感激。

The issue was in the Java code, not the Python code (I had suspected an overflow; some meticulous debugging proved otherwise. Its not the easiest comparing numbers with 20 odd digits). 问题出在Java代码,而不是Python代码(我怀疑是溢出;否则,经过了一些精心的调试,事实证明。这不是最简单的比较20位奇数的数字)。

The erroneous code: 错误代码:

/* for the case where K = N-1 */
// Cayley's formula applies [https://en.wikipedia.org/wiki/Cayley's_formula].
// number of trees on n labeled vertices is n^{n-2}.
if(K == N-1)
    return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();

For N>=17, (long)Math.pow(N, N-2) was not accurate. 对于N> = 17, (long)Math.pow(N, N-2)不准确。 This happened because with greater double values, the gap between consecutive values increases. 发生这种情况的原因是,使用更大的double值时,连续值之间的差距会增加。 A double can't represent every integer value within its range, and that's what's going wrong here. 双精度不能代表其范围内的每个整数值,这就是问题所在。 It's returning the closest double value to the exact result. 它会将最接近的double值返回到精确结果。 Moreover, for double values, the mantissa is 52 bits, which roughly equals 16(?) places of decimals. 此外,对于双精度值,尾数为52位,大约等于16(?)小数位。 Hence the overflowiness (not really a word). 因此,泛滥(不是一个字)。 So, the value being returned was smaller than it should have been. 因此,返回的值小于应该的值。 Had to replace this with the following code block. 不得不用以下代码块替换它。

if(K == N-1) {
    if(N < 2)
        return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();

    // multiply N to itself N-2 times
    BigInteger val = BigInteger.ONE;

    int count = 0;

    while(count++ != N-2)
        val = val.multiply( BigInteger.valueOf( (long)N ) );

    return val.toString();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM