दिए गए दो पूर्णांकों A और B के लिए, X और Y की एक जोड़ी का पता लगाएं, जैसे A = X * Y और B = X Xor Y


22

मैं इस समस्या से जूझ रहा हूं जो मैंने एक प्रतिस्पर्धी प्रोग्रामिंग बुक में पाया है, लेकिन एक समाधान के बिना यह कैसे करना है।

दिए गए दो पूर्णांकों के लिए A और B (64-बिट पूर्णांक प्रकार में फिट हो सकते हैं), जहाँ A विषम है, संख्या X और Y की एक जोड़ी ज्ञात करें कि A = X * Y और B = X xor Y. मेरा दृष्टिकोण सूची में था A के सभी भाजक और sqrt (A) के तहत sqrt (A) की संख्याओं के साथ संख्याओं को जोड़ने का प्रयास करते हैं, जो A तक गुणा करते हैं और देखते हैं कि उनका xor B के बराबर है या नहीं । लेकिन मुझे नहीं पता कि यह पर्याप्त कुशल है या नहीं। इस समस्या का एक अच्छा समाधान / एल्गोरिदम क्या होगा?


1
पूर्णांक ऑपरेटर और बिटवाइज़ ऑपरेटर को मिश्रित करना अजीब है। क्या यह वास्तव में है X*Yया X&Y?
एरिक डुमिनील

यह गुणा है। (*)
एस्टर डब्ल्यू।

क्या आपने इस कार्य को हल करने के लिए पहले से ही कोई लाइन ऑफ कोड लिख दिया है? आप किस प्रोग्रामिंग भाषा का उपयोग करने का इरादा रखते हैं?
लिंक्स 242

जवाबों:


5

यहां एक सरल पुनरावर्तन है जो हमारे द्वारा ज्ञात नियमों का पालन करता है: (1) दोनों एक्स और वाई के कम से कम महत्वपूर्ण बिट्स सेट होते हैं क्योंकि केवल विषम गुणक एक विषम एकाधिक उपज देते हैं; (2) यदि हम X को B के उच्चतम सेट बिट के लिए सेट करते हैं, तो Y, sqrt (A) से बड़ा नहीं हो सकता है; और (3) बी में वर्तमान बिट के अनुसार एक्स या वाई में बिट्स सेट करें।

निम्नलिखित पायथन कोड का परिणाम सभी के लिए 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 केवल सबसे सरल उदाहरण है। बी किसी भी संख्या <sqrt (ए) हो सकता है, एक्स = 0x30000001, वाई = 0x30000007, एक = एक्स * वाई, बी = 6 की तरह
मैट Timmermans

@MattTimmermans शानदार कैच। मैंने परीक्षणों में हैंडलिंग और अपना उदाहरण जोड़ा है, जो 59 पुनरावृत्तियों में हल करता है। कृपया मुझे बताएं कि क्या आपको अन्य मुद्दे मिलते हैं (या यदि यह मुद्दा अनसुलझा लगता है)।
לעג ברקן

दिलचस्प। मैं उम्मीद कर रहा था कि एक महंगा होने के लिए जब आप इसे काम कर रहे हैं। हम जानते हैं कि 231199 एक से महंगे मामले हैं, लेकिन उन्हें चिह्नित करना मुश्किल साबित हो रहा है। वैसे भी ऐसा लग रहा है कि यह अब ठीक काम करता है।
मैट टिम्मरमैन

9

आप जानते हैं कि कम से कम एक कारक <= sqrt (A) है। चलो कि एक एक्स।

बिट्स में X की लंबाई A की लंबाई से लगभग आधी होगी।

X के ऊपरी बिट्स, इसलिए - sqrt (A) से अधिक मूल्य वाले - सभी 0 हैं, और B के संबंधित बिट्स का Y में संबंधित बिट्स के समान मूल्य होना चाहिए।

Y के ऊपरी बिट्स को जानने से आपको संबंधित कारक X = A / Y के लिए बहुत छोटी सीमा मिलती है। क्रमशः, वाई के लिए सबसे बड़े और सबसे छोटे संभव मूल्यों के अनुरूप एक्समिन और एक्समैक्स की गणना करें। याद रखें कि Xmax भी होना चाहिए <= sqrt (A)।

तो बस Xmin और Xmax के बीच सभी संभव एक्स की कोशिश करो। बहुत अधिक नहीं होंगे, इसलिए इसमें बहुत समय नहीं लगेगा।


अच्छा समाधान! क्या ऐसे कितने एक्स मौजूद हैं?
ciamej

यह अधिकांश sqrt (A) / 2 के मामले में है जहाँ Y के ऊपरी भाग सभी 0. 0. कम हैं, उनमें से कुछ विभाजक होंगे, हालाँकि। यदि आप इसके बारे में चिंतित हैं, तो आप Fermat के कारक विधि के साथ भाजकों को खोजने के लिए जाँच कर संख्या को कम कर सकते हैं: en.wikipedia.org/wiki/Fermat%27s_factorization_method
मैट टिम्मरमेर

1
यह एक अच्छी अंतर्दृष्टि (+1) है, लेकिन अगर हम 64-बिट पूर्णांक के बारे में बात कर रहे हैं, तो sqrt (A) / 2 एक अरब से अधिक हो सकता है। ऐसा लगता है कि एक विशिष्ट "प्रतिस्पर्धी प्रोग्रामिंग" स्थिति के लिए अभी भी बहुत धीमा होगा। (अस्वीकरण: मैंने कभी भी एक प्रोग्रामिंग प्रतियोगिता नहीं की है, हो सकता है कि मैं इस बारे में गलत हूं।) हो सकता है कि आगे कोई अंतर्दृष्टि हो जो इसे किसी भी तरह से जोड़ा जा सकता है?
बरबाद

2
यदि आप सीमा में संभावित विभाजकों को खोजने के लिए फ़र्मेट की विधि का उपयोग करते हैं, तो मुझे लगता है कि यह sqrt (sqrt (A)) तक कम हो जाता है, जो निश्चित रूप से ठीक है
मैट टिमरमैन

6

इस समस्या को हल करने के लिए अन्य सरल तरीके से इस तथ्य पर निर्भर करता है कि कम n XY और एक्स XOR वाई के टुकड़े केवल पर कम निर्भर एन एक्स और वाई के टुकड़े इसलिए, आप के लिए कम संभव जवाब उपयोग कर सकते हैं n प्रतिबंधित करने के लिए बिट्स आपके द्वारा किए जाने तक निचले n + 1 बिट्स के लिए संभावित उत्तर ।

मैंने काम किया है, दुर्भाग्य से, एक एन के लिए एक से अधिक संभावनाएं हो सकती हैं । मैं कितनी बार वहाँ एक हो जाएगा पता नहीं है बहुत कुछ संभावनाओं की है, लेकिन यह शायद बहुत बिल्कुल भी अक्सर अगर नहीं है, तो यह एक प्रतिस्पर्धी संदर्भ में ठीक हो सकता है। संभावित रूप से, केवल कुछ संभावनाएं होंगी, क्योंकि 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

ऐसा लगता है कि यह आम तौर पर केवल कुछ हज़ार का चेक लेता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.