एक्सप्र में ओवरफ्लो से कैसे बचें। ऐ बी सी डी


161

मुझे एक अभिव्यक्ति की गणना करने की आवश्यकता है जो ऐसा दिखता है: A*B - C*Dजहां उनके प्रकार हैं: signed long long int A, B, C, D; प्रत्येक संख्या वास्तव में बड़ी हो सकती है (इसके प्रकार को अतिप्रवाह नहीं)। जबकि A*Bअतिप्रवाह का कारण बन सकता है, एक ही समय में अभिव्यक्ति A*B - C*Dवास्तव में छोटी हो सकती है। मैं इसे कैसे सही ढंग से गणना कर सकता हूं?

उदाहरण के लिए: MAX * MAX - (MAX - 1) * (MAX + 1) == 1कहाँ MAX = LLONG_MAX - nऔर n - कुछ प्राकृतिक संख्या।


17
सटीकता कितनी महत्वपूर्ण है?
अनिरुद्ध रामनाथन

1
@ कथुलु, महान प्रश्न। वह उन सभी को 10 या कुछ से विभाजित करके, फिर परिणाम को गुणा करके छोटी संख्या का उपयोग करके एक समान कार्य करने का प्रयास कर सकता है।
क्रिस

4
Vars A, B, C, D पर हस्ताक्षर किए गए हैं। इसका तात्पर्य A - Cअतिप्रवाह हो सकता है। यह विचार करने या आपको पता है कि यह आपके डेटा के साथ नहीं होने जा रहा है?
विलियम मॉरिस

2
@MooingDuck लेकिन आप पहले से जांच कर सकते हैं आपरेशन अतिप्रवाह जाएगा stackoverflow.com/a/3224630/158285
bradgonesurfing

1
@ क्रिस: नहीं, मैं कह रहा हूं कि अगर कोई हस्ताक्षरित अतिप्रवाह हुआ है तो यह जांचने का कोई पोर्टेबल तरीका नहीं है। (ब्रैड सही है कि आप आंशिक रूप से पता लगा सकते हैं कि ऐसा होगा )। इनलाइन असेंबली का उपयोग करना जाँचने के कई गैर-पोर्टेबल तरीकों में से एक है।
मूविंग डक

जवाबों:


120

मुझे लगता है यह बहुत तुच्छ लगता है। लेकिन A*Bएक है जो अतिप्रवाह कर सकता है।

आप सटीक खोए बिना, निम्नलिखित कर सकते हैं

A*B - C*D = A(D+E) - (A+F)D
          = AD + AE - AD - DF
          = AE - DF
             ^smaller quantities E & F

E = B - D (hence, far smaller than B)
F = C - A (hence, far smaller than C)

यह अपघटन आगे भी किया जा सकता है
जैसा कि @Gian ने कहा, यदि प्रकार लंबे समय तक अहस्ताक्षरित है, तो घटाव संचालन के दौरान देखभाल की आवश्यकता हो सकती है।


उदाहरण के लिए, आपके पास प्रश्न के मामले में, यह सिर्फ एक पुनरावृत्ति लेता है,

 MAX * MAX - (MAX - 1) * (MAX + 1)
  A     B       C           D

E = B - D = -1
F = C - A = -1

AE - DF = {MAX * -1} - {(MAX + 1) * -1} = -MAX + MAX + 1 = 1

4
@ कालेब, बस उसी एल्गोरिथ्म को लागू करेंC*D
क्रिस

2
मुझे लगता है कि आपको यह बताना चाहिए कि ई क्या दर्शाता है।
कालेब

7
दोनों लंबे लंबे और डबल 64 बिट हैं। चूंकि डबल को घातांक के लिए कुछ बिट्स आवंटित करना पड़ता है, इसमें सटीक नुकसान के बिना संभावित मानों की एक छोटी श्रृंखला होती है।
जिम गैरिसन

3
@Cthulhu - मुझे ऐसा लगता है कि यह तभी काम करेगा जब सभी संख्या बहुत बड़ी हो ... उदाहरण के लिए, आपके पास अभी भी {A, B, C, D} = {MAX, MAX, MAX, 2} के साथ ओवरफ्लो होगा। ओपी का कहना है कि "प्रत्येक संख्या वास्तव में बड़ी हो सकती है", लेकिन यह समस्या कथन से स्पष्ट नहीं है कि प्रत्येक संख्या वास्तव में बड़ी होनी चाहिए।
केविन के

