नकारात्मक संख्या का मॉड मेरे दिमाग को पिघला रहा है


189

मैं एक पूर्णांक को मॉड करने के लिए एक सरणी स्थिति प्राप्त करने की कोशिश कर रहा हूं ताकि यह गोल हो जाए। i % arrayLengthसकारात्मक संख्याओं के लिए काम करना ठीक है लेकिन नकारात्मक संख्याओं के लिए यह सब गलत हो जाता है।

 4 % 3 == 1
 3 % 3 == 0
 2 % 3 == 2
 1 % 3 == 1
 0 % 3 == 0
-1 % 3 == -1
-2 % 3 == -2
-3 % 3 == 0
-4 % 3 == -1

इसलिए मुझे इसके कार्यान्वयन की आवश्यकता है

int GetArrayIndex(int i, int arrayLength)

ऐसा है कि

GetArrayIndex( 4, 3) == 1
GetArrayIndex( 3, 3) == 0
GetArrayIndex( 2, 3) == 2
GetArrayIndex( 1, 3) == 1
GetArrayIndex( 0, 3) == 0
GetArrayIndex(-1, 3) == 2
GetArrayIndex(-2, 3) == 1
GetArrayIndex(-3, 3) == 0
GetArrayIndex(-4, 3) == 2

मैंने पहले भी ऐसा किया है लेकिन किसी कारण से यह आज मेरे दिमाग को पिघला रहा है :(


Math.stackexchange.com/questions/519845/… पर गणितीय मापांक के बारे में चर्चा देखें
PPC

blogs.msdn.microsoft.com/ericlippert/2011/12/05/… एक बेहतरीन लेख है
समरा

जवाबों:


281

मैं हमेशा अपने स्वयं के modफ़ंक्शन का उपयोग करता हूं , जैसा कि परिभाषित किया गया है

int mod(int x, int m) {
    return (x%m + m)%m;
}

बेशक, यदि आप मापांक ऑपरेशन में दो कॉल करने से परेशान हैं, तो आप इसे लिख सकते हैं

int mod(int x, int m) {
    int r = x%m;
    return r<0 ? r+m : r;
}

या इसके प्रकार।

इसका कारण यह है कि "x% m" हमेशा सीमा [-m + 1, m-1] में होता है। तो अगर यह सब नकारात्मक है, तो इसमें m जोड़ने से इसे सकारात्मक रेंज में रखा जाएगा, बिना इसके मूल्य modulo m को बदले।


7
नोट: पूर्ण संख्या-सिद्धांत संबंधी पूर्णता के लिए, आप शीर्ष पंक्ति में यह कहना चाह सकते हैं कि "यदि (m <0) m = -m;" हालांकि इस मामले में यह "arrayLength" के रूप में मायने नहीं रखता है, संभवतः हमेशा सकारात्मक है।
श्रीवत्सआर

4
यदि आप मी के मूल्य की जांच करने जा रहे हैं, तो आपको शून्य को भी बाहर करना चाहिए।
बिल्वपत्र 9

6
@RuudLenders: No. यदि x = -5 और m = 2 है, तो r = x%mहै -1, जिसके बाद r+mहै 1। जबकि लूप की जरूरत नहीं है। मुद्दा यह है कि (जैसा कि मैंने उत्तर में लिखा है), x%mहमेशा की तुलना में कड़ाई से अधिक है -m, इसलिए आपको mइसे सकारात्मक बनाने के लिए अधिक से अधिक एक बार जोड़ने की आवश्यकता है ।
श्रीवत्सआर

4
@dcastro: मैं चाहता हूँ -12 mod -10 होना 8. 8. यह गणित में सबसे आम सम्मेलन है, कि अगर modulo के rलिए एक प्रतिनिधि चुन रहा है , तो यह ऐसा है कि 0 <r <| b | ab
श्रीवत्सआर

8
+1। मुझे इस बात की परवाह नहीं है कि एक नकारात्मक मापांक के लिए कोई भी व्यक्ति क्या करता है - 'कम से कम गैर-नकारात्मक अवशेष' गणितीय नियमितता को प्रदर्शित करता है और किसी भी अस्पष्टता को दूर करता है।
ब्रेट हेल

79

कृपया ध्यान दें कि C # और C ++ का% ऑपरेटर वास्तव में मॉडुलो नहीं है, यह शेष है। आपके मामले में, जो आप चाहते हैं, उनके लिए सूत्र है:

float nfmod(float a,float b)
{
    return a - b * floor(a / b);
}

आपको इसे C # (या C ++) में फिर से लिखना होगा लेकिन यह तरीका है जिससे आपको modulo मिलता है और शेष नहीं।


21
"कृपया ध्यान दें कि C ++ का% ऑपरेटर वास्तव में मॉडुलो नहीं है, यह शेष है।" धन्यवाद, यह अब समझ में आता है, हमेशा आश्चर्य होता है कि इसने कभी भी नकारात्मक संख्याओं के साथ ठीक से काम क्यों नहीं किया।
leetNightshade

2
"कृपया ध्यान दें कि C ++ का% ऑपरेटर वास्तव में मॉडुलो नहीं है, यह शेष है।" मुझे नहीं लगता कि यह सटीक है और मैं नहीं देखता कि मॉडुलो शेष से अलग क्यों है। यही कारण है कि यह मोडुलो ऑपरेशन विकिपीडिया पृष्ठ पर भी कहता है। यह सिर्फ इतना है कि प्रोग्रामिंग भाषाएं नकारात्मक संख्याओं को अलग तरह से व्यवहार करती हैं। C # में modulo ऑपरेटर स्पष्ट रूप से "शून्य (-9% 4 = -1) से रहता है, क्योंकि 1 के अंतर के साथ 4 * -2 is -8 है), जबकि एक अन्य परिभाषा -9% 4 को +3 के रूप में मानेगी, क्योंकि -4 * 3 -12 है, शेष +3 (जैसे Google के खोज फ़ंक्शन में, वहां बैक-एंड भाषा के बारे में सुनिश्चित नहीं है)।
टायर

18
टायरिस, मापांक और शेष के बीच अंतर है। उदाहरण के लिए: -21 mod 4 is 3 because -21 + 4 x 6 is 3. लेकिन -21 divided by 4 gives -5ए के साथ remainder of -1। सकारात्मक मूल्यों के लिए, कोई अंतर नहीं है। तो कृपया इन अंतरों के बारे में खुद को सूचित करें। और हर समय विकिपीडिया पर भरोसा न करें :)
Пет Пр Петров

