【问题标题】:For given two integers A and B, find a pair of numbers X and Y such that A = X*Y and B = X xor Y对于给定的两个整数 A 和 B,找到一对数字 X 和 Y 使得 A = X*Y 和 B = X xor Y
【发布时间】:2019-11-23 17:53:10
【问题描述】:

我正在努力解决我在一本竞争性编程书中找到的这个问题,但没有解决方法。

对于给定的两个整数 AB(可以适合 64 位整数类型),其中 A 是奇数,找到一对数字 X 和 Y,使得 A = X*Y 和 B = X xor Y。 我的方法是列出 A 的所有除数,并尝试将 sqrt(A) 下的数字与 sqrt(A) 上乘以 A 的数字配对,看看它们的异或是否等于 B。但我不知道这是否足够有效。 什么是这个问题的好解决方案/算法?

【问题讨论】:

  • 将整数运算符和位运算符混合在一起很奇怪。真的是X*Y 还是X&Y
  • 是乘法。 ( * )
  • 您是否已经编写了任何代码行来解决此任务?您打算使用哪种编程语言?

标签: algorithm bit-manipulation


【解决方案1】:

你知道至少有一个因素是

X 的长度(以位为单位)大约是 A 长度的一半。

因此,X 的高位——比 sqrt(A) 高的位——都是 0,B 中的对应位必须与 Y 中的对应位具有相同的值。

知道 Y 的高位给你一个相当小的范围来对应因子 X = A/Y。分别计算对应于 Y 的最大和最小可能值的 Xmin 和 Xmax。请记住,Xmax 也必须是

然后尝试 Xmin 和 Xmax 之间所有可能的 X。不会太多,所以不会花很长时间。

【讨论】:

  • 在 Y 的高位全为 0 的情况下最多为 sqrt(A)/2。不过,它们中的除数较少。如果您对此感到担心,可以通过使用费马分解方法找到除数来减少要检查的数量:en.wikipedia.org/wiki/Fermat%27s_factorization_method
  • 这是一个很好的见解 (+1),但如果我们谈论的是 64 位整数,那么 sqrt(A)/2 可能超过十亿。对于典型的“竞争性编程”情况来说,这似乎仍然太慢了。 (免责声明:我从来没有参加过编程比赛,也许我错了。)也许有更深入的见解可以与这个结合起来?
  • 如果你使用fermat的方法来寻找范围内可能的除数,我认为它可以简化为sqrt(sqrt(A)),这当然可以
【解决方案2】:

解决这个问题的另一种直接方法是,XY 和 X xor Y 的低 n 位仅依赖于 X 和 X 的低 n 位。 Y. 因此,您可以使用较低 n 位的可能答案来限制较低 n+1 位的可能答案,直到您完成为止。

我发现,不幸的是,单个n 的可能性不止一种。我不知道多久会有很多种可能性,但如果有的话,可能不会太频繁,所以这在竞争环境中可能没问题。从概率上讲,只有少数可能性,因为 n 位的解决方案将为 n+1 位提供 0 个或两个解决方案,概率相等。

对于随机输入似乎效果很好。这是我用来测试它的代码:

public static void solve(long A, long B)
{
    List<Long> sols = new ArrayList<>();
    List<Long> prevSols = new ArrayList<>();
    sols.add(0L);
    long tests=0;
    System.out.print("Solving "+A+","+B+"... ");
    for (long bit=1; (A/bit)>=bit; bit<<=1)
    {
        tests += sols.size();
        {
            List<Long> t = prevSols;
            prevSols = sols;
            sols = t;
        }
        final long mask = bit|(bit-1);
        sols.clear();
        for (long prevx : prevSols)
        {
            long prevy = (prevx^B) & mask;
            if ((((prevx*prevy)^A)&mask) == 0)
            {
                sols.add(prevx);
            }
            long x = prevx | bit;
            long y = (x^B)&mask;
            if ((((x*y)^A)&mask) == 0)
            {
                sols.add(x);
            }
        }
    }
    tests += sols.size();
    {
        List<Long> t = prevSols;
        prevSols = sols;
        sols = t;
    }
    sols.clear();
    for (long testx: prevSols)
    {
        if (A/testx >= testx)
        {
            long testy = B^testx;
            if (testx * testy == A)
            {
                sols.add(testx);
            }
        }
    }

    System.out.println("" + tests + " checks -> X=" + sols);
}
public static void main(String[] args)
{
    Random rand = new Random();
    for (int range=Integer.MAX_VALUE; range > 32; range -= (range>>5))
    {
        long A = rand.nextLong() & Long.MAX_VALUE;
        long X = (rand.nextInt(range)) + 2L;
        X|=1;
        long Y = A/X;
        if (Y==0)
        {
            Y = rand.nextInt(65536);
        }
        Y|=1;
        solve(X*Y, X^Y);
    }
}

您可以在此处查看结果:https://ideone.com/cEuHkQ