4
यदि कोई A,B,C,Dनकारात्मक हो तो क्या होगा ? नहीं करेंगे Eया Fफिर भी बड़ा हो सकता है?
Supr

68

सबसे सरल और सबसे सामान्य समाधान एक प्रतिनिधित्व का उपयोग करना है, जो एक पूर्णांक पुस्तकालय (जैसे http://gmplib.org/ ) का उपयोग करके या किसी संरचना या सरणी का उपयोग करके या एक प्रकार का लंबा गुणा लागू करके अतिप्रवाह नहीं कर सकता है ( अर्थात प्रत्येक संख्या को दो 32 बिट्स तक सीमित करना और गुणा को नीचे के रूप में करना:

(R1 + R2 * 2^32 + R3 * 2^64 + R4 * 2^96) = R = A*B = (A1 + A2 * 2^32) * (B1 + B2 * 2^32) 
R1 = (A1*B1) % 2^32
R2 = ((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) % 2^32
R3 = (((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) / 2^32 + (A1*B2) / 2^32 + (A2*B1) / 2^32 + (A2*B2) % 2^32) %2^32
R4 = ((((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) / 2^32 + (A1*B2) / 2^32 + (A2*B1) / 2^32 + (A2*B2) % 2^32) / 2^32) + (A2*B2) / 2^32

अंतिम परिणाम यह मानते हुए कि 64 बिट्स में वास्तव में आपको R3 के अधिकांश बिट्स और R4 में से कोई भी नहीं चाहिए


8
उपरोक्त गणना वास्तव में उतनी जटिल नहीं है जितनी कि यह दिखती है, यह वास्तव में आधार 2 ^ 32 में सरल लंबी गुणा है, और सी में कोड सरल दिखना चाहिए। साथ ही, इस कार्य को अपने कार्यक्रम में करने के लिए सामान्य कार्य करना एक अच्छा विचार होगा।
Ofir

46

ध्यान दें कि यह मानक नहीं है क्योंकि यह रैप-अराउंड साइन-ओवरफ्लो पर निर्भर करता है। (जीसीसी में संकलक झंडे हैं जो इसे सक्षम करते हैं।)

लेकिन अगर आप सिर्फ सभी गणनाएँ करते हैं long long, तो सीधे फॉर्मूला लागू करने का परिणाम:
(A * B - C * D)तब तक सटीक होगा जब तक कि सही परिणाम में फिट नहीं हो जाता long long


यहां एक ऐसा काम है, जो केवल हस्ताक्षर किए गए पूर्णांक के लिए अहस्ताक्षरित कास्टिंग के कार्यान्वयन-परिभाषित व्यवहार पर निर्भर करता है। लेकिन आज लगभग हर सिस्टम पर काम करने की उम्मीद की जा सकती है।

(long long)((unsigned long long)A * B - (unsigned long long)C * D)

यह उन इनपुट्स को जमा करता है unsigned long longजहां अतिप्रवाह व्यवहार को मानक द्वारा लपेटे जाने की गारंटी है। अंत में एक हस्ताक्षरित पूर्णांक पर वापस कास्टिंग कार्यान्वयन-परिभाषित हिस्सा है, लेकिन आज लगभग सभी वातावरणों पर काम करेगा।


यदि आपको अधिक पांडित्य समाधान की आवश्यकता है, तो मुझे लगता है कि आपको "लंबे अंकगणित" का उपयोग करना होगा


+1 यह नोटिस करने वाले आप अकेले हैं। एकमात्र पेचीदा हिस्सा कंपाइलर को रैप-अराउंड-ओवरफ़्लो करने के लिए सेट कर रहा है और जाँच कर रहा है कि क्या सही परिणाम वास्तव में फिट होता है long long
रहस्यवादी

2
यहां तक ​​कि किसी भी चाल के बिना भोले संस्करण सबसे कार्यान्वयन पर सही काम करेंगे ; यह मानक की गारंटी नहीं है, लेकिन आपको इसे विफल बनाने के लिए 1-पूरक मशीन या कुछ अन्य काफी अजीब डिवाइस ढूंढना होगा।
hobbs

1
मुझे लगता है कि यह एक महत्वपूर्ण उत्तर है। मैं मानता हूं कि कार्यान्वयन विशिष्ट व्यवहार को मानने के लिए यह सही प्रोग्रामिंग नहीं हो सकता है, लेकिन प्रत्येक इंजीनियर को मॉडुलो अंकगणित को समझना चाहिए और यदि प्रदर्शन आवश्यक है तो निरंतर व्यवहार सुनिश्चित करने के लिए सही कंपाइलर झंडे कैसे प्राप्त करें। डीएसपी इंजीनियर निश्चित बिंदु फ़िल्टर कार्यान्वयन के लिए इस व्यवहार पर भरोसा करते हैं, जिसके लिए स्वीकृत जवाब में अस्वीकार्य प्रदर्शन होगा।
पीटर एम

18

यह काम करना चाहिए (मुझे लगता है):

signed long long int a = 0x7ffffffffffffffd;
signed long long int b = 0x7ffffffffffffffd;
signed long long int c = 0x7ffffffffffffffc;
signed long long int d = 0x7ffffffffffffffe;
signed long long int bd = b / d;
signed long long int bdmod = b % d;
signed long long int ca = c / a;
signed long long int camod = c % a;
signed long long int x = (bd - ca) * a * d - (camod * d - bdmod * a);

यहाँ मेरी व्युत्पत्ति है:

x = a * b - c * d
x / (a * d) = (a * b - c * d) / (a * d)
x / (a * d) = b / d - c / a

now, the integer/mod stuff:
x / (a * d) = (b / d + ( b % d ) / d) - (c / a + ( c % a ) / a )
x / (a * d) = (b / d - c / a) - ( ( c % a ) / a - ( b % d ) / d)
x = (b / d - c / a) * a * d - ( ( c % a ) * d - ( b % d ) * a)

1
धन्यवाद @bradgonesurfing - क्या आप ऐसा इनपुट प्रदान कर सकते हैं? मैंने अपना उत्तर अपडेट कर दिया है, इसे निष्पादित कर दिया है और यह काम करता है (bd और ca 0 हैं) ...
paquetp

1
हममम। अब मैं इसके बारे में सोचता हूं शायद नहीं। डी = 1 और ए = 1 और बी = मैक्सिंट और सी = अधिकतम के साथ डीजेनरेट मामला अभी भी काम करता है। कूल :)
ब्रैडगोनसर्फिंग

1
@paquetp: a = 1, b = 0x7fffffffffffff, c = -0x7fffffffffffffff, d = 1 (नोट c ऋणात्मक है)। हालांकि चालाक, मुझे यकीन है कि आपका कोड सभी सकारात्मक संख्याओं को सही ढंग से संभालता है।
मिचिंग डक

3
@MooDDuck लेकिन आपके सेट के लिए अंतिम उत्तर के रूप में अच्छी तरह से बह निकला है, इसलिए यह एक वैध सेटअप नहीं है। यह केवल तभी काम करता है जब प्रत्येक पक्ष समान चिन्ह का हो इसलिए परिणामी घटाव सीमा के भीतर होता है।
ब्रैडगोनसर्फिंग

1
स्टैकऑवरफ्लो के साथ कुछ अजीब है जब यह जवाब जो सबसे सरल है और सबसे अच्छे को शीर्ष स्कोर उत्तर की तुलना में इतना कम स्कोर मिला है।
ब्रैडगोनसर्फिंग

9

आप अपने सभी मूल्यों के लिए एक सबसे बड़े सामान्य कारक की गणना करने पर विचार कर सकते हैं, और फिर अपने अंकगणितीय कार्यों को करने से पहले उन्हें उस कारक से विभाजित कर सकते हैं, फिर फिर से गुणा कर सकते हैं। मतलब यह है कि इस तरह के एक कारक मौजूद है, फिर भी (उदाहरण के लिए, अगर A, B, Cऔर Dअपेक्षाकृत प्रधानमंत्री होने के लिए होता है, वे एक आम कारक नहीं होगा)।

इसी तरह, आप लॉग-तराजू पर काम करने पर विचार कर सकते हैं, लेकिन यह थोड़ा डरावना होने वाला है, संख्यात्मक परिशुद्धता के अधीन है।


1
यदि long doubleउपलब्ध हो तो लॉगरिदमिंग अच्छा लगता है। उस स्थिति में, एक सटीक स्तर की सटीकता हासिल की जा सकती है (और परिणाम को गोल किया जा सकता है)।

9

यदि परिणाम लंबे लंबे इंट में फिट बैठता है तो अभिव्यक्ति ए * बीसी * डी ठीक है क्योंकि यह अंकगणितीय मॉड 2 ^ 64 का प्रदर्शन करता है, और सही परिणाम देगा। समस्या यह जानने के लिए है कि क्या परिणाम लंबे लंबे इंट में फिट बैठता है। इसका पता लगाने के लिए, आप युगल का उपयोग करके निम्नलिखित चाल का उपयोग कर सकते हैं:

if( abs( (double)A*B - (double)C*D ) > MAX_LLONG ) 
    Overflow
else 
    return A*B-C*D;

इस दृष्टिकोण के साथ समस्या यह है कि आप युगल (54 बिट्स) के मंटिसा की सटीकता से सीमित हैं, इसलिए आपको उत्पादों को ए * बी और सी * डी को 63 + 54 बिट्स (या शायद थोड़ा कम) तक सीमित करने की आवश्यकता है।


यह सबसे व्यावहारिक उदाहरण है। स्पष्ट और सही उत्तर देता है (या इनपुट खराब होने पर एक अपवाद फेंकता है)।
मार्क लाटाटा

1
अच्छा और सुरुचिपूर्ण! आप दूसरों के लिए गिर गया जाल के लिए गिर नहीं था। बस एक और बात: मैं शर्त लगाऊंगा कि कुछ उदाहरण हैं जहां राउंडिंग त्रुटियों के कारण डबल गणना MAX_LLONG से नीचे है। मेरी गणितीय प्रवृत्ति मुझे बताती है कि आपको इसके बजाय दोहरे और लंबे परिणाम के अंतर की गणना करनी चाहिए, और उसकी तुलना MAX_LLONG / 2 या कुछ और से करनी चाहिए। यह अंतर डबल गणना और प्लसफ़्लो की गोलाई की त्रुटियां हैं और आम तौर पर अपेक्षाकृत कम होना चाहिए, लेकिन जिस मामले में मैंने उल्लेख किया है वह बड़ा होगा। लेकिन अभी मैं निश्चित रूप से पता लगाने के लिए बहुत आलसी हूं। :-)
हंस-पीटर स्टॉर


7

आप एक सरणी में प्रत्येक संख्या को लिख सकते हैं, प्रत्येक तत्व एक अंक है और गणना बहुपद के रूप में कर सकते हैं । परिणामी बहुपद को लीजिए, जो एक सरणी है, और सरणी में स्थिति की शक्ति के साथ सरणी के प्रत्येक तत्व को गुणा करके परिणाम की गणना करें (पहली स्थिति सबसे बड़ी और अंतिम होने वाली शून्य)।

संख्या 123को इस प्रकार व्यक्त किया जा सकता है:

123 = 100 * 1 + 10 * 2 + 3

जिसके लिए आप सिर्फ एक सरणी बनाते हैं [1 2 3]

आप सभी संख्याओं ए, बी, सी और डी के लिए ऐसा करते हैं, और फिर आप उन्हें बहुपदों के रूप में गुणा करते हैं। एक बार जब आप परिणामी बहुपद हो जाते हैं, तो आप बस इससे संख्या का पुनर्निर्माण करते हैं।


2
पता नहीं क्या है, लेकिन मुझे ढूंढना होगा। डाल :) । जब मैं अपनी प्रेमिका के साथ खरीदारी कर रहा होता हूं, तो यह मेरे सिर के शीर्ष का एक हल है :)
मिहई