2
कोई भी मोड्यूलो के बजाय शेष फ़ंक्शन का उपयोग क्यों करना चाहेगा? उन्होंने %शेष क्यों बनाया ?
आरोन फ्रेंके

4
@AaronFranke - इसकी पहले की सीपी से एक विरासत थी जिसमें विभाजन हार्डवेयर जल्दी से एक भागफल और शेष बनाने के लिए था - और यही वह हार्डवेयर था जिसने एक नकारात्मक लाभांश दिया था। भाषा ने केवल हार्डवेयर को प्रतिबिंबित किया। अधिकांश समय प्रोग्रामर सकारात्मक लाभांश के साथ काम कर रहे थे, और इस क्विक को अनदेखा कर रहे थे। गति सर्वोपरि थी।
टूलमेकरसैट

15

%केवल एक बार उपयोग करके एकल-पंक्ति कार्यान्वयन :

int mod(int k, int n) {  return ((k %= n) < 0) ? k+n : k;  }

1
क्या ये सही है? जैसा कि मैं इसे किसी के द्वारा स्वीकार किए जाने के रूप में नहीं देखता, और न ही कोई टिप्पणी। उदाहरण के लिए। mod (-10,6) वापस आएगा 6. क्या यह सही है? यह 4 वापस नहीं आना चाहिए?
जॉन डेमेट्रियौ

