क्या मुझे चुकता अंकों को स्पष्ट रूप से ऋणात्मक संख्याओं या शून्य को संभालने की आवश्यकता है?


220

मैंने हाल ही में अपनी कक्षा में एक परीक्षण किया था। समस्याओं में से एक निम्नलिखित था:

संख्या n को देखते हुए , C / C ++ में एक फ़ंक्शन लिखें जो संख्या वर्ग के अंकों का योग लौटाता है । (निम्नलिखित महत्वपूर्ण है)। रेंज के एन [-, 10 ^ 7 (10 ^ 7)] है। उदाहरण: यदि n = 123, आपके फ़ंक्शन को 14 (1 ^ 2 + 2 ^ 2 + 3 ^ 2 = 14) वापस करना चाहिए।

यह वह फ़ंक्शन है जो मैंने लिखा था:

int sum_of_digits_squared(int n) 
{
    int s = 0, c;

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

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

int sum_of_digits_squared(int n) 
 {
    int s = 0, c;

    if (n == 0) {      //
        return 0;      //
    }                  //
                       // THIS APPARENTLY SHOULD'VE 
    if (n < 0) {       // BEEN IN THE FUNCTION FOR IT
        n = n * (-1);  // TO BE CORRECT
    }                  //

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

इसके लिए तर्क यह है कि संख्या n रेंज [- (10 ^ 7), 10 ^ 7] में है, इसलिए यह ऋणात्मक संख्या हो सकती है। लेकिन मैं यह नहीं देखता कि फंक्शन का मेरा अपना संस्करण कहां विफल है। अगर मैं सही ढंग से समझूं, तो इसका अर्थ while(n)है while(n != 0), नहीं while (n > 0) , इसलिए मेरे कार्य के संस्करण में संख्या n लूप में प्रवेश करने में विफल नहीं होगी। यह सिर्फ एक ही काम करेगा।

फिर, मैंने घर पर अपने कंप्यूटर पर फ़ंक्शन के दोनों संस्करणों की कोशिश की और मुझे उन सभी उदाहरणों के लिए समान उत्तर मिले जो मैंने कोशिश की थी। तो, sum_of_digits_squared(-123)के बराबर है sum_of_digits_squared(123)(जो फिर से, के बराबर है 14) (यहां तक ​​कि विस्तार के बिना कि मुझे जाहिरा तौर पर जोड़ा जाना चाहिए)। वास्तव में, यदि मैं स्क्रीन पर अंक की संख्या (कम से कम से सबसे बड़ी तक) में प्रिंट करने की कोशिश करता हूं, तो मुझे जो 123मिलता है 3 2 1और उस -123मामले में जो मुझे मिलता है -3 -2 -1(जो वास्तव में दिलचस्प है)। लेकिन इस समस्या में यह मायने नहीं रखता क्योंकि हम अंकों को चौकोर करते हैं।

तो, कौन गलत है?

संपादित करें : मेरा बुरा, मैं निर्दिष्ट करना भूल गया और यह नहीं जानता कि यह महत्वपूर्ण था। हमारी कक्षा और परीक्षणों में प्रयुक्त C का संस्करण C99 या नया होना चाहिए । इसलिए मुझे लगता है (टिप्पणियों को पढ़कर) कि मेरे संस्करण को किसी भी तरह से सही उत्तर मिलेगा।


120
n = n * (-1)लिखने का एक हास्यास्पद तरीका है n = -n; केवल एक अकादमिक भी इसके बारे में सोचेगा। अकेले निरर्थक कोष्ठक जोड़ें।
user207421

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

94
मुझे यह दिलचस्प लगता है कि "संख्या वर्ग के अंकों का योग" तीन (3) में पूरी तरह से अलग तरीके से व्याख्या की जा सकती है। (यदि संख्या 123 है, तो संभावित व्याख्याएं 18, 14 और 36 प्राप्त करती हैं।)
एंड्रियास रिजेब्रैंड

23
@ilkachachu: "संख्या वर्ग के अंकों का योग"। खैर, "संख्या वर्ग" स्पष्ट रूप से 123 ^ 2 = 15129 है, इसलिए "संख्या वर्ग के अंकों का योग" "15129 के अंकों का योग" है, जो स्पष्ट रूप से 1 + 5 + 1 + 2 + 9 है। = 18।
एंड्रियास रिब्रांडैंड

15
n = n * (-1)? Wut ??? आपके प्रोफेसर को जो दिख रहा है वह यह है: `n = -n '। C भाषा में एक शून्य माइनस ऑपरेटर है।
काज़

जवाबों:


245

एक चर्चा को सारांशित करना, जो टिप्पणियों में स्पष्ट हो रही है:

  • पहले से परीक्षण करने का कोई अच्छा कारण नहीं है n == 0while(n)परीक्षण उस मामले पूरी तरह से संभाल लेंगे।
  • यह संभावना है कि आपका शिक्षक अभी भी पहले के समय के लिए उपयोग किया जाता है, जब %नकारात्मक ऑपरेंड के परिणाम को अलग तरह से परिभाषित किया गया था। कुछ पुरानी प्रणालियों पर (सहित, विशेष रूप से, प्रारंभिक यूनिक्स एक पीडीपी -11 पर, जहां डेनिस रिची मूल रूप से सी विकसित किया a % bगया था ), इसका परिणाम हमेशा सीमा में था [0 .. b-1], जिसका अर्थ है कि -123% 10 था 7. ऐसी प्रणाली पर, परीक्षण अग्रिम में n < 0आवश्यक होगा।

लेकिन दूसरी गोली केवल पहले के समय पर लागू होती है। C और C ++ दोनों मानकों के वर्तमान संस्करणों में पूर्णांक विभाजन को 0 की ओर छोटा करने के लिए परिभाषित किया गया है, इसलिए यह पता चलता है कि n % 10नकारात्मक होने पर nभी आपको (संभवतः नकारात्मक) अंतिम अंक देने की गारंटी nहै।

तो सवाल का जवाब "का अर्थ क्या है while(n)?" है "के रूप में वास्तव में एक ही while(n != 0)" , और का जवाब "इस कोड को नकारात्मक के लिए ठीक से काम के साथ-साथ सकारात्मक रूप में विल n?" है "हाँ, किसी भी आधुनिक तहत, संकलक मानक-अनुरूप।" प्रश्न का उत्तर "फिर प्रशिक्षक ने इसे नीचे क्यों चिह्नित किया?" शायद यह है कि वे एक महत्वपूर्ण भाषा पुनर्वितरण के बारे में नहीं जानते हैं जो कि 1999 में C और 2010 में C ++ के लिए हुआ था।


39
"अग्रिम रूप से n == 0 के लिए परीक्षण करने का कोई अच्छा कारण नहीं है" - तकनीकी रूप से, यह सही है। लेकिन यह देखते हुए कि हम एक शिक्षण सेटिंग में एक प्रोफेसर के बारे में बात कर रहे हैं, वे संक्षिप्तता की सराहना कर सकते हैं जो हम करते हैं। n == 0कम से कम अतिरिक्त परीक्षा जोड़ना किसी भी पाठक के लिए तत्काल और पूरी तरह से स्पष्ट कर देता है कि उस मामले में क्या होता है। इसके बिना, पाठक को खुद को संतुष्ट करना होगा कि लूप वास्तव में छोड़ दिया गया है, और sलौटाया गया डिफ़ॉल्ट मान सही है।
इलकाचू

22
साथ ही, प्रोफेसर यह जानना चाहते हैं कि छात्र जागरूक है और उसने इस बारे में सोचा है कि फ़ंक्शन शून्य के इनपुट के साथ क्यों और कैसे व्यवहार करता है (अर्थात यह दुर्घटना से सही मूल्य वापस नहीं करता है )। वे ऐसे छात्रों से मिल सकते हैं जिन्हें इस बात का अंदाजा नहीं है कि उस स्थिति में क्या होगा, कि लूप शून्य समय चल सकता है, आदि। जब आप एक शिक्षण सेटिंग के साथ काम कर रहे हों, तो किसी भी धारणा और कोने के मामलों के बारे में अतिरिक्त स्पष्ट होना सबसे अच्छा है। ..
ilkachachu

36
@ilkkachu यदि यह मामला है, तो शिक्षक को एक कार्य सौंपना चाहिए जो इस तरह के परीक्षण को सही ढंग से काम करने के लिए आवश्यक है।
klutt

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

49
@ilkkachu उस तर्क से आपको निश्चित रूप से n = 1 और इतने पर परीक्षण जोड़ना चाहिए। वहाँ के बारे में कुछ खास नहीं है n=0। अनावश्यक शाखाओं और जटिलताओं का परिचय कोड को आसान नहीं बनाता है, यह कठिन बना देता है क्योंकि अब आपको न केवल यह दिखाना होगा कि सामान्य एल्गोरिथ्म सही है आपको सभी विशेष मामलों के बारे में अलग से सोचना होगा।
वू

107

आपका कोड पूरी तरह से ठीक है

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

पहला, यदि अलग-अलग चेक nशून्य है तो स्पष्ट रूप से पूरी तरह से अनावश्यक है और यह महसूस करना बहुत आसान है। ईमानदार होने के लिए, मैं वास्तव में आपके शिक्षकों की योग्यता पर सवाल उठाता हूं अगर उसे इस बारे में आपत्ति है। लेकिन मुझे लगता है कि हर कोई समय-समय पर एक मस्तिष्क गोज़ हो सकता है। हालाँकि, मुझे लगता है कि while(n)इसे बदल दिया जाना चाहिए while(n != 0)क्योंकि यह एक अतिरिक्त लाइन की लागत के बिना थोड़ा अतिरिक्त स्पष्टता जोड़ता है। हालांकि यह मामूली बात है।

दूसरा वाला थोड़ा अधिक समझ में आता है, लेकिन वह अभी भी गलत है।

यह C11 मानक 6.5.5.p6 कहता है:

यदि भागफल a / b प्रतिनिधित्व योग्य है, तो अभिव्यक्ति (a / b) * b + a% b बराबर होगी a; अन्यथा, a / b और% b दोनों का व्यवहार अपरिभाषित है।

फुटनोट कहता है:

इसे अक्सर "शून्य की ओर झुकाव" कहा जाता है।

शून्य साधन की ओर ट्रंकेशन कि के लिए निरपेक्ष मूल्य a/bहै के लिए निरपेक्ष मूल्य के बराबर (-a)/bसभी के लिए aऔर bजो बारी साधन में अपने कोड बिल्कुल ठीक है कि,।

मोदुलो आसान गणित है, लेकिन प्रतिवाद हो सकता है

हालाँकि, आपके शिक्षक के पास एक बिंदु है कि आपको सावधान रहना चाहिए, क्योंकि इस तथ्य को कि आप परिणाम को चुकता कर रहे हैं, वास्तव में यहाँ महत्वपूर्ण है। a%bउपरोक्त परिभाषा के अनुसार गणना करना आसान गणित है, लेकिन यह आपके अंतर्ज्ञान के खिलाफ जा सकता है। गुणन और विभाजन के लिए, परिणाम सकारात्मक है यदि ऑपरेंड्स के बराबर चिह्न हैं। लेकिन जब मोडुलो की बात आती है, तो परिणाम में पहले ऑपरेंड के समान संकेत होते हैं । दूसरा ऑपरेंड संकेत को प्रभावित नहीं करता है। उदाहरण के लिए, 7%3==1लेकिन (-7)%(-3)==(-1)

यहाँ एक स्निपेट प्रदर्शित किया गया है:

$ cat > main.c 
#include <stdio.h>

void f(int a, int b) 
{
    printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
           a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}

int main(void)
{
    int a=7, b=3;
    f(a,b);
    f(-a,b);
    f(a,-b);
    f(-a,-b);
}

$ gcc main.c -Wall -Wextra -pedantic -std=c99

$ ./a.out
a:  7 b:  3 a/b:  2 a%b:  1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a: -7 b:  3 a/b: -2 a%b: -1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a:  7 b: -3 a/b: -2 a%b:  1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a: -7 b: -3 a/b:  2 a%b: -1 (a%b)^2:  1 (a/b)*b+a%b==a:  true

इसलिए, विडंबना यह है कि आपके शिक्षक ने गलत होने से अपनी बात साबित की।

आपके शिक्षक का कोड त्रुटिपूर्ण है

हाँ, यह वास्तव में है। यदि इनपुट है INT_MINऔर आर्किटेक्चर दो का पूरक है और बिट पैटर्न जहां साइन बिट 1 है और सभी वैल्यू बिट्स 0 हैं, ट्रैप वैल्यू नहीं है (ट्रैप वैल्यू के बिना दो के पूरक का उपयोग करना बहुत सामान्य है) तो आपके शिक्षक का कोड अपरिभाषित व्यवहार करेगा लाइन पर n = n * (-1)। आपका कोड है - अगर कभी इतना थोड़ा - उससे बेहतर । और कोड को अनावश्यक जटिल बनाकर और बिल्कुल शून्य मान प्राप्त करके एक छोटे से बग को शुरू करने पर विचार करते हुए, मैं कहूंगा कि आपका कोड बेहतर है।

दूसरे शब्दों में, संकलन में जहां INT_MIN = -32768 (भले ही परिणामी फ़ंक्शन एक इनपुट प्राप्त नहीं कर सकता है <-32768 या> 32767), -32768 का वैध इनपुट अपरिभाषित व्यवहार का कारण बनता है, क्योंकि - (- 32768i16) का परिणाम 16-बिट पूर्णांक के रूप में व्यक्त नहीं किया जा सकता है। (वास्तव में, -32768 शायद गलत परिणाम का कारण नहीं बनेगा, क्योंकि - (- 32768i16) आमतौर पर -32768i16 का मूल्यांकन करता है, और आपका प्रोग्राम नकारात्मक संख्याओं को सही ढंग से संभालता है।) (SHRT_MIN संकलक के आधार पर -3288 या -32767 हो सकता है।)

लेकिन आपके शिक्षक ने स्पष्ट रूप से कहा कि nयह सीमा [-10 ^ 7 में हो सकती है; 10 ^ 7]। एक 16-बिट पूर्णांक बहुत छोटा है; आपको 32-बिट पूर्णांक [कम से कम] का उपयोग करना होगा। का उपयोग intकर अपने कोड को सुरक्षित करने के लिए लग सकता है, सिवाय इसके कि intएक 32-बिट पूर्णांक आवश्यक नहीं है। यदि आप 16-बिट आर्किटेक्चर के लिए संकलन करते हैं, तो आपके दोनों कोड स्निपेट त्रुटिपूर्ण हैं। लेकिन आपका कोड अभी भी बहुत बेहतर है क्योंकि यह परिदृश्य INT_MINउसके संस्करण के साथ ऊपर उल्लिखित बग को फिर से प्रस्तुत करता है । इससे बचने के लिए, आप longइसके बजाय लिख सकते हैं int, जो कि आर्किटेक्चर पर 32-बिट पूर्णांक है। ए longकी गारंटी है कि रेंज में किसी भी मूल्य को धारण करने में सक्षम होने के लिए [-2147483647; 2147483647]। C11 स्टैंडर्ड 5.2.4.2.1 LONG_MIN अक्सर होता है-2147483648लेकिन अधिकतम (हाँ, अधिकतम, यह एक नकारात्मक संख्या है) के लिए अनुमत मूल्य LONG_MINहै 2147483647

मैं आपके कोड में क्या बदलाव करूंगा?

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

  • चरों का नाम थोड़ा बेहतर हो सकता है, लेकिन यह एक छोटा कार्य है जिसे समझना आसान है, इसलिए यह कोई बड़ी बात नहीं है।
  • आप से हालत बदल सकता है nके लिए n!=0। शब्दार्थ, यह 100% समतुल्य है, लेकिन यह इसे थोड़ा स्पष्ट करता है।
  • जब तक यह केवल वहाँ उपयोग किया जाता है, तब तक लूप के अंदर c(जिसे मैंने नाम दिया था digit) की घोषणा को स्थानांतरित करें ।
  • longयह सुनिश्चित करने के लिए कि यह पूरे इनपुट सेट को संभाल सके तर्क प्रकार बदलें ।
int sum_of_digits_squared(long n) 
{
    long sum = 0;

    while (n != 0) {
        int digit = n % 10;
        sum += (digit * digit);
        n /= 10;
    }

    return sum;
}

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

  • चर को पूरी तरह sum += (digit * digit)से बदलें sum += ((n%10)*(n%10))और छोड़ें digit
  • digitनकारात्मक होने पर संकेत बदलें । लेकिन मैं दृढ़ता से कोड को और अधिक जटिल बनाने के खिलाफ सलाह दूंगा कि केवल एक चर नाम बनाने का मतलब है। यह एक बहुत मजबूत कोड गंध है।
  • एक अलग फ़ंक्शन बनाएँ जो अंतिम अंक निकालता है। int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }यह उपयोगी है यदि आप उस फ़ंक्शन को कहीं और उपयोग करना चाहते हैं।
  • cजैसा कि आप मूल रूप से करते हैं, वैसे ही इसे नाम दें । वह परिवर्तनशील नाम कोई उपयोगी जानकारी नहीं देता है, लेकिन दूसरी ओर, यह भ्रामक भी नहीं है।

लेकिन ईमानदार होने के लिए, इस बिंदु पर आपको अधिक महत्वपूर्ण कार्य करने के लिए आगे बढ़ना चाहिए। :)


19
यदि इनपुट है INT_MINऔर आर्किटेक्चर दो पूरक का उपयोग कर रहा है (जो बहुत सामान्य है) तो आपके शिक्षक कोड अपरिभाषित व्यवहार करेंगे। आउच। जो एक निशान छोड़ देगा। ;-)
एंड्रयू हेनल

5
यह उल्लेख किया जाना चाहिए कि इसके अलावा (a/b)*b + a%b ≡ a, ओपी का कोड इस तथ्य पर भी टिका है कि /शून्य की ओर गोल होता है, और वह (-c)*(-c) ≡ c*c। यह तर्क दिया जा सकता है कि अतिरिक्त चेक सभी मानक गारंटी के बावजूद उचित हैं, क्योंकि यह पर्याप्त रूप से गैर-स्पष्ट है। (बेशक यह समान रूप से अच्छी तरह से तर्क दिया जा सकता है कि प्रासंगिक मानक खंडों को जोड़ने वाली एक टिप्पणी होनी चाहिए, लेकिन शैली दिशानिर्देश अलग-अलग हैं।)
वामावर्त

7
@MartinRosenau आप कहते हैं "हो सकता है"। क्या आपको यकीन है कि यह वास्तव में हो रहा है या यह मानक या कुछ और द्वारा अनुमति दी गई है या आप सिर्फ अटकलें लगा रहे हैं?
klutt

6
@MartinRosenau: ठीक है, लेकिन उन स्विचों का उपयोग करने से यह C नहीं होगा। जीसीसी / क्लैंग में कोई स्विच नहीं है जो किसी भी आईएसए पर पूर्णांक विभाजन को तोड़ता है जिससे मैं परिचित हूं। भले ही साइन बिट को अनदेखा करना शायद निरंतर विभाजक के लिए सामान्य गुणात्मक व्युत्क्रम का उपयोग करके एक गति प्रदान कर सकता है। (लेकिन सभी ISAs मैं परिचित हूं कि एक हार्डवेयर डिवीजन निर्देश है इसे C99 तरीके से लागू करें, शून्य की ओर बढ़ते हुए, इसलिए C %और /ऑपरेटर बस idivx86, या sdivARM या जो भी हो, पर संकलित कर सकते हैं । फिर भी, यह बहुत से असंबंधित है। संकलित-समय-निरंतर विभाजकों के लिए तेज कोड-जीन)
पीटर कॉर्ड्स

5
@ टोंकी AFIK, कि कैसे यह आमतौर पर हल है, लेकिन मानक के अनुसार यह यूबी है।
klutt

20

मैं पूरी तरह से या तो आपके संस्करण या आपके शिक्षक की तरह नहीं हूं। आपके शिक्षक का संस्करण अतिरिक्त परीक्षण करता है जिसे आप सही ढंग से इंगित करते हैं कि अनावश्यक हैं। सी का मॉड ऑपरेटर एक उचित गणितीय मॉड नहीं है: एक नकारात्मक संख्या मॉड 10 एक नकारात्मक परिणाम देगा (उचित गणितीय मापांक हमेशा गैर-नकारात्मक होता है)। लेकिन जब से आप इसे वैसे भी चुकता कर रहे हैं, कोई फर्क नहीं पड़ता।

लेकिन यह स्पष्ट नहीं है, इसलिए मैं आपके कोड को आपके शिक्षक के चेक से नहीं जोड़ूंगा, लेकिन एक बड़ी टिप्पणी है जो बताती है कि यह क्यों काम करता है। उदाहरण के लिए:

/ * नोट: यह नकारात्मक मानों के लिए काम करता है, क्योंकि मापांक चुकता हो जाता है * /


9
सी के %सर्वश्रेष्ठ को शेष कहा जाता है , क्योंकि यह वह है, यहां तक ​​कि हस्ताक्षरित प्रकारों के लिए भी।
पीटर कॉर्ड्स

15
स्क्वैरिंग महत्वपूर्ण है, लेकिन मुझे लगता है कि यह स्पष्ट हिस्सा है। जो बताया जाना चाहिए वह यह है (जैसे) -7 % 10 वास्तव में-7 3 के बजाय होगा
याकूब रायहल

5
"उचित गणितीय मापांक" का कोई मतलब नहीं है। सही शब्द है "यूक्लिडियन मोडुलो" (प्रत्यय मन!) और यह वास्तव में सी का %ऑपरेटर नहीं है।
Jan Hudec

मुझे यह उत्तर पसंद है क्योंकि यह मोडुलो की व्याख्या करने के कई तरीकों के सवाल को सुलझाता है। मौका / व्याख्या करने के लिए कभी भी इस तरह की चीज न छोड़ें। यह कोड गोल्फ नहीं है।
हार्पर - मोनिका

1
"उचित गणितीय मापांक हमेशा गैर-नकारात्मक होता है" - वास्तव में नहीं। मोडुलो ऑपरेशन का परिणाम एक समतुल्य वर्ग है , लेकिन परिणाम को उस वर्ग से संबंधित सबसे छोटी गैर-ऋणात्मक संख्या के रूप में माना जाता है।
klutt

10

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

आधुनिक C ++ में (ध्यान दें: मुझे वास्तव में यह नहीं पता है कि C इस पर कहां खड़ा है), आपका प्रोफेसर दोनों गणनाओं में गलत प्रतीत होता है।

पहला हिस्सा यह है:

if (n == 0) {
        return 0;
}

C ++ में, यह मूल रूप से समान है :

if (!n) {
        return 0;
}

इसका मतलब है कि आपका समय कुछ इस तरह के बराबर है:

while(n != 0) {
    // some implementation
}

इसका मतलब यह है कि जब से आप किसी भी तरह से निष्पादित नहीं करेंगे जब आप केवल तभी बाहर निकल रहे हैं, तो वास्तव में इसे यहाँ रखने का कोई कारण नहीं है, क्योंकि आप लूप के बाद क्या कर रहे हैं और अगर वैसे भी बराबर हैं। हालाँकि मुझे यह कहना चाहिए कि ये किसी कारण से अलग थे, अगर आपको ऐसा करना होगा तो।

जब तक मैं गलत नहीं हूँ, तो वास्तव में, यह कथन विशेष रूप से उपयोगी नहीं है।

दूसरा हिस्सा वह है जहां चीजें बालों वाली हो जाती हैं:

if (n < 0) {
    n = n * (-1);
}  

इस मुद्दे का दिल एक नकारात्मक संख्या आउटपुट के मापांक के आउटपुट का है।

आधुनिक C ++ में, यह ज्यादातर अच्छी तरह से परिभाषित किया गया लगता है :

बाइनरी / ऑपरेटर भागफल की पैदावार करता है, और बाइनरी% ऑपरेटर दूसरी द्वारा पहली अभिव्यक्ति के विभाजन से शेष पैदावार देता है। यदि / या% का दूसरा संचालन शून्य है तो व्यवहार अपरिभाषित है। अभिन्न ऑपरेंड के लिए / ऑपरेटर बीजीय भाग को किसी भी आंशिक भाग के साथ छोड़ देता है; यदि भागफल a / b परिणाम के प्रकार में प्रतिनिधित्व करने योग्य है, (a / b) * b + a% b बराबर है।

और बादमें:

यदि दोनों ऑपरेंड नॉनजेनेटिव हैं तो शेष नॉनजेगेटिव हैं; यदि नहीं, तो शेष का चिह्न कार्यान्वयन-परिभाषित है।

जैसा कि उद्धृत उत्तर का पोस्टर सही ढंग से बताता है, इस समीकरण का महत्वपूर्ण हिस्सा यहीं है:

(a / b) * b + a% b

अपने मामले का उदाहरण लेते हुए, आपको कुछ इस तरह मिलेगा:

-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3 

एकमात्र पकड़ यह है कि अंतिम पंक्ति:

यदि दोनों ऑपरेंड नॉनजेनेटिव हैं तो शेष नॉनजेगेटिव हैं; यदि नहीं, तो शेष का चिह्न कार्यान्वयन-परिभाषित है।

इसका मतलब है कि इस तरह के मामले में, केवल संकेत कार्यान्वयन-परिभाषित किया गया लगता है। आपके मामले में यह समस्या नहीं होनी चाहिए क्योंकि, आप इस मूल्य को वैसे भी चुकता कर रहे हैं।

उस ने कहा, यह ध्यान रखें कि यह आवश्यक रूप से C ++ के पुराने संस्करणों या C99 पर लागू नहीं होता है। यदि वह है जो आपके प्रोफेसर का उपयोग कर रहा है, तो यह हो सकता है।


संपादित करें: नहीं, मैं गलत हूँ। ऐसा लगता है कि C99 या बाद में भी ऐसा ही होगा :

C99 के लिए आवश्यक है कि जब a / b प्रतिनिधित्व योग्य हो:

(a / b) * b + a% b बराबर होगा a

और दूसरी जगह :

जब पूर्णांकों को विभाजित किया जाता है और विभाजन अक्षम होता है, यदि दोनों संचालक सकारात्मक होते हैं / संचालक का परिणाम बीजगणितीय भागफल की तुलना में सबसे बड़ा पूर्णांक होता है और% संचालक का परिणाम सकारात्मक होता है। यदि या तो ऑपरेंड नकारात्मक है, तो क्या / ऑपरेटर का परिणाम बीजगणितीय भागफल की तुलना में सबसे बड़ा पूर्णांक है या बीजगणितीय भागफल की तुलना में सबसे छोटा पूर्णांक कार्यान्वयन-परिभाषित है, जैसा कि% ऑपरेटर के परिणाम का संकेत है। यदि भागफल a / b प्रतिनिधित्व योग्य है, तो अभिव्यक्ति (a / b) * b + a% b बराबर होगी।

क्या एएनएसआई सी या आईएसओ सी निर्दिष्ट करता है कि ५% १० क्या होना चाहिए?

तो हाँ। C99 में भी, यह आपको प्रभावित नहीं करता है। समीकरण समान है।


1
आपके द्वारा उद्धृत अंश इस उत्तर का समर्थन नहीं करते हैं। "शेष का चिह्न कार्यान्वयन है परिभाषित" का मतलब यह नहीं है (-1)%10कि उत्पादन -1या कर सकता है 1; इसका मतलब है कि यह उत्पादन -1कर सकता है या 9, और बाद वाले मामले (-1)/10में उत्पादन करेगा -1और ओपी का कोड कभी समाप्त नहीं होगा।
स्टूअबेसिक

क्या आप इसके लिए किसी स्रोत की ओर संकेत कर सकते हैं? मुझे विश्वास करने में बहुत कठिन समय है (-1) / 10 -1। यह 0. होना चाहिए। (-1)% 10 = 9 गवर्निंग समीकरण का उल्लंघन करता है।
चिपस्टर

1
@Chipster, के साथ शुरू (a/b)*b + a%b == a, तो दे a=-1; b=10, दे (-1/10)*10 + (-1)%10 == -1। अब, अगर -1/10वास्तव में गोल नीचे हो जाता है (-inf की ओर), तो हमारे पास है (-1/10)*10 == -10, और आप की जरूरत है (-1)%10 == 9मैच के लिए पहले समीकरण के लिए। अन्य उत्तरों की तरह , यह वर्तमान मानक (एस) के भीतर काम नहीं करता है, लेकिन यह है कि यह कैसे काम करता है। यह वास्तव में इस तरह के रूप शेष के संकेत है, लेकिन कैसे विभाजन राउंड और क्या शेष तो बारे में नहीं है है समीकरण को संतुष्ट करने के लिए किया जाना है।
इलकाचू

1
@Chipster स्रोत आपके द्वारा उद्धृत स्निपेट्स है। ध्यान दें (-1)*10+9=-1, कि चुनाव (-1)/10=-1और (-1)%10=9गवर्निंग समीकरण का उल्लंघन नहीं करता है। दूसरी ओर चुनाव (-1)%10=1को नियंत्रित करने वाले समीकरण को संतुष्ट नहीं किया जा सकता है, चाहे वह कैसे (-1)/10चुना जाए; ऐसा कोई पूर्णांक नहीं qहै q*10+1=-1
स्टूअबेसिक

8

जैसा कि दूसरों ने बताया है, n == 0 के लिए विशेष उपचार बकवास है, क्योंकि प्रत्येक सी गंभीर प्रोग्रामर के लिए यह स्पष्ट है कि "जबकि (एन)" काम करता है।

N <0 के लिए व्यवहार स्पष्ट नहीं है, इसीलिए मैं उन 2 लाइनों को देखना पसंद करूंगा:

if (n < 0) 
    n = -n;

या कम से कम एक टिप्पणी:

// don't worry, works for n < 0 as well

ईमानदारी से, आप किस समय पर विचार करना शुरू करते हैं कि एन नकारात्मक हो सकता है? कोड लिखते समय या अपने शिक्षक की टिप्पणियों को पढ़ते समय?


1
भले ही एन नकारात्मक हो, एन चुकता सकारात्मक होगा। तो, पहली जगह में साइन क्यों हटाएं? -3 * -3 = 9; ३ * ३ = ९। या ३० वर्षों में गणित बदल गया है क्योंकि मैंने इसे सीखा है?
मेरोवेक्स

2
@ सीबी निष्पक्ष होने के लिए, मैंने यह भी नहीं देखा कि टेस्ट लिखते समय एन नेगेटिव हो सकता है, लेकिन जब वापस आया तो मुझे बस एक एहसास था कि जबकि लूप को स्किप नहीं किया जाएगा, भले ही नंबर नेगेटिव हो। मैंने अपने कंप्यूटर पर कुछ परीक्षण किए और इसने मेरे संदेह की पुष्टि की। उसके बाद, मैंने यह प्रश्न पोस्ट किया। तो नहीं, मैं कोड लिखते समय इतनी गहराई से नहीं सोच रहा था।
20010 पर user010517720

5

यह मुझे एक असाइनमेंट की याद दिलाता है जो मैं विफल रहा

90 के दशक में वापस आ गए। व्याख्याता छोरों के बारे में अंकुरित कर रहे थे और, छोटी कहानी, हमारा काम एक फ़ंक्शन लिखना था जो किसी भी पूर्णांक> 0 के लिए अंकों की संख्या वापस कर देगा।

इसलिए, उदाहरण के लिए, अंकों की संख्या 321होगी 3

यद्यपि असाइनमेंट ने एक फ़ंक्शन लिखने के लिए कहा था जो अंकों की संख्या लौटाता था, उम्मीद थी कि हम एक लूप का उपयोग करेंगे जो 10 तक विभाजित करता है ... आपको यह मिलता है, जैसा कि व्याख्यान द्वारा कवर किया गया है

लेकिन लूप का उपयोग स्पष्ट रूप से नहीं किया गया था इसलिए मैंने कहा: took the log, stripped away the decimals, added 1और बाद में पूरी कक्षा के सामने लंबित था।

बिंदु, असाइनमेंट का उद्देश्य व्याख्यान के दौरान हमने जो सीखा था, उसकी हमारी समझ का परीक्षण करना था । मुझे प्राप्त व्याख्यान से मैंने सीखा कि कंप्यूटर शिक्षक थोड़ा झटका था (लेकिन शायद एक योजना के साथ एक झटका)?


आपकी स्थिति में:

C / C ++ में एक फ़ंक्शन लिखें जो संख्या वर्ग के अंकों का योग लौटाता है

मैंने निश्चित रूप से दो उत्तर दिए होंगे:

  • सही उत्तर (संख्या पहले वर्ग), और
  • उदाहरण के साथ रखने में गलत जवाब, बस उसे खुश रखने के लिए ;-)

5
और एक तिहाई अंक का योग भी है?
क्रि।

@ क्रेस - हाँ, मैं वह स्मार्ट नहीं हूँ :-(
धीरे

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

1

आम तौर पर असाइनमेंट में सभी निशान सिर्फ इसलिए नहीं दिए जाते क्योंकि कोड काम करता है। आपको पढ़ने में आसान, कुशल और सुरुचिपूर्ण बनाने के लिए अंक भी मिलते हैं। ये चीजें हमेशा परस्पर अनन्य नहीं होती हैं।

एक मैं पर्याप्त नहीं कर सकता है "सार्थक चर नामों का उपयोग करें"

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

एक और बात मैं सी कोड के साथ देख रहा हूं, लोग चालाक दिखने की कोशिश कर रहे हैं। जबकि (n! = 0) का उपयोग करने के बजाय मैं सभी को दिखाऊंगा कि मैं (n) लिखते समय कितना चतुर हूं क्योंकि इसका मतलब वही है। वैसे यह आपके पास संकलक में है, लेकिन जैसा कि आपने सुझाव दिया है कि शिक्षक के पुराने संस्करण ने इसे उसी तरह लागू नहीं किया है।

एक सामान्य उदाहरण एक सूचकांक में एक ही समय में वृद्धि करते हुए एक सूचकांक का संदर्भ दे रहा है; संख्या [i ++] = iPrime;

अब, कोड पर काम करने वाले अगले प्रोग्रामर को यह जानना होगा कि क्या मैं असाइनमेंट से पहले या बाद में बढ़ जाता हूं, बस कोई ऐसा दिखावा कर सकता है।

डिस्क स्थान का मेगाबाइट सस्ता है कि टॉयलेट पेपर का एक रोल, अंतरिक्ष को बचाने के लिए प्रयास करने के बजाय स्पष्टता के लिए जाएं, आपके साथी प्रोग्रामर अधिक खुश होंगे।


2
मैंने C को केवल कुछ समय में क्रमादेशित किया है और मुझे पता है कि ++iमूल्यांकन से पहले i++वेतन वृद्धि और उसके बाद वेतन वृद्धि। while(n)एक सामान्य भाषा सुविधा भी है। इस तरह तर्क के आधार पर मैंने बहुत सारे कोड देखे हैं if (foo == TRUE)। मैं फिर से सहमत हूं: चर नाम, हालांकि।
एलन ओलाघन

3
आम तौर पर यह गलत सलाह नहीं है, लेकिन रक्षात्मक प्रोग्रामिंग के उद्देश्य के लिए बुनियादी भाषा सुविधाओं (जिससे लोग अनिवार्य रूप से वैसे भी भर में आएंगे) से बचना अत्यधिक है। लघु, स्पष्ट कोड अक्सर अधिक पठनीय होता है। हम यहाँ पागल पर्ल या बैश वन-लाइनर्स के बारे में बात नहीं कर रहे हैं, बस वास्तव में बुनियादी भाषा सुविधाएँ हैं।
एलन ओलाघन

1
निश्चित नहीं है, इस उत्तर के लिए क्या गिरावट आई थी। मेरे लिए यहां बताई गई हर बात सही और महत्वपूर्ण है और हर डेवलपर के लिए एक अच्छी सलाह है। विशेष रूप से स्मार्ट-गधा-प्रोग्रामिंग भाग, भले ही while(n)उसके लिए सबसे खराब उदाहरण नहीं है (मैं " if(strcmp(one, two))अधिक " पसंद करता हूं )
काई हुप्पमन

1
और इसके अलावा, आपको वास्तव में ऐसे किसी भी व्यक्ति को नहीं होने देना चाहिए जो उत्पादन में उपयोग किए जाने वाले सी कोड को बीच में अंतर नहीं करता है i++और ++iसंशोधित करना चाहिए।
klutt

2
@PaulMcCarthy हम दोनों कोड पठनीयता के लिए हैं। लेकिन हम इस बात से असहमत हैं कि इसका क्या मतलब है। इसके अलावा, यह उद्देश्य नहीं है। एक व्यक्ति के लिए पढ़ना आसान है, दूसरे के लिए कठिन हो सकता है। व्यक्तित्व और पृष्ठभूमि ज्ञान दृढ़ता से इसे प्रभावित करता है। मेरा मुख्य बिंदु यह है कि आपको कुछ नियमों का आँख बंद करके अधिकतम पठनीयता नहीं मिलती है।
klutt

0

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


4
"अतिरिक्त रिटर्न एक गोटो स्टेटमेंट है और हम सी में गोटो का उपयोग नहीं करते हैं।" - यह एक बहुत व्यापक सामान्यीकरण और बहुत दूर खिंचाव का संयोजन है।
एसएस ऐनी

1
"हम" निश्चित रूप से गोटो का उपयोग करते हैं। सी इसमें कुछ भी गलत नहीं है।
कलूट

1
इसके अलावा, वहाँ एक समारोह के साथ कुछ भी गलत नहीं हैint findChar(char *str, char c) { if(!str) return -1; int i=0; while(str[i]) { if(str[i] == c) return i; i++; } return -1; }
klutt

1
@PeterKrassoi मैं हार्ड-टू-रीड कोड की वकालत नहीं कर रहा हूं, लेकिन मैंने कई उदाहरण देखे हैं जहां कोड एक साधारण गोटो या अतिरिक्त रिटर्न स्टेटमेंट से बचने के लिए पूरी तरह गड़बड़ लगता है। यहाँ गोटो के उपयुक्त उपयोग के साथ कुछ कोड है। मैं आपको चुनौती देता हूं कि एक ही समय में गोटो के बयानों को हटा दें और कोड को पढ़ना और आसान बनाना आसान बना देता है: pastebin.com/aNaGb66Q
klutt 22'19

1
@PeterKrassoi कृपया अपना संस्करण भी दिखाएं: pastebin.com/qBmR78KA
klutt

0

समस्या कथन भ्रामक है, लेकिन संख्यात्मक उदाहरण संख्या वर्ग के अंकों के योग का अर्थ स्पष्ट करता है । यहाँ एक बेहतर संस्करण है:

C और C ++ के सामान्य सबसेट में एक फ़ंक्शन लिखें जो कि [-10 7 , 10 7 ]n में पूर्णांक लेता है और आधार 10. में इसके प्रतिनिधित्व के अंकों के वर्गों का योग लौटाता है। उदाहरण: यदि है , तो आपका कार्य लौट जाना चाहिए (१ + २ + ३ = १४)।n12314

आपके द्वारा लिखा गया फ़ंक्शन 2 विवरणों को छोड़कर ठीक है:

  • तर्क longमें निर्दिष्ट सीमा में सभी मानों को समायोजित करने के लिए टाइप होना चाहिए longक्योंकि C मानक द्वारा कम से कम 31 मान बिट्स की गारंटी दी गई है, इसलिए [-10 7 , 10 7 ] में सभी मूल्यों का प्रतिनिधित्व करने के लिए पर्याप्त सीमा है । (ध्यान दें कि प्रकार intवापसी के प्रकार के लिए पर्याप्त है, जिसका अधिकतम मूल्य है 568।)
  • %नकारात्मक ऑपरेंड्स के लिए व्यवहार गैर-सहज है और इसकी विशिष्टता C99 मानक और पिछले संस्करणों के बीच भिन्न है। आपको यह बताना चाहिए कि नकारात्मक दृष्टिकोणों के लिए भी आपका दृष्टिकोण क्यों मान्य है।

यहाँ एक संशोधित संस्करण है:

int sum_of_digits_squared(long n) {
    int s = 0;

    while (n != 0) {
        /* Since integer division is defined to truncate toward 0 in C99 and C++98 and later,
           the remainder of this division is positive for positive `n`
           and negative for negative `n`, and its absolute value is the last digit
           of the representation of `n` in base 10.
           Squaring this value yields the expected result for both positive and negative `c`.
           dividing `n` by 10 effectively drops the last digit in both cases.
           The loop will not be entered for `n == 0`, producing the correct result `s = 0`.
         */
        int c = n % 10;
        s += c * c;
        n /= 10;
    }
    return s;
}

शिक्षक के उत्तर में कई दोष हैं:

  • प्रकार intमें मानों की अपर्याप्त सीमा हो सकती है।
  • मूल्य विशेष मामले की कोई जरूरत नहीं है 0
  • नकारात्मक मूल्यों को नकारना अनावश्यक है और इसके लिए अपरिभाषित व्यवहार हो सकता है n = INT_MIN

समस्या कथन (C99 और मानों की श्रेणी n) में अतिरिक्त बाधाओं को देखते हुए , केवल पहला दोष एक मुद्दा है। अतिरिक्त कोड अभी भी सही उत्तर तैयार करता है।

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

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