आप एक base10 सरणी में bignums लागू कर रहे हैं। GMP एक क्वालिटी बिग्नम लाइब्रेरी है जो आधार 4294967296 का उपयोग करती है। MUCH तेजी से। हालांकि कोई जवाब नहीं है, क्योंकि उत्तर सही है, और उपयोगी है।
मूविंग डक

धन्यवाद :) । यह जानकर उपयोगी है कि इसे करने का यह एक तरीका है लेकिन बेहतर तरीके हैं इसलिए इसे इस तरह से न करें। कम से कम इस स्थिति में तो नहीं :)
मिहाइ

वैसे भी ... इस समाधान का उपयोग कर आप किसी भी आदिम प्रकार की तुलना में बड़ी संख्या में कंप्यूटर को बोल्ड कर सकते हैं (जैसे 100 अंकों की संख्या) और परिणाम को एक सरणी के रूप में रख सकते हैं। यह
मिहाई

मुझे यकीन नहीं है कि यह एक उत्थान है, क्योंकि यह विधि (हालांकि प्रभावी और समझने में आसान है) स्मृति भूख और धीमी है।
मिचिंग डक

6

जबकि एक signed long long intपकड़ नहीं होगा A*B, उनमें से दो करेंगे। तो A*Bविभिन्न घातांक के पेड़ की शर्तों को विघटित किया जा सकता है, उनमें से कोई भी एक फिटिंग signed long long int

