Для заданих двох цілих чисел A і B знайдіть пару чисел X і Y таких, що A = X * Y і B = X x або Y


22

Я боюся з цією проблемою, яку я знайшов у книзі з програмування конкурентоспроможних програм, але без рішення, як це зробити.

Для заданих двох цілих чисел A і B (може вміститися в 64-бітний цілочисельний тип), де A непарне, знайдіть пару чисел X і Y таких, що A = X * Y і B = X xor Y. Мій підхід полягав у списку всі подільники а й спробуйте спарювання номера під SQRT (а) з номерами над SQRT (A) , які розмножуються до а й подивитися , якщо їх виключає одно B . Але я не знаю, чи це досить ефективно. Яке б було хорошим рішенням / алгоритмом цієї проблеми?


1
Дивно змішувати цілий оператор і побітовий оператор. Це насправді X*Yчи X&Y?
Ерік Думініл

Це множення. (*)
Астер В.

Ви вже написали будь-який рядок коду, щоб вирішити це завдання? Яку мову програмування ви маєте намір використовувати?
Lynx 242

Відповіді:


5

Ось проста рекурсія, яка дотримується відомих нам правил: (1) встановлюються найменш значущі біти як X, так і Y, оскільки лише непарні множини дають непарне кратне; (2) якщо ми встановимо, що X має найвищий встановлений біт B, Y не може бути більшим за sqrt (A); і (3) встановити біти в X або Y відповідно до поточного біта в B.

Наступний код 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 <sqrt (A), наприклад, коли X == Y
Метт Тіммерманс

X == Y - найпростіший приклад. B може бути будь-яке число <sqrt (A), як X = 0x30000001, Y = 0x30000007, A = X * Y, B = 6
Метт Тіммерманс

@MattTimmermans чудовий улов. До тестів я додав обробку та ваш приклад, який вирішується в 59 ітераціях. Будь ласка, повідомте мене, якщо ви знайдете інші проблеми (або якщо ця проблема здається невирішеною).
גלעד ברקן

Цікаво. Я очікував, що ця дорога, коли ти будеш працювати. Ми знаємо, що є 231199 дорогих випадків, але їх важко охарактеризувати. У будь-якому випадку, схоже, це зараз добре працює.
Метт Тіммерманс

9

Ви знаєте, що принаймні один фактор - <= sqrt (A). Зробимо цей X.

Довжина X у бітах становитиме приблизно половину довжини А.

Верхні біти X, отже, - ті, що мають значення, ніж sqrt (A), - всі 0, і відповідні біти в B повинні мати те саме значення, що і відповідні біти в Y.

Знання верхніх бітів Y дає досить невеликий діапазон для відповідного коефіцієнта X = A / Y. Обчисліть Xmin і Xmax, що відповідають найбільшим і найменшим можливим значенням Y відповідно. Пам'ятайте, що Xmax також повинен бути <= sqrt (A).

Потім просто спробуйте всі можливі Xs між Xmin та Xmax. Не буде занадто багато, тому це не займе багато часу.


Приємне рішення! чи існує обмеження на те, скільки таких X існує?
ciamej

це не більше sqrt (A) / 2 у випадку, коли верхні біти Y дорівнюють 0. Менше їх буде дільниками. Якщо вас це турбує, ви можете зменшити кількість для перевірки, знайшовши дільники методом факторизації Ферма: en.wikipedia.org/wiki/Fermat%27s_factorization_method
Метт Тіммерманс

1
Це добре розуміння (+1), але якщо ми говоримо про 64-бітні цілі числа, то sqrt (A) / 2 може становити більше мільярда. Здається, що це все ще буде надто повільним для типової ситуації "конкурентного програмування". (Відмова: Я ніколи не робив змагання з програмування, можливо, я помиляюся з цього приводу.) Можливо, є додаткове розуміння, яке можна якось поєднати з цим?
ruakh

2
Якщо ви використовуєте метод Fermat, щоб знайти можливі дільники в діапазоні, я думаю, що він зводиться до sqrt (sqrt (A)), що, звичайно, добре
Метт Тіммерманс

6

Інший прямолінійний спосіб вирішити цю проблему покладається на той факт, що нижні n бітів XY і X xor Y залежать лише від нижніх n бітів X і Y. Тому ви можете використовувати можливі відповіді на нижчі n бітів для обмеження можливі відповіді на нижчі n + 1 біт, поки ви не закінчите.

Я розробив, що, на жаль, може бути більше однієї можливості для одного n . Я не знаю, як часто буде багато можливостей, але це, мабуть, не надто часто, якщо взагалі є, тому це може бути добре в конкурентному контексті. Ймовірно, що буде лише кілька можливостей, оскільки рішення для n бітів забезпечить або 0, або два рішення для n + 1 біт, з однаковою ймовірністю.

Здається, це спрацює досить добре для випадкового введення. Ось код, який я використовував для його тестування:

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

Схоже, це зазвичай займає лише пару тисяч чеків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.