C ++ में बड़ा int कैसे लागू करें


80

मैं सी ++ में एक बड़ी इंट क्लास को प्रोग्रामिंग एक्सरसाइज के रूप में लागू करना चाहता हूं - एक ऐसा क्लास जो एक लंबे इंट से बड़े नंबरों को हैंडल कर सके। मुझे पता है कि वहाँ पहले से ही कई खुले स्रोत कार्यान्वयन हैं, लेकिन मैं अपना खुद का लिखना चाहूंगा। मैं महसूस करने की कोशिश कर रहा हूं कि सही दृष्टिकोण क्या है।

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

मैं वास्तविक कामकाजी कोड के विपरीत एक सामान्य दृष्टिकोण और सलाह की तलाश कर रहा हूं।


4
पहली बात - अंक तार ठीक हैं, लेकिन आधार 2 ^ 32 (4 बिलियन विषम विशिष्ट अंक) के संदर्भ में सोचें। शायद इन दिनों आधार 2 ^ 64 भी हो। दूसरा, हमेशा अहस्ताक्षरित पूर्णांक "अंकों" के साथ काम करें। आप स्वयं हस्ताक्षरित बड़े पूर्णांकों के लिए टोमस पूरक कर सकते हैं, लेकिन यदि आप हस्ताक्षर किए गए पूर्णांक के साथ अपने अतिप्रवाह हैंडलिंग आदि को करने की कोशिश करते हैं, तो आप मानकों-अपरिभाषित बहावी मुद्दों में भाग लेंगे।
स्टीव 314

3
जैसा कि एल्गोरिदम के लिए - एक बुनियादी पुस्तकालय के लिए, जो आपने स्कूल में सीखा है वह सही है।
स्टीव 314

1
यदि आप स्वयं बहुआयामी गणित का प्रदर्शन करना चाहते हैं, तो मेरा सुझाव है कि आप डोनाल्ड नॉथ के आर्ट ऑफ़ कंप्यूटर प्रोग्रामिंग पर एक नज़र डालें । मेरा मानना ​​है कि वॉल्यूम II, सेमिन्युमेरिकल एल्गोरिदम, अध्याय 4, मल्टीपल प्रिसिजन अरिथमेटिक, क्या आप में रुचि रखते हैं। यह भी देखें कि C ++ में 2 मनमाने आकार के पूर्णांक कैसे जोड़े जाएं? , जो कुछ सी ++ लाइब्रेरी और ओपनएसएसएल के लिए कोड प्रदान करता है।
jww

जवाबों:


37

बड़े इंट क्लास के लिए विचार करने योग्य बातें:

  1. गणितीय ऑपरेटर: +, -, /, *,% यह मत भूलो कि आपकी कक्षा ऑपरेटर के दोनों ओर हो सकती है, कि ऑपरेटरों को जंजीर दी जा सकती है, कि ऑपरेटर में से एक इंट, फ्लोट, डबल आदि हो सकता है। ।

  2. I / O ऑपरेटर: >>, << यह वह जगह है जहां आप यह पता लगाते हैं कि उपयोगकर्ता इनपुट से अपनी कक्षा को ठीक से कैसे बनाया जाए, और इसे आउटपुट के लिए कैसे प्रारूपित किया जाए।

  3. रूपांतरण / कास्ट: यह पता लगाएं कि आपके बड़े इंट क्लास किस प्रकार / कक्षाओं में परिवर्तनीय होने चाहिए, और रूपांतरण को ठीक से कैसे प्रबंधित करें। एक त्वरित सूची में डबल और फ्लोट शामिल होंगे, और इसमें int (उचित सीमा की जाँच के साथ) और जटिल शामिल हो सकते हैं (यह मानते हुए कि यह सीमा को संभाल सकता है)।


1
ऑपरेटरों को करने के लिए मुहावरेदार तरीकों के लिए यहां देखें ।
मूविंग डक

5
पूर्णांक के लिए, ऑपरेटर << और >> बिट-शिफ्ट ऑपरेशन हैं। उन्हें I / O के रूप में व्याख्या करना खराब डिजाइन होगा।
डेव

3
@ क्या: सिवाय इसके कि यह मानक C ++ का उपयोग करने के लिए operator<<और I / O के लिए s के operator>>साथ है iostream