A1=A>>32;
A0=A & 0xffffffff;
B1=B>>32;
B0=B & 0xffffffff;

AB_0=A0*B0;
AB_1=A0*B1+A1*B0;
AB_2=A1*B1;

उसी के लिए C*D

सीधे तरीके से फोलो करते हुए, प्रत्येक जोड़ी के लिए सबअरेक्शन किया जा सकता है AB_iऔर CD_iइसी तरह, प्रत्येक के लिए एक अतिरिक्त कैरी बिट (सटीक रूप से 1-बिट पूर्णांक) का उपयोग किया जा सकता है। तो अगर हम कहते हैं कि E = A * BC * D आपको कुछ मिलता है:

E_00=AB_0-CD_0 
E_01=(AB_0 > CD_0) == (AB_0 - CD_0 < 0) ? 0 : 1  // carry bit if overflow
E_10=AB_1-CD_1 
...

हम के ऊपरी-आधा स्थानांतरित करके जारी रखने E_10के लिए E_20(32 से पारी और जोड़ने के लिए, तो के ऊपरी हिस्से को मिटा E_10)।

अब आप E_11इसे सही चिह्न (नॉन-कैरी भाग से प्राप्त) के साथ जोड़कर कैरी बिट से छुटकारा पा सकते हैं E_20। यदि यह एक अतिप्रवाह को ट्रिगर करता है, तो परिणाम या तो फिट नहीं होगा।