3
@JohnDemetriou आपकी संख्या दोनों गलत हैं: (ए) इसे 2 वापस करना चाहिए और (बी) यह 2 वापस करता है; कोड चलाने का प्रयास करें। आइटम (ए): mod(-10, 6)हाथ से खोजने के लिए , आप या तो 6 दोहराते हैं या घटाते हैं जब तक कि उत्तर सीमा में न हो [0, 6)। इस अंकन का अर्थ है "बाईं ओर समावेशी, और दाईं ओर अनन्य"। हमारे मामले में, हम 6 दो बार जोड़ते हैं, 2 दे रहे हैं। कोड काफी सरल है, और यह देखना आसान है कि यह सही है: पहला, यह nऊपर के रूप में जोड़ने / घटाना के बराबर करता है, सिवाय इसके कि यह एक nछोटी रोक देता है , अगर से आ रहा है नकारात्मक पक्ष। उस मामले में हम इसे ठीक करते हैं। वहाँ: टिप्पणियाँ :)
इवगेनी सर्गेव

1
वैसे, यहां एक कारण एक एकल का उपयोग %करना एक अच्छा विचार हो सकता है। तालिका देखें क्या बातें प्रबंधित कोड में खर्च लेख में जानें क्या बातें लागत: लेखन तेज़ प्रबंधित कोड । उपयोग करना तालिका में सूचीबद्ध %समान रूप से महंगा है int div: जोड़ना या घटाना की तुलना में लगभग 36 गुना अधिक महंगा, और गुणा करने की तुलना में लगभग 13 गुना अधिक महंगा है। बेशक, कोई बड़ी बात नहीं है जब तक कि यह आपके कोड के मूल में न हो।
इवगेनी सर्गेव

2
लेकिन %एक परीक्षण और कूद की तुलना में एक अधिक महंगा है, खासकर अगर यह आसानी से भविष्यवाणी नहीं की जा सकती है?
मेडिनोक

6

श्रीवत्सआर का जवाब सभी मामलों के लिए काम नहीं करेगा, भले ही आप "यदि (m <0) m = -m?" जोड़ते हैं, यदि आप नकारात्मक लाभांश / भाजक के लिए खाते हैं।

उदाहरण के लिए, -12 मॉड -10 8 होगा, और यह -2 होना चाहिए।

निम्नलिखित कार्यान्वयन सकारात्मक और नकारात्मक लाभांश / भाजक दोनों के लिए काम करेगा और अन्य कार्यान्वयन (अर्थात्, जावा, पायथन, रूबी, स्काला, स्कीम, जावास्क्रिप्ट और Google के कैलकुलेटर) का अनुपालन करेगा:

internal static class IntExtensions
{
    internal static int Mod(this int a, int n)
    {
        if (n == 0)
            throw new ArgumentOutOfRangeException("n", "(a mod 0) is undefined.");

        //puts a in the [-n+1, n-1] range using the remainder operator
        int remainder = a%n;

        //if the remainder is less than zero, add n to put it in the [0, n-1] range if n is positive
        //if the remainder is greater than zero, add n to put it in the [n-1, 0] range if n is negative
        if ((n > 0 && remainder < 0) ||
            (n < 0 && remainder > 0))
            return remainder + n;
        return remainder;
    }
}

टेस्ट सूट का उपयोग कर xUnit:

    [Theory]
    [PropertyData("GetTestData")]
    public void Mod_ReturnsCorrectModulo(int dividend, int divisor, int expectedMod)
    {
        Assert.Equal(expectedMod, dividend.Mod(divisor));
    }

    [Fact]
    public void Mod_ThrowsException_IfDivisorIsZero()
    {
        Assert.Throws<ArgumentOutOfRangeException>(() => 1.Mod(0));
    }

    public static IEnumerable<object[]> GetTestData
    {
        get
        {
            yield return new object[] {1, 1, 0};
            yield return new object[] {0, 1, 0};
            yield return new object[] {2, 10, 2};
            yield return new object[] {12, 10, 2};
            yield return new object[] {22, 10, 2};
            yield return new object[] {-2, 10, 8};
            yield return new object[] {-12, 10, 8};
            yield return new object[] {-22, 10, 8};
            yield return new object[] { 2, -10, -8 };
            yield return new object[] { 12, -10, -8 };
            yield return new object[] { 22, -10, -8 };
            yield return new object[] { -2, -10, -2 };
            yield return new object[] { -12, -10, -2 };
            yield return new object[] { -22, -10, -2 };
        }
    }

