【问题标题】:Optimal way to determine If it is possible to arrive at pair (c,d) when starting from (a,b)确定从 (a,b) 开始时是否可能到达对 (c,d) 的最佳方法
【发布时间】:2019-01-31 17:04:09
【问题描述】:

每一对 (a,b) 都可以转换为 (a+b, b) 或 (a, a+b)。

给定一个函数 bool isPossible(int a, int b, int c, int d) ,根据是否可以通过 (a,b) 到达 (c,d) 的目标对,实现返回 true 或 false 的逻辑

// (a,b) -> (a, a+b)
// (a,b) -> (a+b,b)
// 2,3 -> 2,5 | 5,3 -> 7,5 | 2, 7 | 5, 8 | 8, 3 -> 12, 5 | 7, 12 | 9, 7 | 2, 9 |5, 13|13,8 | 11, 3 | 8, 11
// a,b -> a,a+b | a+b, b -> a, 2a+b | 2a +b , a+b | a+b, a+2b | a+2b, b -> a, 3a + b | 3a + b , 2a +b |

更新:在此处添加我的解决方案。 寻找其他优雅的解决方案会很有趣。

bool isPossible(int a,int b,int c,int d) {

    while(c>=a && d>=b) {
        if(a == c && b==d ) {
            return true;
        }
        int temp = c;
        c= c>d?c-d:c;
        d= d>=temp?d-temp:d;
    }
    return false;
}

更新:这个解决方案似乎只适用于 a 和 b 的正值。

【问题讨论】:

  • 当我们执行 (a+b,b) 时,我们是否将 b 的初始值添加到 b 的更新值?
  • 在 (a,b) 变为 (a+b, b) 的情况下,'b' 保持不变,但 'a' 变为 'a+b'。在 (a, a+b) 的情况下发生反之亦然
  • 请注意,这个解决方案似乎是绝对错误的;它没有通过我能想到的所有测试:isPossible(1, 2, 1, 2); isPossible(1, 2, 3, 2); isPossible(1, 2, 3, 5); isPossible(-1, -2, -3, -2); all return false。虽然前三种情况可能很容易解决,但我想后一种情况要困难得多。
  • 你是对的。我也应该做负面测试。我已经更新了应该适用于前三种情况的解决方案。稍后会更新最后一个案例。
  • @AlokGarg 您的新解决方案仍然失败 (1,2,1,2) 案例

标签: algorithm data-structures


【解决方案1】:

您的解决方案是基于最好倒退的想法,即从 (c,d) 到 (a,b)。这是因为当你从 (a,b) 开始往前走时,下一对你有两种可能,而当你从 (c,d) 往后走时,你显然只有一种可能——你需要减去从最大值取最小值。

ab 都为正时,情况确实如此。在这种情况下,a+b>aa+b>b,因此给定一个 (c,d) 对,很容易确定转换链中的哪一步是最后一步(这取决于是 c>d 还是 d>c)。但是,如果ab 可以为负数,这种推理就会失效,因为在这种情况下,您不知道是否应该从d 中减去c,反之亦然。这是您的方法中的一个基本问题,我看不到解决它的简单方法。

如果我们只考虑正面ab 的情况,那么我们可以将您的解决方案修改为更有效的解决方案。

考虑c 远大于d 的情况。在这种情况下,您将多次从c 中减去d,直到新的c 变得小于d。你到底会到什么地步?显然是(c%d, d)。因此,您可以一步完成,得到与最大公约数的Euclidean algorithm 非常相似的代码。

但是,通过从减法到模除法的这种跳跃,您实际上可能已经跳过了所需的对(即 (a,b))。但是,这很容易解决,因为其中一个数字没有变化,因此我们可以轻松确定这种情况。

代码将如下所示(未测试)

while (c > 0 && d > 0) {  // similar to how Eucledian algorithm is written
    if (c > d) {
        int new_c = c % d;
        if (b == d) {  // we should have seen (a,b) here
            return (a % d == new_c && a >= new_c && a <= c);
        }
        c = new_c;
    } else {
        //  a symmetrical case follows
        ...
    }
} 

这段代码的时间复杂度为O(log(c + d)),而您的减法代码在O(c+d) 中有效。


至于a 和/或b 可以为负数的情况,这似乎是一个更困难的问题。

【讨论】:

    【解决方案2】:

    我还没有(数学)证明,但是这个函数:

    static boolean myPossible(int a, int b, int c, int d) {
      // parenthesis not needed, but for better human readability...
      return (c % a == b && d % b == a) || (c % b == a && d % a == b);
    }
    

    ...返回(总是)和你一样的结果!

    “数值/蛮力测试”:

    public class Test {
    
        private static final Random RAND = new Random();
    
        public static void main(String[] args) {
    
            for (int i = 0; i < 1000000; i++) {
                // temporarily set to 0..1 (corner case)
                int a = RAND.nextInt(1);
                // temporarily set to 0..1 (corner case)
                int b = RAND.nextInt(1);
                int c = RAND.nextInt(10000);
                int d = RAND.nextInt(10000);
                assert (isPossible(a, b, c, d) == myPossible(a, b, c, d));
            }
        }
    
        static boolean isPossible(int a, int b, int c, int d) {
            while (c >= a && d >= b) {
                c = c > d ? c - d : c;
                d = d >= c ? d - c : d;
                if (a == c && b == d) {
                    return true;
                }
            }
            return false;
        }
    
        static boolean myPossible(int a, int b, int c, int d) {
            return c % a == b && d % b == a || c % b == a && d % a == b;
        }
    }
    

    【讨论】:

    • a==1b==1 时,您的函数至少会失败。因为任何%1 等于零,它总是会返回false。这显然常常是错误的。我相信还有更多类似的案例。
    • 你是对的,@Petr(关于“运动目标”..这个功能还不正确/尚未完成)......但我仍然坚持,myPossible 给出的结果与isPossible(即使是(a,b) in ([0,1],[0,1]);)
    猜你喜欢
    • 2021-03-16
    • 2019-10-29
    • 1970-01-01
    • 2012-01-27
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多