E_10अब पर्याप्त 'स्थान' ऊपरी आधे हिस्से से ले जाने के लिए E_00 (शिफ्ट, ऐड, इरेज़) और कैरी बिट है E_01

E_10अब फिर से बड़ा हो सकता है, इसलिए हम स्थानांतरण को दोहराते हैं E_20

इस बिंदु पर, E_20शून्य होना चाहिए, अन्यथा परिणाम फिट नहीं होगा। E_10स्थानांतरण के परिणाम के रूप में ऊपरी आधा खाली है।

अंतिम चरण के निचले आधे हस्तांतरण करने के लिए है E_20में E_10फिर से।

अगर उम्मीद है कि धारण E=A*B+C*Dफिट होगा signed long long int, हम अब है

E_20=0
E_10=0
E_00=E

1
यह वास्तव में सरलीकृत फार्मूला है जो किसी को भी होगा तोर के गुणा फार्मूला का उपयोग करने और हर बेकार अस्थायी परिणाम को हटाने के लिए।
ड्रोनस

3

यदि आप जानते हैं कि अंतिम परिणाम आपके पूर्णांक प्रकार में प्रतिनिधित्व करने योग्य है, तो आप नीचे दिए गए कोड का उपयोग करके इस गणना को जल्दी से कर सकते हैं। क्योंकि C मानक निर्दिष्ट करता है कि अहस्ताक्षरित अंकगणित modulo अंकगणित है और अतिप्रवाह नहीं करता है, आप गणना करने के लिए एक अहस्ताक्षरित प्रकार का उपयोग कर सकते हैं।

निम्न कोड मानता है कि एक ही चौड़ाई का एक अहस्ताक्षरित प्रकार है और यह कि हस्ताक्षरित प्रकार मानों का प्रतिनिधित्व करने के लिए सभी बिट पैटर्न का उपयोग करता है (कोई जाल निरूपण नहीं, हस्ताक्षरित प्रकार का न्यूनतम अहस्ताक्षरित प्रकार के आधे मापांक का ऋणात्मक है)। यदि यह C कार्यान्वयन में नहीं है, तो इसके लिए ConvertToSigned दिनचर्या में सरल समायोजन किया जा सकता है।

निम्नलिखित उपयोग करता है signed charऔर unsigned charकोड प्रदर्शित करने के लिए। अपने कार्यान्वयन के लिए, की परिभाषा बदलने Signedके लिए typedef signed long long int Signed;और की परिभाषा Unsignedकरने के लिए typedef unsigned long long int Unsigned;

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>


//  Define the signed and unsigned types we wish to use.
typedef signed char   Signed;
typedef unsigned char Unsigned;

//  uHalfModulus is half the modulus of the unsigned type.
static const Unsigned uHalfModulus = UCHAR_MAX/2+1;

//  sHalfModulus is the negation of half the modulus of the unsigned type.
static const Signed   sHalfModulus = -1 - (Signed) (UCHAR_MAX/2);