सबसे पहले, एक modफ़ंक्शन को आमतौर पर सकारात्मक मापांक के साथ बुलाया जाता है ( arrayLengthमूल प्रश्न में चर पर ध्यान दें , जो यहां उत्तर दिया जा रहा है, जो संभवतः नकारात्मक नहीं है), इसलिए फ़ंक्शन को वास्तव में नकारात्मक मापांक के लिए काम करने की आवश्यकता नहीं है। (यही कारण है कि मैं अपने जवाब पर टिप्पणी में नकारात्मक मापांक के उपचार का उल्लेख करता हूं, उत्तर में ही नहीं।) (contd ...)
श्रीवत्सआर

3
(... "contd) दूसरे, एक नकारात्मक मापांक के लिए क्या करना है यह सम्मेलन का विषय है। उदाहरण देखें विकिपीडिया । "आमतौर पर, संख्या सिद्धांत में, सकारात्मक शेष हमेशा चुना जाता है" और इसी तरह मैंने इसे (बर्टन के एलिमेंटरी नंबर थ्योरी में ) भी सीखा । नुथ इसे भी इस तरह परिभाषित करता है (विशेष रूप से, r = a - b floor(a/b)हमेशा सकारात्मक होता है)। उदाहरण के लिए, कंप्यूटर सिस्टम, पास्कल और मेपल के बीच भी, इसे हमेशा सकारात्मक रहने के लिए परिभाषित किया जाता है।
श्रीवत्सआर

@ श्रीवत्सआर मुझे पता है कि यूक्लिडियन परिभाषा बताती है कि परिणाम हमेशा सकारात्मक होगा - लेकिन मैं इस धारणा के तहत हूं कि अधिकांश आधुनिक मॉड कार्यान्वयन एक नकारात्मक भाजक के लिए [n + 1, 0] रेंज में एक मान लौटाएंगे "n", जिसका अर्थ है कि -12 मॉड -10 = -2। Ive ने Google कैलकुलेटर , पायथन , रूबी और स्काला को देखा और वे सभी इस सम्मेलन का अनुसरण करते हैं।
१.१४ बजे के दिन

इसके अलावा, सूची में जोड़ने के लिए: स्कीम और जावास्क्रिप्ट
dcastro

1
फिर, यह अभी भी एक अच्छा पढ़ा है। "हमेशा सकारात्मक" परिभाषा (मेरा उत्तर) ALGOL, डार्ट, मेपल, पास्कल, Z3, आदि के साथ संगत है। "विभाजक का संकेत" (यह उत्तर) संगत है: APL, COBOL, J, Lua, Mathematica, MS Excel, Perl, Python, R, Ruby, Tcl, आदि दोनों "लाभांश के संकेत" के रूप में असंगत हैं: AWK, bash, bc, C99, C ++ 11, C #, D, Eiffel, Erlang, Go, Java , OCaml, PHP, Rust, Scala, Swift, VB, x86 विधानसभा, आदि। मैं वास्तव में नहीं देखता कि आप कैसे दावा कर सकते हैं कि एक सम्मेलन "सही" है और अन्य "गलत" हैं।
श्रीवत्सआर

6

कुछ समझ जोड़ना।

द्वारा इयूक्लिडियन परिभाषा आधुनिक परिणाम हमेशा सकारात्मक होना चाहिए।

उदाहरण के लिए:

 int n = 5;
 int x = -3;

 int mod(int n, int x)
 {
     return ((n%x)+x)%x;
 }

आउटपुट:

 -1

15
मैं उलझन में हूँ ... आप कहते हैं कि परिणाम हमेशा सकारात्मक होना चाहिए, लेकिन फिर आउटपुट की सूची के रूप में -1?
जेफ बी

@JeffBridgman मैंने कहा है कि यूक्लिडियन परिभाषा पर आधारित है। `शेष के लिए दो संभावित विकल्प हैं, एक नकारात्मक और दूसरा सकारात्मक, और भागफल के लिए दो संभावित विकल्प भी हैं। आमतौर पर, संख्या सिद्धांत में the positive remainder is always chosen, लेकिन प्रोग्रामिंग भाषा भाषा और ए और / या n के संकेतों के आधार पर चुनती है। [५] मानक पास्कल और अल्गोल 68 नकारात्मक विभाजकों के लिए भी एक सकारात्मक शेष (या 0) देते हैं, और कुछ प्रोग्रामिंग भाषाएं, जैसे कि C90, इसे लागू होने तक छोड़ देते हैं जब n या तो नकारात्मक होता है।
`

5

दो प्रमुख उत्तरों की तुलना

(x%m + m)%m;

तथा

int r = x%m;
return r<0 ? r+m : r;

किसी ने वास्तव में इस तथ्य का उल्लेख नहीं किया है कि पहले वाला फेंक सकता है OverflowExceptionजबकि दूसरा नहीं। और भी बदतर, डिफ़ॉल्ट अनियंत्रित संदर्भ के साथ, पहला उत्तर गलत उत्तर दे सकता है ( mod(int.MaxValue - 1, int.MaxValue)उदाहरण के लिए देखें )। तो दूसरा उत्तर न केवल तेज लगता है, बल्कि अधिक सही भी है।


4

बस% के नकारात्मक परिणाम के लिए अपने मापांक (arrayLength) को जोड़ें और आप ठीक हो जाएंगे।


4

अधिक प्रदर्शन के लिए जागरूक देवता

uint wrap(int k, int n) ((uint)k)%n

एक छोटे प्रदर्शन की तुलना

Modulo: 00:00:07.2661827 ((n%x)+x)%x)
Cast:   00:00:03.2202334 ((uint)k)%n
If:     00:00:13.5378989 ((k %= n) < 0) ? k+n : k