9
@ क्या आप अभी भी धाराओं / के लिए I / O के साथ-साथ बिट-शिफ्ट संचालन के लिए << और >> परिभाषित कर सकते हैं ...
Miguel.martin

46

एक मजेदार चुनौती। :)

मुझे लगता है कि आप मनमाने ढंग से लंबाई के पूर्णांक चाहते हैं। मैं निम्नलिखित दृष्टिकोण का सुझाव देता हूं:

डेटाटाइप "इंट" के द्विआधारी प्रकृति पर विचार करें। चीजों को जोड़ने पर आपके सीपीयू में सर्किट क्या करते हैं, इसका अनुकरण करने के लिए सरल बाइनरी ऑपरेशन का उपयोग करने के बारे में सोचें। यदि आप अधिक गहराई से रुचि रखते हैं, तो आधे हिस्से और पूर्ण-जोड़ने वालों पर इस विकिपीडिया लेख को पढ़ने पर विचार करें । आप ऐसा ही कुछ कर रहे होंगे, लेकिन आप निम्न स्तर के रूप में नीचे जा सकते हैं - लेकिन आलसी होने के नाते, मैंने सोचा कि मैं बस आगे बढ़ना चाहता हूं और एक सरल समाधान भी ढूंढ सकता हूं।

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

template< class BaseType >
class BigInt
{
typedef typename BaseType BT;
protected: std::vector< BaseType > value_;
};

यदि आप एक निश्चित आकार के वेक्टर बनाना चाहते हैं और इसे प्रचारित करना चाहते हैं तो आप इस पर विचार कर सकते हैं। कारण यह है कि विभिन्न कार्यों के लिए, आपको वेक्टर के प्रत्येक तत्व - ओ (एन) से गुजरना होगा। आप यह जानना चाह सकते हैं कि एक ऑपरेशन कितना जटिल है और एक निश्चित एन ही करता है।

लेकिन अब संख्याओं पर काम करने के लिए कुछ एल्गोरिदम। आप इसे तर्क-स्तर पर कर सकते हैं, लेकिन हम परिणामों की गणना करने के लिए उस जादुई CPU शक्ति का उपयोग करेंगे। लेकिन हम हाफ- और फुलएडर्स के लॉजिक-इलस्ट्रेशन से जो कुछ लेंगे, वह है कि यह किस तरह से किया जाता है। एक उदाहरण के रूप में, विचार करें कि आप + = ऑपरेटर को कैसे लागू करेंगे । BigInt में प्रत्येक संख्या के लिए <> :: value_, आप उन्हें जोड़ेंगे और देखेंगे कि क्या परिणाम कुछ प्रकार के कैरी का उत्पादन करता है। हम इसे थोड़ा समझदार नहीं करेंगे, लेकिन हमारे बेसटाइप की प्रकृति पर भरोसा करें (यह लंबे या इंट या कम या जो भी हो): यह ओवरफ्लो होता है।

निश्चित रूप से, यदि आप दो संख्याएँ जोड़ते हैं, तो परिणाम उन संख्याओं में से एक से अधिक होना चाहिए, है ना? यदि ऐसा नहीं है, तो परिणाम बह निकला।