/*  Map the unsigned value to the signed value that is the same modulo the
    modulus of the unsigned type.  If the input x maps to a positive value, we
    simply return x.  If it maps to a negative value, we return x minus the
    modulus of the unsigned type.

    In most C implementations, this routine could simply be "return x;".
    However, this version uses several steps to convert x to a negative value
    so that overflow is avoided.
*/
static Signed ConvertToSigned(Unsigned x)
{
    /*  If x is representable in the signed type, return it.  (In some
        implementations, 
    */
    if (x < uHalfModulus)
        return x;

    /*  Otherwise, return x minus the modulus of the unsigned type, taking
        care not to overflow the signed type.
    */
    return (Signed) (x - uHalfModulus) - sHalfModulus;
}


/*  Calculate A*B - C*D given that the result is representable as a Signed
    value.
*/
static signed char Calculate(Signed A, Signed B, Signed C, Signed D)
{
    /*  Map signed values to unsigned values.  Positive values are unaltered.
        Negative values have the modulus of the unsigned type added.  Because
        we do modulo arithmetic below, adding the modulus does not change the
        final result.
    */
    Unsigned a = A;
    Unsigned b = B;
    Unsigned c = C;
    Unsigned d = D;

    //  Calculate with modulo arithmetic.
    Unsigned t = a*b - c*d;

    //  Map the unsigned value to the corresponding signed value.
    return ConvertToSigned(t);
}


int main()
{
    //  Test every combination of inputs for signed char.
    for (int A = SCHAR_MIN; A <= SCHAR_MAX; ++A)
    for (int B = SCHAR_MIN; B <= SCHAR_MAX; ++B)
    for (int C = SCHAR_MIN; C <= SCHAR_MAX; ++C)
    for (int D = SCHAR_MIN; D <= SCHAR_MAX; ++D)
    {
        //  Use int to calculate the expected result.
        int t0 = A*B - C*D;

        //  If the result is not representable in signed char, skip this case.
        if (t0 < SCHAR_MIN || SCHAR_MAX < t0)
            continue;

        //  Calculate the result with the sample code.
        int t1 = Calculate(A, B, C, D);

        //  Test the result for errors.
        if (t0 != t1)
        {
            printf("%d*%d - %d*%d = %d, but %d was returned.\n",
                A, B, C, D, t0, t1);
            exit(EXIT_FAILURE);
        }
    }
    return 0;
}

2

आप समीकरण को छोटे घटकों में तोड़ने की कोशिश कर सकते हैं जो अतिप्रवाह नहीं करते हैं।

AB - CD
= [ A(B - N) - C( D - M )] + [AN - CM]

= ( AK - CJ ) + ( AN - CM)

    where K = B - N
          J = D - M

यदि घटक अभी भी अतिप्रवाह करते हैं, तो आप उन्हें छोटे घटकों में पुनरावर्ती रूप से तोड़ सकते हैं और फिर पुन: संयोजित कर सकते हैं।


यह सही हो सकता है या नहीं भी, लेकिन निश्चित रूप से भ्रामक है। आप परिभाषित करते हैं Kऔर J, क्यों नहीं Nऔर M। इसके अलावा, मुझे लगता है कि आप समीकरण को बड़े टुकड़ों में तोड़ रहे हैं । चूँकि आपका चरण 3 ओपी के प्रश्न के समान है, सिवाय अधिक जटिल (AK-CJ)->(AB-CD)
मूविंग डक

N किसी भी चीज़ से सरल नहीं है। इसे छोटा करने के लिए यह A से घटाया गया एक नंबर है। वास्तव में यह एक समान लेकिन अवर समाधान है। यहाँ मैं इसे छोटा बनाने के लिए पूर्णांक विभाजन के बजाय घटाव का उपयोग कर रहा हूँ।
ब्रैडगोनसर्फिंग

2

मैंने सभी किनारे के मामलों को कवर नहीं किया हो सकता है, न ही मैंने इसका कड़ाई से परीक्षण किया है, लेकिन यह एक तकनीक है जिसे मैं 80 के दशक में उपयोग करते हुए याद करता हूं जब 16-बिट सीपीयू पर 32-बिट पूर्णांक गणित करने की कोशिश कर रहा था। अनिवार्य रूप से आप 32 बिट्स को दो 16-बिट इकाइयों में विभाजित करते हैं और उनके साथ अलग से काम करते हैं।

public class DoubleMaths {
  private static class SplitLong {
    // High half (or integral part).
    private final long h;
    // Low half.
    private final long l;
    // Split.
    private static final int SPLIT = (Long.SIZE / 2);

    // Make from an existing pair.
    private SplitLong(long h, long l) {
      // Let l overflow into h.
      this.h = h + (l >> SPLIT);
      this.l = l % (1l << SPLIT);
    }

