Verilen iki A ve B tamsayısı için, A = X * Y ve B = X x veya Y olacak şekilde bir çift X ve Y numarası bulun


22

Rekabetçi bir programlama kitabında bulduğum bu sorunla mücadele ediyorum, ama nasıl yapılacağını bir çözüm olmadan.

Verilen iki tamsayı için bir ve B , (64-bit tamsayı türü uygun olabilir) bir tek sayı, sayı X ve Y'nin bir çift bulmak bu tür bir = X * Y ve B = X XOR Y Benim yaklaşımı listesine oldu A tüm bölenler ve çarpma kadar o sqrt (A) üzerinde sayılarla sqrt (A) altındaki sayıları eşleştirme deneyin A ve xor eşit olup olmadığını görmek B . Ama bunun yeterince verimli olup olmadığını bilmiyorum. Bu soruna iyi bir çözüm / algoritma ne olurdu?


1
Bir tamsayı operatörünü ve bitsel bir operatörü karıştırmak garip. Gerçekten mi X*Yyoksa X&Y?
Eric Duminil

Bu çarpmadır. (*)
Aster W.

Bu görevi çözmek için zaten bir kod satırı yazdınız mı? Hangi programlama dilini kullanmayı düşünüyorsunuz?
Lynx 242

Yanıtlar:


5

İşte bildiğimiz kurallara uyan basit bir özyineleme: (1) hem X hem de Y'nin en az anlamlı bitleri ayarlanmıştır, çünkü sadece tek çoğul ve tek tek bir kat verir; (2) X'i ayarlanan en yüksek B bitine ayarladıysak, Y, sqrt (A) 'dan büyük olamaz; ve (3) B'deki mevcut bite göre bitleri X veya Y olarak ayarlayın.

Aşağıdaki Python kodu, Matt Timmermans'ın örnek kodundan seçtiğim rastgele çiftlerden biri hariç tümü için 300'den az yineleme ile sonuçlandı . Ama ilki 231.199 iterasyon aldı :)

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

Çıktı:

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), örneğin X == Y
Matt Timmermans

X == Y bunun en basit örneğidir. B, X = 0x30000001, Y = 0x30000007, A = X * Y, B = 6 gibi herhangi bir sayı <sqrt (A) olabilir
Matt Timmermans

@ MattTimmermans harika yakalama. 59 yinelemede çözümlenen testlere kullanım ve örnek ekledim. Başka sorunlar bulursanız (veya bu sorun çözülmemiş gibi görünüyorsa) lütfen bize bildirin.
ברקן at

İlginç. Çalıştırdığınızda bunun pahalı olmasını bekliyordum. 231199'dan pahalı vakalar olduğunu biliyoruz, ancak bunları karakterize etmek zor. Her neyse, bu şimdi iyi çalışıyor gibi görünüyor.
Matt Timmermans

9

En az bir faktörün <= sqrt (A) olduğunu biliyorsunuz. Bunu X yapalım.

Bit cinsinden X uzunluğu A uzunluğunun yaklaşık yarısı olacaktır.

Bu nedenle X'in üst bitleri - sqrt (A) 'dan daha yüksek değerde olanların tümü - 0'dır ve B'deki karşılık gelen bitlerin Y'deki karşılık gelen bitlerle aynı değere sahip olması gerekir.

Y'nin üst bitlerini bilmek, karşılık gelen X = A / Y faktörü için oldukça küçük bir aralık verir. Y için sırasıyla mümkün olan en büyük ve en küçük değerlere karşılık gelen Xmin ve Xmax değerlerini hesaplayın. Xmax değerinin de <= sqrt (A) olması gerektiğini unutmayın.

Sonra sadece Xmin ve Xmax arasındaki tüm olası X'leri deneyin. Çok fazla olmayacak, bu yüzden çok uzun sürmeyecek.


Güzel çözüm! kaç tane X'in var olduğuna dair bir sınır var mı?
ciamej

Y'nin üst bitlerinin hepsinin 0 olduğu durumda en fazla sqrt (A) / 2'dir. Bu konuda endişeleriniz varsa, Fermat'ın çarpanlara ayırma yöntemiyle bölenleri bularak kontrol edilecek sayıyı azaltabilirsiniz: en.wikipedia.org/wiki/Fermat%27s_factorization_method
Matt Timmermans

1
Bu iyi bir fikir (+1), ancak 64 bit tamsayılardan bahsediyorsak, sqrt (A) / 2 bir milyardan fazla olabilir. Bu tipik bir "rekabetçi programlama" durumu için hala çok yavaş gibi görünüyor. (Feragatname: Hiçbir zaman bir programlama yarışması yapmadım, belki de bu konuda yanılıyorum.) Belki de bununla bir şekilde birleştirilebilecek başka bir fikir var mı?
ruakh

2
Menzil içindeki olası bölenleri bulmak için fermat yöntemini kullanırsanız, bence sqrt'a (sqrt (A)) düşüyor, ki bu kesinlikle sorun değil
Matt Timmermans

6

Bu sorunu çözmek için diğer basit yolu düşük olduğu gerçeğine dayanır n XY ve X xor Y bitleri sadece alt bağlıdır n daha düşük olası cevaplar kullanabilirsiniz nedenle X ve Y bit n kısıtlamak için bit bitene kadar düşük n + 1 bitleri için olası cevaplar .

Maalesef, tek bir n için birden fazla olasılık olabileceğini düşündüm . Ne kadar sıklıkta çok fazla olasılık olacağını bilmiyorum , ama muhtemelen çok sık değil, bu yüzden rekabetçi bir bağlamda iyi olabilir. Olasılıkla, sadece birkaç olasılık olacaktır, çünkü n bit için bir çözüm n + 1 bit için eşit olasılıkla 0 veya iki çözüm sağlayacaktır .

Rastgele girdi için oldukça iyi çalışıyor gibi görünüyor. İşte test etmek için kullandığım kod:

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

Sonuçları burada görebilirsiniz: https://ideone.com/cEuHkQ

Görünüşe göre, genellikle sadece birkaç bin kontrol gerekiyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.