कलाकारों के प्रदर्शन की लागत के रूप में यहाँ एक नज़र है


3
मेथिंक्स जो -3 % 10या तो -3 या 7. होना चाहिए क्योंकि एक गैर-नकारात्मक परिणाम चाहता है, 7 का जवाब होगा। आपका कार्यान्वयन रिटर्न 3. आपको uintकलाकारों को हटाने और निकालने के लिए दोनों मापदंडों को बदलना चाहिए ।
मुझे

5
बिना अंकगणित अंकगणित केवल तभी समतुल्य है यदि nदो की शक्ति है, तो इस स्थिति में आप केवल एक तार्किक और ( (uint)k & (n - 1)) का उपयोग कर सकते हैं , यदि कंपाइलर पहले से आपके लिए ऐसा नहीं करता है (कंपाइलर अक्सर स्मार्ट होते हैं यह पता लगाने के लिए पर्याप्त है)।
j_schultz

2

मुझे इस थ्रेड पर पीटर एन लुईस द्वारा प्रस्तुत की गई ट्रिक पसंद है : "यदि n की एक सीमित सीमा है, तो आप [डिवाइजर] के ज्ञात स्थिर मल्टीपल को जोड़कर आप जो परिणाम चाहते हैं वह प्राप्त कर सकते हैं। न्यूनतम।"

इसलिए यदि मेरे पास एक मान d है जो डिग्री में है और मैं लेना चाहता हूं

d % 180f

और मैं समस्याओं से बचना चाहता हूं यदि d नकारात्मक है, तो इसके बजाय मैं बस यही करता हूं:

(d + 720f) % 180f

यह मान लिया गया है, हालांकि यह है कि डी नकारात्मक हो सकता है, यह ज्ञात है कि यह -720 की तुलना में अधिक नकारात्मक कभी नहीं होगा।