    public SplitLong(long v) {
      h = v >> SPLIT;
      l = v % (1l << SPLIT);
    }

    public long longValue() {
      return (h << SPLIT) + l;
    }

    public SplitLong add ( SplitLong b ) {
      // TODO: Check for overflow.
      return new SplitLong ( longValue() + b.longValue() );
    }

    public SplitLong sub ( SplitLong b ) {
      // TODO: Check for overflow.
      return new SplitLong ( longValue() - b.longValue() );
    }

    public SplitLong mul ( SplitLong b ) {
      /*
       * e.g. 10 * 15 = 150
       * 
       * Divide 10 and 15 by 5
       * 
       * 2 * 3 = 5
       * 
       * Must therefore multiply up by 5 * 5 = 25
       * 
       * 5 * 25 = 150
       */
      long lbl = l * b.l;
      long hbh = h * b.h;
      long lbh = l * b.h;
      long hbl = h * b.l;
      return new SplitLong ( lbh + hbl, lbl + hbh );
    }

    @Override
    public String toString () {
      return Long.toHexString(h)+"|"+Long.toHexString(l);
    }
  }

  // I'll use long and int but this can apply just as easily to long-long and long.
  // The aim is to calculate A*B - C*D without overflow.
  static final long A = Long.MAX_VALUE;
  static final long B = Long.MAX_VALUE - 1;
  static final long C = Long.MAX_VALUE;
  static final long D = Long.MAX_VALUE - 2;

  public static void main(String[] args) throws InterruptedException {
    // First do it with BigIntegers to get what the result should be.
    BigInteger a = BigInteger.valueOf(A);
    BigInteger b = BigInteger.valueOf(B);
    BigInteger c = BigInteger.valueOf(C);
    BigInteger d = BigInteger.valueOf(D);
    BigInteger answer = a.multiply(b).subtract(c.multiply(d));
    System.out.println("A*B - C*D = "+answer+" = "+answer.toString(16));

    // Make one and test its integrity.
    SplitLong sla = new SplitLong(A);
    System.out.println("A="+Long.toHexString(A)+" ("+sla.toString()+") = "+Long.toHexString(sla.longValue()));

    // Start small.
    SplitLong sl10 = new SplitLong(10);
    SplitLong sl15 = new SplitLong(15);
    SplitLong sl150 = sl10.mul(sl15);
    System.out.println("10="+sl10.longValue()+"("+sl10.toString()+") * 15="+sl15.longValue()+"("+sl15.toString()+") = "+sl150.longValue() + " ("+sl150.toString()+")");

    // The real thing.
    SplitLong slb = new SplitLong(B);
    SplitLong slc = new SplitLong(C);
    SplitLong sld = new SplitLong(D);
    System.out.println("B="+Long.toHexString(B)+" ("+slb.toString()+") = "+Long.toHexString(slb.longValue()));
    System.out.println("C="+Long.toHexString(C)+" ("+slc.toString()+") = "+Long.toHexString(slc.longValue()));
    System.out.println("D="+Long.toHexString(D)+" ("+sld.toString()+") = "+Long.toHexString(sld.longValue()));
    SplitLong sanswer = sla.mul(slb).sub(slc.mul(sld));
    System.out.println("A*B - C*D = "+sanswer+" = "+sanswer.longValue());

  }

}

प्रिंटों:

A*B - C*D = 9223372036854775807 = 7fffffffffffffff
A=7fffffffffffffff (7fffffff|ffffffff) = 7fffffffffffffff
10=10(0|a) * 15=15(0|f) = 150 (0|96)
B=7ffffffffffffffe (7fffffff|fffffffe) = 7ffffffffffffffe
C=7fffffffffffffff (7fffffff|ffffffff) = 7fffffffffffffff
D=7ffffffffffffffd (7fffffff|fffffffd) = 7ffffffffffffffd
A*B - C*D = 7fffffff|ffffffff = 9223372036854775807

जो मुझे लगता है कि यह काम कर रहा है।

मुझे यकीन है कि मैंने कुछ सूक्ष्मताओं को याद किया है जैसे कि साइन ओवरफ़्लो आदि देखने के लिए, लेकिन मुझे लगता है कि सार है।


1
मुझे लगता है कि यह @Ofir ने जो सुझाव दिया है उसका कार्यान्वयन है।
OldCurmudgeon

2

पूर्णता के लिए, चूंकि किसी ने भी इसका उल्लेख नहीं किया है, इसलिए कुछ संकलक (जैसे जीसीसी) वास्तव में आजकल आपको 128 बिट पूर्णांक प्रदान करते हैं।