看起来通常只需要几千张支票。

【讨论】:

    【解决方案3】:

    这是一个简单的递归,它遵守我们知道的规则:(1)X 和 Y 的最低有效位都被设置,因为只有奇数被乘数产生奇数倍数; (2) 如果我们设置 X 具有 B 的最高设置位,则 Y 不能大于 sqrt(A); (3) 根据 B 中的当前位设置 X 或 Y 中的位。

    对于我从 Matt Timmermans 的example code 中挑选的随机对,以下 Python 代码的迭代次数少于 300 次。但是第一个迭代了 231,199 次 :)

    from math import sqrt
    
    def f(A, B):
      i = 64
      while not ((1<<i) & B):
        i = i - 1
      X = 1 | (1 << i)
    
      sqrtA = int(sqrt(A))
    
      j = 64
      while not ((1<<j) & sqrtA):
        j = j - 1
    
      if (j > i):
        i = j + 1
    
      memo = {"it": 0, "stop": False, "solution": []}
    
      def g(b, x, y):
        memo["it"] = memo["it"] + 1
        if memo["stop"]:
          return []
    
        if y > sqrtA or y * x > A:
          return []
    
        if b == 0:
          if x * y == A:
            memo["solution"].append((x, y))
            memo["stop"] = True
            return [(x, y)]
          else:
            return []
    
        bit = 1 << b
    
        if B & bit:
          return g(b - 1, x, y | bit) + g(b - 1, x | bit, y)
        else:
          return g(b - 1, x | bit, y | bit) + g(b - 1, x, y)
    
      g(i - 1, X, 1)
      return memo
    
    vals = [
      (6872997084689100999, 2637233646), # 1048 checks with Matt's code
      (3461781732514363153, 262193934464), # 8756 checks with Matt's code
      (931590259044275343, 5343859294), # 4628 checks with Matt's code
      (2390503072583010999, 22219728382), # 5188 checks with Matt's code
      (412975927819062465, 9399702487040), # 8324 checks with Matt's code
      (9105477787064988985, 211755297373604352), # 3204 checks with Matt's code
      (4978113409908739575,67966612030), # 5232 checks with Matt's code
      (6175356111962773143,1264664368613886), # 3756 checks with Matt's code
      (648518352783802375, 6) # B smaller than sqrt(A)
    ]
    
    for A, B in vals:
      memo = f(A, B)
      [(x, y)] = memo["solution"]
      print "x, y: %s, %s" % (x, y)
      print "A:   %s" % A
      print "x*y: %s" % (x * y)
      print "B:   %s" % B
      print "x^y: %s" % (x ^ y)
      print "%s iterations" % memo["it"]
      print ""
    

    输出:

    x, y: 4251585939, 1616572541
    A:   6872997084689100999
    x*y: 6872997084689100999
    B:   2637233646
    x^y: 2637233646
    231199 iterations
    
    x, y: 262180735447, 13203799
    A:   3461781732514363153
    x*y: 3461781732514363153
    B:   262193934464
    x^y: 262193934464
    73 iterations
    
    x, y: 5171068311, 180154313
    A:   931590259044275343
    x*y: 931590259044275343
    B:   5343859294
    x^y: 5343859294
    257 iterations
    
    x, y: 22180179939, 107776541
    A:   2390503072583010999
    x*y: 2390503072583010999
    B:   22219728382
    x^y: 22219728382
    67 iterations
    
    x, y: 9399702465439, 43935
    A:   412975927819062465
    x*y: 412975927819062465
    B:   9399702487040
    x^y: 9399702487040
    85 iterations
    
    x, y: 211755297373604395, 43
    A:   9105477787064988985
    x*y: 9105477787064988985
    B:   211755297373604352
    x^y: 211755297373604352
    113 iterations
    
    x, y: 68039759325, 73164771
    A:   4978113409908739575
    x*y: 4978113409908739575
    B:   67966612030
    x^y: 67966612030
    69 iterations
    
    x, y: 1264664368618221, 4883
    A:   6175356111962773143
    x*y: 6175356111962773143
    B:   1264664368613886
    x^y: 1264664368613886
    99 iterations
    
    x, y: 805306375, 805306369
    A:   648518352783802375
    x*y: 648518352783802375
    B:   6
    x^y: 6
    59 iterations
    

    【讨论】:

    • 这在 B
    • X==Y 只是最简单的例子。 B 可以是任意数字
    • @MattTimmermans 很棒的收获。我在测试中添加了处理和您的示例,它在 59 次迭代中得到解决。如果您发现其他问题(或此问题似乎未解决),请告诉我。
    • 有趣。当你让它工作时,我原以为它会很贵。我们知道 231199 有一些昂贵的案例,但事实证明很难描述它们的特征。无论如何,现在看起来这工作正常。
    猜你喜欢
    • 2023-03-27
    • 2012-01-28
    • 2022-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多