2
-1: सामान्य रूप से पर्याप्त नहीं है, (और अधिक सामान्य समाधान देना बहुत आसान है)।
इवगेनी सर्गेव

4
यह वास्तव में बहुत मददगार है। जब आपके पास सार्थक सीमा होती है, तो यह गणना को सरल बना सकता है। मेरे मामले में math.stackexchange.com/questions/2279751/…
M.kazem Akhgary

वास्तव में, बस इसे dayOfWeek गणना (-6 से +6 की ज्ञात सीमा) के लिए इस्तेमाल किया और इससे दो की बचत हुई %
नेटमैज

मेरे लिए @EvgeniSergeev +0: ओपी प्रश्न का उत्तर नहीं देता है, लेकिन एक अधिक विशिष्ट संदर्भ में सहायक हो सकता है (लेकिन अभी भी प्रश्न के संदर्भ में)
एरडल जी।

1

आप एक व्यवहार की अपेक्षा कर रहे हैं जो c # में% ऑपरेटर के प्रलेखित व्यवहार के विपरीत है - संभवतः क्योंकि आप इसे इस तरह से काम करने की अपेक्षा कर रहे हैं कि यह किसी अन्य भाषा में काम करता है जिसे आप अधिक उपयोग करते हैं। प्रलेखन c # राज्यों (जोर मेरा) पर:

पूर्णांक प्रकारों के परिचालनों के लिए,% b का परिणाम - (a / b) * b द्वारा उत्पादित मूल्य है। गैर-शून्य शेष का चिह्न बाएं हाथ के ऑपरेंड के समान है

आप जो मूल्य चाहते हैं, उसकी गणना एक अतिरिक्त कदम से की जा सकती है:

int GetArrayIndex(int i, int arrayLength){
    int mod = i % arrayLength;
    return (mod>=0) : mod ? mod + arrayLength;
}

0

यदि आपके विभाजक सकारात्मक हैं, तो यहां सभी उत्तर बहुत अच्छे हैं, लेकिन यह पूर्ण नहीं है। यहाँ मेरा कार्यान्वयन है जो हमेशा की एक सीमा पर लौटता है [0, b), जैसे कि आउटपुट का संकेत भाजक के संकेत के समान होता है, जो नकारात्मक विभाजक को आउटपुट रेंज के लिए समापन बिंदु के रूप में अनुमति देता है।

PosMod(5, 3)रिटर्न 2
PosMod(-5, 3)रिटर्न 1
PosMod(5, -3)रिटर्न -1
PosMod(-5, -3)रिटर्न-2

    /// <summary>
    /// Performs a canonical Modulus operation, where the output is on the range [0, b).
    /// </summary>
    public static real_t PosMod(real_t a, real_t b)
    {
        real_t c = a % b;
        if ((c < 0 && b > 0) || (c > 0 && b < 0)) 
        {
            c += b;
        }
        return c;
    }

(जहां real_tकिसी भी प्रकार की संख्या हो सकती है)


0

डस्कोरो के उत्तर की एक पंक्ति का कार्यान्वयन (अन्य भाषाओं के साथ सबसे अधिक अनुपालन):

int Mod(int a, int n)
{
    return (((a %= n) < 0) && n > 0) || (a > 0 && n < 0) ? a + n : a;
}

यदि आप %ऑपरेटर का उपयोग करना चाहते हैं (आप C # में देशी ऑपरेटरों को अधिभार नहीं दे सकते हैं):

public class IntM
{
    private int _value;

    private IntM(int value)
    {
        _value = value;
    }

    private static int Mod(int a, int n)
    {
        return (((a %= n) < 0) && n > 0) || (a > 0 && n < 0) ? a + n : a;
    }

    public static implicit operator int(IntM i) => i._value;
    public static implicit operator IntM(int i) => new IntM(i);
    public static int operator %(IntM a, int n) => Mod(a, n);
    public static int operator %(int a, IntM n) => Mod(a, n);
}

मामले का उपयोग करें, दोनों काम करता है:

int r = (IntM)a % n;

// Or
int r = a % n(IntM);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.