2015-06-07 4 views
7

का सटीक योग long[] का सटीक योग प्राप्त करने के लिए मैं निम्न स्निपेट का उपयोग कर रहा हूं।एक लंबी सरणी

public static BigInteger sum(long[] a) { 
    long low = 0; 
    long high = 0; 
    for (final long x : a) { 
     low += (x & 0xFFFF_FFFFL); 
     high += (x >> 32); 
    } 
    return BigInteger.valueOf(high).shiftLeft(32).add(BigInteger.valueOf(low)); 
} 

यह दो हिस्सों में विभाजित संख्याओं को संसाधित करके अंततः आंशिक रकम को संयोजित करके ठीक काम करता है। हैरानी की बात है, इस विधि भी काम करता है:

public static BigInteger fastestSum(long[] a) { 
    long low = 0; 
    long high = 0; 
    for (final long x : a) { 
     low += x; 
     high += (x >> 32); 
    } 
    // We know that low has the lowest 64 bits of the exact sum. 
    // We also know that BigInteger.valueOf(high).shiftLeft(32) differs from the exact sum by less than 2**63. 
    // So the upper half of high is off by at most one. 
    high >>= 32; 
    if (low < 0) ++high; // Surprisingly, this is enough to fix it. 
    return BigInteger.valueOf(high).shiftLeft(64).add(BigInteger.valueOf(low)); 
} 

मुझे क्या करना का मानना ​​है कि fastestSumकाम करना चाहिए के रूप में है नहीं। मेरा मानना ​​है कि यह काम कर सकता है, लेकिन अंतिम चरण में कुछ और करना है। हालांकि, यह मेरे सभी परीक्षणों (बड़े यादृच्छिक परीक्षण सहित) पास करता है। तो मैं पूछ रहा हूं: क्या कोई साबित कर सकता है कि यह काम करता है या काउंटररेक्स नमूना ढूंढता है?

+1

यह काम करना चाहिए, लेकिन मेरे ज्ञान के लिए, आप अपरिभाषित व्यवहार के आधार पर हैं। सबसे महत्वपूर्ण [** पूर्णांक ऑपरेटर किसी भी तरह से ओवरफ़्लो या अंडरफ़्लो इंगित नहीं करते हैं **] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.2 .2 # jls-4.2.2)। – dhke

+0

@ डीकेके, शायद आप सी के साथ जावा को भ्रमित कर रहे हैं। जेएलएस 15.18.2 कहते हैं, कुछ हिस्सों में, "अगर एक पूर्णांक अतिरिक्त ओवरफ्लो हो जाता है, तो परिणाम गणितीय योग की निम्न-क्रम बिट्स है जो कुछ पर्याप्त बड़े दो में दर्शाया गया है - प्रारूप प्रारूप। अगर अतिप्रवाह होता है, तो परिणाम का संकेत दो ऑपरेंड मानों के गणितीय योग के संकेत के समान नहीं है। " जावा भाषा में कम से कम एकल-थ्रेडेड प्रोग्राम्स के लिए कम से कम, यदि कोई है, अपरिभाषित व्यवहार है। –

+0

@ जॉन बोलिंगर सहमत हुए। जैसा कि ढके लिखते हैं, कोई अतिप्रवाह संकेत नहीं है, लेकिन मैं किसी का उपयोग नहीं कर रहा हूं। और परिणाम सही मोड 2 ** 64 है, इसलिए कम से कम 64 बिट सही हैं। मैं केवल 32 बिट्स के बारे में चिंतित हूं। – maaartinus

उत्तर

4
fastestSum(new long[]{+1, -1}) => -18446744073709551616 
+0

क्या यह एक उत्तर है? :) –

+0

@DmitryGinzburg अविश्वसनीय, लेकिन सच! और मेरे "स्मार्ट" परीक्षण के मामलों ने इसे याद किया और एक लाख यादृच्छिक परीक्षण भी किया! असल में, मुझे यह जवाब स्वीकार करना चाहिए, लेकिन यह उबाऊ लगता है और मैं इसके बारे में पूछने के लिए कहूंगा कि इसे कैसे ठीक किया जाए। – maaartinus

+0

@ bayou.io जांच करता है कि विधि काम नहीं करती है .. हालांकि मुझे लगता है कि यह दिलचस्प एल्गोरिदम है, यह उत्तर, आपके प्रश्न का उत्तर दें। – Victor

1

ऐसा लगता है। यह देखते हुए कि मेरे परीक्षणों ने मेरे छोटे संस्करण के साथ समस्या को याद किया, मुझे यकीन नहीं है कि यह सही है या नहीं। जो भी इसका विश्लेषण करना चाहता है उसका स्वागत है:

public static BigInteger fastestSum(long[] a) { 
    long low = 0; 
    long control = 0; 
    for (final long x : a) { 
     low += x; 
     control += (x >> 32); 
    } 
    /* 
    We know that low has the lowest 64 bits of the exact sum. 
    We also know that 2**64 * control differs from the exact sum by less than 2**63. 
    It can't be bigger than the exact sum as the signed shift always rounds towards negative infinity. 
    So the upper half of control is either right or must be incremented by one. 
    */ 
    final long x = control & 0xFFFF_FFFFL; 
    final long y = (low >> 32); 
    long high = (control >> 32); 
    if (x - y > 1L << 31) ++high; 
    return BigInteger.valueOf(high).shiftLeft(64).add(BigInteger.valueOf(low)); 
} 

यह सैने संस्करण से 30% तेज है।

संबंधित मुद्दे