template< class BaseType >
BigInt< BaseType >& BigInt< BaseType >::operator += (BigInt< BaseType > const& operand)
{
  BT count, carry = 0;
  for (count = 0; count < std::max(value_.size(), operand.value_.size(); count++)
  {
    BT op0 = count < value_.size() ? value_.at(count) : 0, 
       op1 = count < operand.value_.size() ? operand.value_.at(count) : 0;
    BT digits_result = op0 + op1 + carry;
    if (digits_result-carry < std::max(op0, op1)
    {
      BT carry_old = carry;
      carry = digits_result;
      digits_result = (op0 + op1 + carry) >> sizeof(BT)*8; // NOTE [1]
    }
    else carry = 0;
  }

  return *this;
}
// NOTE 1: I did not test this code. And I am not sure if this will work; if it does
//         not, then you must restrict BaseType to be the second biggest type 
//         available, i.e. a 32-bit int when you have a 64-bit long. Then use
//         a temporary or a cast to the mightier type and retrieve the upper bits. 
//         Or you do it bitwise. ;-)

अन्य अंकगणितीय ऑपरेशन अनुरूप होते हैं। हेक, आप भी stl- फंक्शनलर्स का उपयोग कर सकते हैं std :: plus and std :: minus, std :: times और std :: divide, ..., लेकिन कैरी को माइंड करें। :) आप अपने प्लस और माइनस ऑपरेटरों का उपयोग करके गुणन और विभाजन को भी लागू कर सकते हैं, लेकिन यह बहुत धीमा है, क्योंकि इससे आपको प्रत्येक कॉल में पहले से प्लस और माइनस करने के लिए पहले से कॉल किए गए परिणामों की पुनर्गणना होगी। इस सरल कार्य के लिए बहुत सारे अच्छे एल्गोरिदम हैं, विकिपीडिया या वेब का उपयोग करें

और निश्चित रूप से, आपको मानक ऑपरेटरों को लागू करना चाहिए जैसे कि operator<<(बस प्रत्येक मूल्य को मूल्य में_ को n बिट्स के लिए बाईं ओर स्थानांतरित कर दें value_.size()-1... ओह से शुरू करें और कैरी को याद रखें :), operator<- आप यहां थोड़ा अनुकूलन भी कर सकते हैं, जाँच कर सकते हैं size()पहले के साथ अंकों की संख्या । और इसी तरह। फिर अपनी कक्षा को उपयोगी बनायें, दोस्ती से std :: ostream operator<<

आशा है कि यह दृष्टिकोण सहायक है!


6
"int" (जैसा कि हस्ताक्षरित है) एक बुरा विचार है। ओवरफ्लो पर अपरिभाषित मानक, तर्क को सही तरीके से प्राप्त करने के लिए कठिन (यदि असंभव नहीं है) कम से कम आंशिक रूप से बनाता है। यह बहुत आसान है, हालांकि, अहस्ताक्षरित पूर्णांकों के साथ ट्यूलोस पूरक में काम करना, जहां अतिप्रवाह व्यवहार को सख्ती से परिभाषित किया गया है क्योंकि modulo 2 ^ n परिणाम।
स्टीव 314

29

इस पर एक पूर्ण खंड है: [कंप्यूटर प्रोग्रामिंग का कला, खंड ।2: अर्धसूत्रीविभाजन एल्गोरिदम, खंड 4.3 एकाधिक परिशुद्धता अंकगणित, पीपी। 265-318 (ed.3)]। आपको अध्याय 4, अंकगणित में अन्य रोचक सामग्री मिल सकती है।

यदि आप वास्तव में किसी अन्य कार्यान्वयन को देखना नहीं चाहते हैं, तो क्या आपने यह सोचा है कि आप क्या सीखने के लिए बाहर हैं? वहाँ असंख्य गलतियाँ की जानी हैं और उन को उजागर करना शिक्षाप्रद है और खतरनाक भी है। गंभीर कम्प्यूटेशनल अर्थव्यवस्थाओं की पहचान करने और गंभीर प्रदर्शन समस्याओं से बचने के लिए उचित भंडारण संरचनाएं रखने में भी चुनौतियां हैं।

आपके लिए एक चुनौती का सवाल: आप अपने कार्यान्वयन का परीक्षण कैसे करना चाहते हैं और आप यह कैसे प्रदर्शित करना चाहते हैं कि यह अंकगणित सही है?

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

मज़े करो!


2
कुछ संदर्भ कार्यान्वयन के साथ तुलना करने से आपको आगे कोई दिक्कत नहीं होगी, क्योंकि तब आपको एक और समस्या होती है: यदि संदर्भ कार्यान्वयन भी सही है तो परीक्षण कैसे करें? समान समस्या सामान्य रूप से परीक्षण ज्ञान के साथ है: यदि एक आदमी को दूसरे का परीक्षण करना है, तो कौन पूर्व का परीक्षण करेगा? इस समस्या से बाहर निकलने का कोई रास्ता नहीं है, एक लंबे समय से पहले आविष्कार किया गया था: स्वयंसिद्धों से साबित करना। यदि स्वयंसिद्धों के सेट को सही माना जाता है (कोई विरोधाभास नहीं है), और तर्क के नियमों के अनुसार प्रमाण ठीक से प्राप्त होता है, तो यह गलत नहीं हो सकता है, यहां तक ​​कि अनंत संख्या में मामलों के लिए कोई भी संभवतः परीक्षण नहीं कर सकता है।
सासक्यू


5

एक बार जब आपके पास किसी सरणी में संख्या के अंक हो जाते हैं, तो आप इसके अलावा और गुणा कर सकते हैं, जैसे आप उन्हें लंबे समय तक करेंगे।


4

यह मत भूलो कि आपको अपने आप को 0-9 अंकों के रूप में प्रतिबंधित करने की आवश्यकता नहीं है, अर्थात अंकों के रूप में बाइट्स (0-255) का उपयोग करें और आप अभी भी लंबे हाथ अंकगणित कर सकते हैं जैसा कि आप दशमलव अंकों के लिए करेंगे। तुम भी लंबे समय की एक सरणी का उपयोग कर सकते हैं।


यदि आप दशमलव में संख्याओं का प्रतिनिधित्व करना चाहते हैं (यानी मात्र नश्वर के लिए), तो 0-9 प्रति निबल एल्गोरिथ्म आसान है। बस भंडारण छोड़ दो।
dmckee --- पूर्व-मध्यस्थ बिल्ली का बच्चा

आप अपने नियमित बाइनरी समकक्षों की तुलना में बीसीडी एल्गोरिदम को करना आसान मानते हैं?
ग्रहण

2
AFAIK बेस 10 का उपयोग अक्सर किया जाता है क्योंकि आधार 255 में बड़ी संख्या को बदलना (या 10 की शक्ति से कुछ भी नहीं) / से 10 तक बेस महंगा है, और आपके प्रोग्राम का इनपुट और आउटपुट आमतौर पर बेस 10 में होगा
तोबी

@Tobi: मैं शायद आधार 10000 रखने की सलाह दूंगा unsigned, जो कि तेज IO है, और इसके साथ गुणा करना आसान है, नकारात्मक पक्ष यह है कि यह भंडारण स्थान का 59% बर्बाद करता है। मैं अधिक उन्नत शिक्षा के लिए आधार (2 ^ 32) की सिफारिश करता हूं, जो कि IO को छोड़कर सब कुछ के लिए आधार 10/10000 से बहुत अधिक तेज है, लेकिन गुणा / भाग को लागू करने के लिए बहुत कठिन है।
मूिंग डक

3

मुझे यकीन नहीं है कि एक स्ट्रिंग का उपयोग करने का सही तरीका है - हालांकि मैंने कभी भी खुद को कोड नहीं लिखा है, मुझे लगता है कि एक आधार संख्यात्मक प्रकार की एक सरणी का उपयोग करना बेहतर समाधान हो सकता है। विचार यह है कि आप बस उसी तरह का विस्तार करेंगे जो आप पहले ही प्राप्त कर चुके हैं जैसे सीपीयू एक बिट को पूर्णांक में बढ़ाता है।

उदाहरण के लिए, यदि आपके पास एक संरचना है

typedef struct {
    int high, low;
} BiggerInt;

फिर आप प्रत्येक "अंक" (इस मामले में उच्च और निम्न) पर मूल संचालन कर सकते हैं, अतिप्रवाह के प्रति सावधान रहना:

BiggerInt add( const BiggerInt *lhs, const BiggerInt *rhs ) {
    BiggerInt ret;

    /* Ideally, you'd want a better way to check for overflow conditions */
    if ( rhs->high < INT_MAX - lhs->high ) {
        /* With a variable-length (a real) BigInt, you'd allocate some more room here */
    }

    ret.high = lhs->high + rhs->high;

    if ( rhs->low < INT_MAX - lhs->low ) {
        /* No overflow */
        ret.low = lhs->low + rhs->low;
    }
    else {
        /* Overflow */
        ret.high += 1;
        ret.low = lhs->low - ( INT_MAX - rhs->low ); /* Right? */
    }

    return ret;
}

यह एक सादगीपूर्ण उदाहरण का एक सा है, लेकिन यह काफी स्पष्ट होना चाहिए कि एक संरचना का विस्तार कैसे किया जाए, जिसका उपयोग करने वाले आधार संख्या वर्ग का एक चर संख्या थी।


स्ट्रिंग द्वारा ओपी का मतलब है एक स्ट्रिंग जिसमें आप अपने संख्यात्मक प्रतिनिधित्व (जो भी आधार के तहत) चाहते हैं और बिगआईंट को मूल्य के साथ आरंम्भित करना।
केटीसी

बड़ा पूर्णांक रखने के लिए STLPLUS स्ट्रिंग का उपयोग करें।
lsalamon

2

4 वीं कक्षा के माध्यम से आपने 1 में सीखा एल्गोरिदम का उपयोग करें।
लोगों के कॉलम के साथ शुरू करें, फिर दसियों, और आगे।


2

जैसे अन्य लोगों ने कहा, इसे पुराने जमाने के लंबे-चौड़े तरीके से करें, लेकिन यह सब बेस 10 में करने से दूर रहें। मेरा सुझाव है कि यह सब बेस 65536 में करना चाहिए, और लॉन्ग के एक अरेंजमेंट में चीजों को स्टोर करना चाहिए।


1

यदि आपका लक्ष्य आर्किटेक्चर बीसीडी (बाइनरी कोडेड दशमलव) संख्याओं के प्रतिनिधित्व का समर्थन करता है, तो आपको लॉन्गहैंड गुणा / जोड़ के लिए कुछ हार्डवेयर समर्थन मिल सकते हैं जो आपको करने की आवश्यकता है। बीसीडी इंस्ट्रक्शन का उत्सर्जन करने के लिए कंपाइलर प्राप्त करना कुछ ऐसा है जिस पर आपको पढ़ना होगा ...

मोटोरोला 68K श्रृंखला के चिप्स में यह था। ऐसा नहीं कि मैं कड़वा हूँ या कुछ भी।


0

मेरी शुरुआत 31 बिट्स और 32n'd ओवरफ्लो के रूप में करते हुए पूर्णांक की एक मनमानी आकार की सरणी होगी।

स्टार्टर ऑप 2 के पूरक का उपयोग करके ADD, और फिर, MAKE-NEGATIVE होगा। उसके बाद, घटाव तुच्छ रूप से बहता है, और एक बार जब आप जोड़ / उप, बाकी सब कुछ करने योग्य है।

संभवतः अधिक परिष्कृत दृष्टिकोण हैं। लेकिन यह डिजिटल तर्क से भोली दृष्टिकोण होगा।


0

कुछ इस तरह से लागू करने की कोशिश कर सकता है:

http://www.docjar.org/html/api/java/math/BigInteger.java.html

आपको केवल एक अंक 0 - 9 के लिए 4 बिट्स की आवश्यकता होगी

तो एक Int मान 8 अंक तक की अनुमति देगा। मैंने तय किया कि मैं एक चार वर्णों के साथ रहना चाहूंगा, इसलिए मैं मेमोरी का दोगुना उपयोग करता हूं लेकिन मेरे लिए यह केवल 1 बार उपयोग किया जा रहा है।

इसके अलावा जब सभी अंकों को एक ही इंट में रखा जाता है तो यह इसे और अधिक जटिल कर देता है और अगर कुछ भी इसे धीमा कर सकता है।

मेरे पास कोई गति परीक्षण नहीं है, लेकिन BigInteger के जावा संस्करण को देखकर ऐसा लगता है कि यह बहुत भयानक काम कर रहा है।

मेरे लिए मैं नीचे करता हूं

//Number = 100,000.00, Number Digits = 32, Decimal Digits = 2.
BigDecimal *decimal = new BigDecimal("100000.00", 32, 2);
decimal += "1000.99";
cout << decimal->GetValue(0x1 | 0x2) << endl; //Format and show decimals.
//Prints: 101,000.99

ओपी ने कभी नहीं कहा / वह दशमलव अंकों पर ध्यान केंद्रित करना चाहता है।
ईनपोकलूम

-1

अपने पूर्णांक की स्ट्रिंग से 48 घटाएं और बड़े अंकों की संख्या प्राप्त करने के लिए प्रिंट करें। फिर मूल गणितीय कार्य करें। अन्यथा मैं पूरा समाधान प्रदान करूंगा।

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