두 개의 정수 A와 B에 대해 A = X * Y와 B = X xor Y가되도록 숫자 X와 Y의 쌍을 찾으십시오.


22

경쟁 프로그래밍 책에서 찾은이 문제로 어려움을 겪고 있지만 해결 방법이 없습니다. A 가 홀수 인

두 개의 정수 A 와 B (64 비트 정수 유형에 적합)에 대해 A = X * Y와 B = X xor Y와 같은 숫자 X와 Y의 쌍을 찾으십시오 . A의 모든 제수와 sqrt (A) 아래의 숫자를 sqrt (A) 이상의 숫자와 짝을 이루어 A 까지 곱하고 xor가 B와 같은지 확인하십시오 . 그러나 그것이 충분히 효율적인지 모르겠습니다. 이 문제에 대한 좋은 해결책 / 알고리즘은 무엇입니까?


1
정수 연산자와 비트 연산자를 혼합하는 것이 이상합니다. 그것은 정말 X*Y나 X&Y?
— Eric Duminil

곱셈입니다. (*)
— Aster W.

이 작업을 해결하기 위해 이미 코드 줄을 작성 했습니까? 어떤 프로그래밍 언어를 사용 하시겠습니까?
— Lynx 242

답변:


5

우리가 알고있는 규칙을 관찰하는 간단한 재귀는 다음과 같습니다. (2) X를 B의 가장 높은 설정 비트로 설정하면 Y는 sqrt (A)보다 클 수 없습니다. (3) B의 현재 비트에 따라 X 또는 Y의 비트를 설정한다.

다음 Python 코드는 Matt Timmermans의 예제 코드 에서 선택한 임의의 쌍 중 하나를 제외하고 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 <sqrt (A), 예를 들어 X == Y 인 경우 작동하지 않습니다.
— Matt Timmermans

X == Y는 가장 간단한 예입니다. B는 임의의 수 <SQRT (A)가 될 수 X = 0x30000001, Y = 0x30000007, A = X * Y, B = 6 등
— 매트 Timmermans

@MattTimmermans 큰 캐치. 처리 및 예제를 테스트에 추가하여 59 회 반복으로 해결했습니다. 다른 문제가 있거나이 문제가 해결되지 않은 경우 알려주십시오.
— גלעד ברקן

흥미 롭군 나는 그것이 당신이 일할 때 비싸기를 기대하고있었습니다. 우리는 231199 사건에서 값 비싼 사건이 있다는 것을 알고 있지만, 그 특성을 규명하기는 어렵다. 어쨌든 지금은 잘 작동하는 것처럼 보입니다.
— 매트 티머 맨스

9

적어도 하나의 요소가 <= sqrt (A)라는 것을 알고 있습니다. X를 하나 만들어 봅시다.

X 단위의 비트 길이는 A 길이의 약 절반입니다.

따라서 sqrt (A)보다 값이 높은 X의 상위 비트는 모두 0이며 B의 해당 비트는 Y의 해당 비트와 동일한 값을 가져야합니다.

Y의 상위 비트를 알면 해당 인수 X = A / Y에 대해 아주 작은 범위가 제공됩니다. Y의 최대 값과 최소값에 각각 해당하는 Xmin과 Xmax를 계산합니다. Xmax도 <= sqrt (A) 여야합니다.

그런 다음 Xmin과 Xmax 사이에 가능한 모든 X를 시도하십시오. 너무 많지 않으므로 시간이 오래 걸리지 않습니다.


좋은 해결책! 그러한 X가 얼마나 많은지에 대한 경계가 있습니까?
— ciamej 님의

Y의 상위 비트가 모두 0 인 경우 최대 sqrt (A) / 2입니다. 우려되는 경우 Fermat의 인수 분해 방법으로 제수를 찾아서 확인할 수를 줄일 수 있습니다. en.wikipedia.org/wiki/Fermat%27s_factorization_method
— Matt Timmermans

1
이것은 좋은 통찰력 (+1)이지만 64 비트 정수에 대해 이야기한다면 sqrt (A) / 2는 10 억 이상이 될 수 있습니다. 전형적인 "경쟁적 프로그래밍"상황에서는 여전히 너무 느린 것 같습니다. (면책 조항 : 프로그래밍 경쟁을 한 적이 없었습니다. 어쩌면 이것에 대해 틀렸을 수도 있습니다.) 어쩌면 이것과 결합 될 수있는 추가 통찰력이 있습니까?
— ruakh

2
fermat의 방법을 사용하여 범위 내에서 가능한 제수를 찾으면 sqrt (sqrt (A))로 줄어 듭니다. 확실히 괜찮습니다
— Matt Timmermans

6

이 문제를 해결하는 또 다른 간단한 방법은 낮은한다는 사실에 의존 n 개의 XY와 X XOR Y의 비트 만 낮은에 따라 N 당신이 낮은에 대한 가능한 답변을 사용할 수 있습니다, 따라서 X와 Y의 비트 N 제한하는 비트 완료 될 때까지 하위 n + 1 비트에 대한 가능한 답변 .

불행히도 하나의 n에 대해 둘 이상의 가능성이있을 수 있다는 것을 알아 냈습니다 . 나는 얼마나 많은 가능성이 있을지 모르지만, 아마도 너무 자주는 아닐 것이므로 경쟁 상황에서는 괜찮을 것입니다. 확률 적으로 n 비트에 대한 솔루션은 n + 1 비트에 대해 0 또는 2 개의 솔루션을 동일한 확률로 제공 하기 때문에 몇 가지 가능성 만 있습니다 .

무작위 입력에 대해서는 꽤 잘 작동하는 것 같습니다. 테스트에 사용한 코드는 다음과 같습니다.

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 에서 결과를 확인할 수 있습니다.

보통 수만 번의 검사 만 필요한 것 같습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.