Cho hai số nguyên A và B đã cho, tìm một cặp số X và Y sao cho A = X * Y và B = X xor Y


22

Tôi đang vật lộn với vấn đề này tôi đã tìm thấy trong một cuốn sách lập trình cạnh tranh, nhưng không có giải pháp làm thế nào để làm điều đó.

Với hai số nguyên AB đã cho (có thể khớp với loại số nguyên 64 bit), trong đó A là số lẻ, hãy tìm một cặp số X và Y sao cho A = X * Y và B = X xor Y. Cách tiếp cận của tôi là liệt kê tất cả các ước của A và thử ghép nối số dưới sqrt (A) với số lượng hơn sqrt (A) mà nhân lên đến A và xem nếu xor của họ là tương đương với B . Nhưng tôi không biết nếu nó đủ hiệu quả. Điều gì sẽ là một giải pháp / thuật toán tốt cho vấn đề này?


1
Thật kỳ lạ khi trộn một toán tử nguyên và toán tử bitwise. Là nó thực sự X*Yhay X&Y?
Eric Duminil

Đó là sự nhân lên. (*)
Aster W.

Bạn đã viết bất kỳ dòng mã nào để giải quyết nhiệm vụ này chưa? Bạn định sử dụng ngôn ngữ lập trình nào?
Lynx 242

Câu trả lời:


5

Đây là một đệ quy đơn giản quan sát các quy tắc mà chúng ta biết: (1) các bit có ý nghĩa nhỏ nhất của cả X và Y được đặt do chỉ các bội số lẻ mang lại bội số lẻ; (2) nếu chúng ta đặt X có bit đặt B cao nhất, Y không thể lớn hơn sqrt (A); và (3) đặt bit theo X hoặc Y theo bit hiện tại trong B.

Mã Python sau đây cho kết quả dưới 300 lần lặp cho tất cả trừ một trong các cặp ngẫu nhiên tôi đã chọn từ mã ví dụ của Matt Timmermans . Nhưng cái đầu tiên đã mất tới 231.199 lần lặp :)

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 ""

Đầu ra:

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

Điều này không hoạt động khi B <sqrt (A), ví dụ: khi X == Y
Matt Timmermans

X == Y chỉ là ví dụ đơn giản nhất. B có thể là bất kỳ số nào <sqrt (A), như X = 0x30000001, Y = 0x30000007, A = X * Y, B = 6
Matt Timmermans

@MattTimmermans bắt tuyệt vời. Tôi đã thêm xử lý và ví dụ của bạn vào các bài kiểm tra, giải quyết trong 59 lần lặp. Xin vui lòng cho tôi biết nếu bạn tìm thấy các vấn đề khác (hoặc nếu vấn đề này dường như chưa được giải quyết).
גלעד ברקן

Hấp dẫn. Tôi đã mong đợi rằng một cái sẽ đắt tiền khi bạn làm cho nó hoạt động. Chúng tôi biết rằng có những trường hợp đắt tiền từ chiếc 231199, nhưng nó rất khó để mô tả chúng. Dù sao thì có vẻ như điều này hoạt động tốt bây giờ.
Matt Timmermans

9

Bạn biết rằng ít nhất một yếu tố là <= sqrt (A). Hãy tạo một X.

Độ dài của X tính bằng bit sẽ bằng khoảng một nửa chiều dài của A.

Do đó, các bit trên của X - những bit có giá trị cao hơn sqrt (A) - đều bằng 0 và các bit tương ứng trong B phải có cùng giá trị với các bit tương ứng trong Y.

Biết các bit trên của Y cung cấp cho bạn một phạm vi khá nhỏ cho hệ số tương ứng X = A / Y. Tính Xmin và Xmax tương ứng với các giá trị lớn nhất và nhỏ nhất có thể có cho Y, tương ứng. Hãy nhớ rằng Xmax cũng phải là <= sqrt (A).

Sau đó, chỉ cần thử tất cả các X có thể giữa Xmin và Xmax. Sẽ không có quá nhiều, vì vậy sẽ không mất nhiều thời gian.


Giải pháp tốt đẹp! Có ràng buộc về việc có bao nhiêu X như vậy tồn tại?
ciamej

đó là tối đa sqrt (A) / 2 trong trường hợp các bit trên của Y đều bằng 0. Tuy nhiên, ít hơn trong số chúng sẽ là ước số. Nếu bạn lo lắng về điều đó, bạn có thể giảm số lượng để kiểm tra bằng cách tìm các ước số bằng phương pháp nhân tố hóa của Fermat: en.wikipedia.org/wiki/Fermat%27s_factorization_method
Matt Timmermans

1
Đây là một cái nhìn sâu sắc tốt (+1), nhưng nếu chúng ta đang nói về số nguyên 64 bit, thì sqrt (A) / 2 có thể hơn một tỷ. Có vẻ như điều đó vẫn còn quá chậm đối với một tình huống "lập trình cạnh tranh" điển hình. (Tuyên bố miễn trừ trách nhiệm: Tôi chưa bao giờ thực hiện một cuộc thi lập trình, có lẽ tôi đã sai về điều này.) Có lẽ có một cái nhìn sâu sắc hơn nữa có thể được kết hợp với điều này bằng cách nào đó?
ruakh

2
Nếu bạn sử dụng phương pháp của fermat để tìm các ước số có thể có trong phạm vi, tôi nghĩ rằng nó giảm xuống sqrt (sqrt (A)), điều này chắc chắn là ổn
Matt Timmermans

6

Một cách đơn giản khác để giải quyết vấn đề này phụ thuộc vào thực tế là n bit thấp hơn của XY và X xor Y chỉ phụ thuộc vào n bit thấp hơn của X và Y. Do đó, bạn có thể sử dụng các câu trả lời có thể cho các bit n thấp hơn để hạn chế các câu trả lời có thể có cho các bit n + 1 thấp hơn cho đến khi bạn hoàn thành.

Thật không may, tôi đã làm việc rằng, thật không may, có thể có nhiều hơn một khả năng cho một n . Tôi không biết mức độ thường xuyên sẽ có rất nhiều khả năng, nhưng có lẽ nó không quá thường xuyên, vì vậy điều này có thể tốt trong bối cảnh cạnh tranh. Về mặt xác suất, sẽ chỉ có một vài khả năng, vì một giải pháp cho n bit sẽ cung cấp 0 hoặc hai giải pháp cho n + 1 bit, với xác suất bằng nhau.

Nó dường như làm việc khá tốt cho đầu vào ngẫu nhiên. Đây là mã tôi đã sử dụng để kiểm tra nó:

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);
    }
}

Bạn có thể xem kết quả tại đây: https://ideone.com/cEuHkQ

Hình như nó thường chỉ mất vài nghìn séc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.