इस प्रकार एक आसान समाधान हो सकता है:

(long long)((__int128)A * B - (__int128)C * D)

1

AB-CD = (AB-CD) * AC / AC = (B/C-D/A)*A*C। न तो B/Cहै और न ही D/Aअतिप्रवाह सकते हैं, इसलिए गणना (B/C-D/A)पहले। चूंकि अंतिम परिणाम आपकी परिभाषा के अनुसार अतिप्रवाह नहीं होगा, आप सुरक्षित रूप से शेष गुणाओं की गणना कर सकते हैं और गणना कर सकते हैं (B/C-D/A)*A*Cकि आवश्यक परिणाम क्या है।

ध्यान दें, यदि आपका इनपुट बहुत छोटा हो सकता है , B/Cया D/Aओवरफ्लो हो सकता है। यदि यह संभव है, तो इनपुट निरीक्षण के अनुसार अधिक जटिल जोड़तोड़ की आवश्यकता हो सकती है।


2
यह काम नहीं करेगा क्योंकि पूर्णांक विभाजन जानकारी खो देता है (परिणाम का अंश)
ofir

@ ठीक है कि सही है, हालांकि आप केक नहीं खा सकते हैं और इसे अछूता नहीं छोड़ सकते हैं। आपको या तो सटीक या अतिरिक्त संसाधनों का उपयोग करके भुगतान करना होगा (जैसे आपने अपने उत्तर में सुझाव दिया है)। मेरा उत्तर गणितीय प्रकृति का है, जबकि आपका कंप्यूटर-उन्मुख है। परिस्थितियों के आधार पर प्रत्येक सही हो सकता है।
SomeWittyUsername

2
आप सही हैं - मुझे इसे हल करना चाहिए था - गणित सही होने के बजाय एक सटीक परिणाम नहीं देगा। हालांकि, ऐसे मामलों में ध्यान दें कि प्रश्न प्रस्तुतकर्ता (उदाहरण के लिए उदाहरण में प्रश्न) में रुचि रखने की संभावना है, त्रुटि संभवतः आश्चर्यजनक रूप से बड़ी होगी - किसी भी व्यावहारिक अनुप्रयोग के लिए स्वीकार्य से अधिक बड़ी हो सकती है। किसी भी मामले में - यह एक व्यावहारिक जवाब था और मुझे उस भाषा का उपयोग नहीं करना चाहिए था।
Ofir

@ यदि मुझे नहीं लगता कि आपकी भाषा अनुचित थी। ओपी ने स्पष्ट रूप से एक "सही" गणना का अनुरोध किया था, न कि चरम संसाधन बाधाओं के तहत प्रदर्शन के लिए सटीक खोना।
user4815162342

1

चुनें K = a big number(उदाहरण के लिए K = A - sqrt(A))

A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A-C+B-D); // Avoid overflow.

क्यों?

(A-K)*(B-K) = A*B - K*(A+B) + K^2
(C-K)*(D-K) = C*D - K*(C+D) + K^2

=>
(A-K)*(B-K) - (C-K)*(D-K) = A*B - K*(A+B) + K^2 - {C*D - K*(C+D) + K^2}
(A-K)*(B-K) - (C-K)*(D-K) = A*B - C*D - K*(A+B) + K*(C+D) + K^2 - K^2
(A-K)*(B-K) - (C-K)*(D-K) = A*B - C*D - K*(A+B-C-D)

=>
A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A+B-C-D)

=>
A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A-C+B-D)

ध्यान दें कि क्योंकि A, B, C और D बड़ी संख्या हैं, इस प्रकार A-Cऔर B-Dछोटी संख्या हैं।


आप व्यावहारिक में K कैसे चुनते हैं ? इसके अलावा, K * (A-C + BD) अभी भी ओवरफ्लो हो सकता है।
ylc

@ylc: K = sqrt (A) चुनें, A-C+B-Dयह एक छोटी संख्या नहीं है। क्योंकि A, B, C और D बड़ी संख्या हैं, इस प्रकार AC छोटी संख्या है।
अमीर सानीयन

यदि आप K = sqrt (A) चुनते हैं , तो (AK) * (BK) फिर से ओवरफ्लो हो सकता है।
ylc

@ylc: ठीक है! मैं इसे बदल देता हूं A - sqrt(A):)
अमीर सानीयन

तब K * (A-C + BD) ओवरफ्लो हो सकता है।
ylc
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.