3 लंबे पूर्णांकों का औसत


103

मेरे पास 3 बहुत बड़े हस्ताक्षरित पूर्णांक हैं।

long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;

मैं उनकी छंटनी औसत की गणना करना चाहता हूं। अपेक्षित औसत मूल्य है long.MaxValue - 1, जो है 9223372036854775806

इसकी गणना करना असंभव है:

long avg = (x + y + z) / 3; // 3074457345618258600

नोट: मैंने उन सभी प्रश्नों को औसतन 2 संख्याओं के बारे में पढ़ा है, लेकिन मैं यह नहीं देखता कि उस तकनीक को 3 संख्याओं के औसत पर कैसे लागू किया जा सकता है।

के उपयोग के साथ यह बहुत आसान होगा BigInteger, लेकिन मान लें कि मैं इसका उपयोग नहीं कर सकता।

BigInteger bx = new BigInteger(x);
BigInteger by = new BigInteger(y);
BigInteger bz = new BigInteger(z);
BigInteger bavg = (bx + by + bz) / 3; // 9223372036854775806

यदि मैं double, तो, निश्चित रूप से, मैं सटीकता खो देता हूं:

double dx = x;
double dy = y;
double dz = z;
double davg = (dx + dy + dz) / 3; // 9223372036854780000

यदि मैं इसमें रूपांतरित होता हूं decimal, तो यह काम करता है, लेकिन यह भी मान लें कि मैं इसका उपयोग नहीं कर सकता।

decimal mx = x;
decimal my = y;
decimal mz = z;
decimal mavg = (mx + my + mz) / 3; // 9223372036854775806

प्रश्न: क्या केवल एक longप्रकार के उपयोग से 3 बहुत बड़े पूर्णांकों की छंटनी औसत की गणना करने का एक तरीका है ? उस प्रश्न को C # -स्पेशल मत समझिए, बस मेरे लिए C # में नमूने उपलब्ध कराना आसान है।


1
क्यों नहीं कुल मिलाकर औसत की गणना अलग और अधिकतम से घटती है?
एंड्रियास निडरमेयर

6
@AndreasNiedermair अगर मेरे पास long.MinValueऔर long.MaxValueमूल्यों के बीच में काम नहीं करेगा ।
उलुगबेक उमिरोव

अच्छी पकड़, वास्तव में :)
एंड्रियास नीडरमेयर

क्या आप सुनिश्चित हैं कि हमें इस बारे में चिंता करने की ज़रूरत है, क्या इसे रूपरेखा द्वारा नियंत्रित नहीं किया जाना चाहिए?
बोलू

11
क्या कोई वास्तविक कारण है BigIntegerया decimalइसे बाहर रखा गया है, या यह सिर्फ इस कठिन बनाने के लिए है?
jpmc26

जवाबों:


142

यह कोड काम करेगा, लेकिन वह सुंदर नहीं है।

यह पहले सभी तीन मूल्यों को विभाजित करता है (यह मूल्यों को फर्श करता है, इसलिए आप शेष को खो देते हैं), और फिर शेष को विभाजित करता है:

long n = x / 3
         + y / 3
         + z / 3
         + ( x % 3
             + y % 3
             + z % 3
           ) / 3

ध्यान दें कि उपरोक्त नमूना हमेशा एक या अधिक नकारात्मक मान होने पर ठीक से काम नहीं करता है।

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

उत्तर और की टिप्पणी के लिए धन्यवाद Ulugbek Umirov , जेम्स एस , KevinZ , मार्क वैन ल्युवेन , gnasher729 इस मौजूदा समाधान है:

static long CalculateAverage(long x, long y, long z)
{
    return (x % 3 + y % 3 + z % 3 + 6) / 3 - 2
            + x / 3 + y / 3 + z / 3;
}

static long CalculateAverage(params long[] arr)
{
    int count = arr.Length;
    return (arr.Sum(n => n % count) + count * (count - 1)) / count - (count - 1)
           + arr.Sum(n => n / count);
}

3
@DavidG संख्या में गणित, (x + y + z) / 3 = x / 3 + y / 3 + z / 3
क्रिस वैंडरमेन

4
मैंने Z3 का उपयोग 1 और 5. के बीच सभी परिवर्तनीय गणनाओं के लिए इसे सही साबित करने के लिए किया
usr

5
बेशक यह काम करने के लिए प्रकट होता है, लेकिन पूर्णांक ट्रंकेशन जिस तरह से संचालित होता है वह आपको खराब कर देगा। f(1,1,2) == 1जबकिf(-2,-2,8) == 2
केविनजेड

11
ध्यान दें कि मोडुलो ऑपरेशन के मस्तिष्क-क्षतिग्रस्त सिमेंटिक्स के कारण, यह एक परिणाम दे सकता है जो कि एक से बंद है, अर्थात् नीचे के बजाय गोल, अगर चर के लिए नकारात्मक मानों की अनुमति है। उदाहरण के लिए यदि x, y 3 के सकारात्मक गुणक हैं, और z -2 है, तो आपको वह मिलता है (x+y)/3जो बहुत अधिक है।
मार्क वैन लीउवेन

6
@ केवीएनजेड: ... जिसका प्रभाव तब एक प्रोग्रामर द्वारा पूर्ववत होना पड़ता है जो पहले विशेष व्यवहार के मामले में कभी नहीं चाहता था। प्रोग्रामर को इसे शेष से प्राप्त करने के बजाय मापांक निर्दिष्ट करने दें, जो संकलक मापांक से प्राप्त हो सकता है सहायक होगा।
सुपरकैट

26

एनबी - पैट्रिक ने पहले ही एक शानदार जवाब दिया है । इस पर विस्तार करना आप किसी भी संख्या में पूर्णांक जैसे कि एक सामान्य संस्करण कर सकते हैं:

long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;

long[] arr = { x, y, z };
var avg = arr.Select(i => i / arr.Length).Sum() 
        + arr.Select(i => i % arr.Length).Sum() / arr.Length;

1
ऐसा नहीं होगा long, लेकिन छोटे प्रकारों के लिए, ध्यान दें कि दूसरी राशि ओवरफ्लो हो सकती है।
user541686

7

पैट्रिक हॉफमैन ने एक महान समाधान पोस्ट किया है । लेकिन जरूरत पड़ने पर इसे अभी भी कई अन्य तरीकों से लागू किया जा सकता है। यहाँ एल्गोरिथ्म का उपयोग करना मेरे पास एक और उपाय है। यदि इसे सावधानी से लागू किया जाए तो यह धीमी हार्डवेयर विभाजक प्रणाली वाले कई विभाजनों से तेज हो सकता है। हैकर की प्रसन्नता से स्थिरांक तकनीक द्वारा विभाजित करके इसे और अधिक अनुकूलित किया जा सकता है

public class int128_t {
    private int H;
    private long L;

    public int128_t(int h, long l)
    {
        H = h;
        L = l;
    }

    public int128_t add(int128_t a)
    {
        int128_t s;
        s.L = L + a.L;
        s.H = H + a.H + (s.L < a.L);
        return b;
    }

    private int128_t rshift2()  // right shift 2
    {
        int128_t r;
        r.H = H >> 2;
        r.L = (L >> 2) | ((H & 0x03) << 62);
        return r;
    }

    public int128_t divideby3()
    {
        int128_t sum = {0, 0}, num = new int128_t(H, L);
        while (num.H || num.L > 3)
        {
            int128_t n_sar2 = num.rshift2();
            sum = add(n_sar2, sum);
            num = add(n_sar2, new int128_t(0, num.L & 3));
        }

        if (num.H == 0 && num.L == 3)
        {
            // sum = add(sum, 1);
            sum.L++;
            if (sum.L == 0) sum.H++;
        }
        return sum; 
    }
};

int128_t t = new int128_t(0, x);
t = t.add(new int128_t(0, y));
t = t.add(new int128_t(0, z));
t = t.divideby3();
long average = t.L;

64-बिट प्लेटफार्मों पर C / C ++ में यह बहुत आसान है __int128

int64_t average = ((__int128)x + y + z)/3;

2
मैं सुझाव दूंगा कि 3 से 32-बिट अहस्ताक्षरित मान को विभाजित करने का एक अच्छा तरीका 0x55555555L से गुणा करना है, 0x55555555 जोड़ना है, और 32 से दाएं शिफ्ट करना है। आपकी divideby3 विधि, तुलना करके, लगता है कि इसके लिए कई असतत चरणों की आवश्यकता होगी।
सुपरकैट

@ सुपरकैट हाँ मुझे वह तरीका पता है। हैकर की खुशी से विधि और भी सही है, लेकिन मैं इसे लागू करने के लिए एक और समय
phuclv

मुझे यकीन नहीं है कि "अधिक सही" का क्या मतलब है। पारस्परिक गुणन कई मामलों में सीधे सटीक मान प्राप्त कर सकते हैं, या फिर उपज मान जो एक या दो चरणों में परिष्कृत किए जा सकते हैं। BTW, मुझे लगता है कि मुझे 0x55555556 से गुणा करने का सुझाव देना चाहिए था, जो तब "ऐड" की आवश्यकता के बिना सटीक परिणाम देगा। इसके अलावा, क्या आपकी पाश की स्थिति सही है? लूप में एच और एल को क्या संशोधित करता है?
सुपरकैट

संयोग से, भले ही किसी के पास एक हार्डवेयर न हो, एक व्यक्ति के x=y/3माध्यम से एक अहस्ताक्षरित को जल्दी से अनुमानित कर सकता है x=y>>2; x+=x>>2; x+=x>>4; x+=x>>8; x+=x>>16; x+=x>>32;। परिणाम x के बहुत करीब होगा, और आवश्यकतानुसार कंप्यूटिंग delta=y-x-x-x;और समायोजन करके सटीक बनाया जा सकता है x
सुपरकैट

1
@ gnasher729 मुझे आश्चर्य है कि अगर यह 32-बिट कंप्यूटरों में उस अनुकूलन का उपयोग कर सकता है क्योंकि यह अक्सर 64x64 नहीं कर सकता है। 128 बिट गुणा
phuclv

7

आप योग का उपयोग करने के बजाय संख्याओं के बीच अंतर के आधार पर संख्याओं के माध्य की गणना कर सकते हैं।

मान लीजिए कि x अधिकतम है, y माध्य है, z न्यूनतम है (जैसा आपके पास है)। हम उन्हें अधिकतम, मध्य और मिनट कहेंगे।

सशर्त चेकर ने @ उलुगबेकउमरोव की टिप्पणी के अनुसार जोड़ा:

long tmp = median + ((min - median) / 2);            //Average of min 2 values
if (median > 0) tmp = median + ((max - median) / 2); //Average of max 2 values
long mean;
if (min > 0) {
    mean = min + ((tmp - min) * (2.0 / 3)); //Average of all 3 values
} else if (median > 0) {
    mean = min;
    while (mean != tmp) {
        mean += 2;
        tmp--;
    }
} else if (max > 0) {
    mean = max;
    while (mean != tmp) {
        mean--;
        tmp += 2;
    }
} else {
    mean = max + ((tmp - max) * (2.0 / 3));
}

2
@ उलुगबेकउम्रॉव की टिप्पणी देखें: यदि मेरे पास लंबे समय तक काम नहीं करेगा तो मैं करूंगा। वैल्यू और लॉन्ग.मैक्स वैल्यू। वैल्यू के बीच
बोलू

@Bolu टिप्पणी केवल long.MinValue पर लागू होती है। इसलिए मैंने इस सशर्त को हमारे मामले के लिए काम करने के लिए जोड़ा।
ला-कोमाद्रेजा

जब आप इनिशियलाइज़ नहीं हुए हैं तो आप माध्यिका का उपयोग कैसे कर सकते हैं?
फुलकव

@ LưuV LnhPhúc, माध्य न्यूनतम और अधिकतम के बीच का मान है।
ला-कॉमरेडेजा

1
(double)(2 / 3)0.0 के बराबर नहीं है?
फुलकव

5

क्योंकि C यूक्लिडियन डिवीजन के बजाय फ्लोर्ड डिवीजन का उपयोग करता है, इसलिए तीन हस्ताक्षरित लोगों की तुलना में तीन अहस्ताक्षरित मानों का एक उचित-गोल औसत गणना करना आसान हो सकता है। बस, औसत दिए गए औसत को लेने से पहले प्रत्येक संख्या में 0x8000000000000000+ जोड़ें, परिणाम लेने के बाद इसे घटाएं, और Int64हस्ताक्षरित औसत प्राप्त करने के लिए एक अनियंत्रित कास्ट का उपयोग करें ।

अहस्ताक्षरित औसत की गणना करने के लिए, तीन मानों के शीर्ष 32 बिट्स के योग की गणना करें। फिर तीन मानों के निचले 32 बिट्स की राशि की गणना करें, साथ ही ऊपर से योग, प्लस एक [प्लस एक गोल परिणाम प्राप्त करने के लिए है]। औसत पहली राशि का 0x55555555 गुना होगा, दूसरे का एक तिहाई।

32-बिट प्रोसेसर पर प्रदर्शन तीन "योग" मानों को बढ़ाकर किया जा सकता है, जिनमें से प्रत्येक 32 बिट लंबा है, ताकि अंतिम परिणाम हो ((0x55555555UL * sumX)<<32) + 0x55555555UL * sumH + sumL/3; इसे संभवतः आगे के sumL/3साथ बदलकर बढ़ाया जा सकता है ((sumL * 0x55555556UL) >> 32), हालांकि बाद वाला जेआईटी ऑप्टिमाइज़र पर निर्भर करेगा [यह जान सकता है कि किसी डिवीजन को 3 से गुणा करके कैसे बदला जा सकता है, और इसका कोड वास्तव में एक स्पष्ट मल्टीप्ल ऑपरेशन की तुलना में अधिक कुशल हो सकता है]।


0x8000000000000000+ जोड़ने के बाद ओवरफ़्लो परिणाम को प्रभावित नहीं करता है?
फुकुव

@ LưuV LnhPhúc कोई अतिप्रवाह नहीं है। एक कार्यान्वयन के लिए मेरे उत्तर पर जाएं । 2 32 बिट इंट में विभाजन हालांकि अनावश्यक था।
केविनजेड

@ केवीएनजेड: प्रत्येक मूल्य को एक ऊपरी और निचले 32-बिट भाग में विभाजित करना, इसे विभाजित-तीन भाग भाग और शेष में विभाजित करने की तुलना में तेज़ है।
सुपरकैट

1
@ LưuV LnhPhúc: हस्ताक्षरित मूल्यों के विपरीत, जो संख्याओं की तरह व्यवहार करते हैं और एक वैध सी कार्यक्रम में अतिप्रवाह की अनुमति नहीं दी जाती है, अहस्ताक्षरित मान आमतौर पर एक रैपिंग एब्जेक्टिव बीजीय रिंग के सदस्यों की तरह व्यवहार करते हैं, इसलिए रैपिंग शब्दार्थ अच्छी तरह से परिभाषित होते हैं।
सुपरकैट

1
टपल -3, -2, -1 का प्रतिनिधित्व करता है। प्रत्येक मान में 0x8000U जोड़े जाने के बाद, मानों को फिर से आधा: 7F + FF 7F + FE 7F + FD में विभाजित किया जाना चाहिए। ऊपरी और निचले हिस्सों को जोड़ें, 17D + 2FA उपज। टॉप-हाफ समरी को बॉटम-हाफ समयावधि 477 में जोड़ें। 55D 7E81 को 17D से गुणा करें। 477 को तीन उपज 17D से विभाजित करें। 7E81 से 17D उपज वाले 7D81 जोड़ें। उसमें से 8000 घटाएं और -2 प्राप्त करें।
सुपरकाट

5

पैचिंग पैट्रिक Hofman के साथ समाधान supercat के सुधार, मैं आप निम्नलिखित दे:

static Int64 Avg3 ( Int64 x, Int64 y, Int64 z )
{
    UInt64 flag = 1ul << 63;
    UInt64 x_ = flag ^ (UInt64) x;
    UInt64 y_ = flag ^ (UInt64) y;
    UInt64 z_ = flag ^ (UInt64) z;
    UInt64 quotient = x_ / 3ul + y_ / 3ul + z_ / 3ul
        + ( x_ % 3ul + y_ % 3ul + z_ % 3ul ) / 3ul;
    return (Int64) (quotient ^ flag);
}

और एन तत्व मामला:

static Int64 AvgN ( params Int64 [ ] args )
{
    UInt64 length = (UInt64) args.Length;
    UInt64 flag = 1ul << 63;
    UInt64 quotient_sum = 0;
    UInt64 remainder_sum = 0;
    foreach ( Int64 item in args )
    {
        UInt64 uitem = flag ^ (UInt64) item;
        quotient_sum += uitem / length;
        remainder_sum += uitem % length;
    }

    return (Int64) ( flag ^ ( quotient_sum + remainder_sum / length ) );
}

यह हमेशा माध्य का फर्श () देता है, और हर संभव किनारे के मामले को समाप्त करता है।


1
मैंने एक्सप्रेस 3 को Z3 कोड में अनुवादित किया और सभी उचित इनपुट आकारों (जैसे 1 <= args.Length <= 5 और 6 के बिटवेक्टर आकार) के लिए यह सही साबित हुआ। यह उत्तर सही है।
usr

अद्भुत जवाब केविन। आपके योगदान के लिए धन्यवाद! meta.stackoverflow.com/a/303292/993547
पैट्रिक हॉफमैन

4

आप इस तथ्य का उपयोग कर सकते हैं कि आप प्रत्येक संख्या को लिख सकते हैं y = ax + b, जहां xएक स्थिर है। प्रत्येक aहोगा y / x(कि विभाजन का पूर्णांक भाग)। प्रत्येक बी होगाy % x (उस विभाजन के बाकी / मोडुलो) होगा। यदि आप बुद्धिमान तरीके से इस स्थिरांक का चयन करते हैं, उदाहरण के लिए एक स्थिरांक के रूप में अधिकतम संख्या का वर्गमूल चुनकर, आप औसत प्राप्त कर सकते हैंx अतिप्रवाह के साथ समस्याओं के बिना संख्याओं ।

संख्याओं की एक मनमानी सूची का औसत खोजकर पाया जा सकता है:

( ( sum( all A's ) / length ) * constant ) + 
( ( sum( all A's ) % length ) * constant / length) +
( ( sum( all B's ) / length )

जहां %modulo और दर्शाता है/ दर्शाता है विभाजन के 'संपूर्ण' भाग को दर्शाता है।

कार्यक्रम कुछ इस तरह दिखेगा:

class Program
{
    static void Main()
    {
        List<long> list = new List<long>();
        list.Add( long.MaxValue );
        list.Add( long.MaxValue - 1 );
        list.Add( long.MaxValue - 2 );

        long sumA = 0, sumB = 0;
        long res1, res2, res3;
        //You should calculate the following dynamically
        long constant = 1753413056;

        foreach (long num in list)
        {
            sumA += num / constant;
            sumB += num % constant;
        }

        res1 = (sumA / list.Count) * constant;
        res2 = ((sumA % list.Count) * constant) / list.Count;
        res3 = sumB / list.Count;

        Console.WriteLine( res1 + res2 + res3 );
    }
}

4

यदि आप जानते हैं कि आपके पास एन मान हैं, तो क्या आप एन द्वारा प्रत्येक मूल्य को विभाजित कर सकते हैं और उन्हें एक साथ जोड़ सकते हैं?

long GetAverage(long* arrayVals, int n)
{
    long avg = 0;
    long rem = 0;

    for(int i=0; i<n; ++i)
    {
        avg += arrayVals[i] / n;
        rem += arrayVals[i] % n;
    }

    return avg + (rem / n);
}

यह पैट्रिक हॉफ़मैन के समाधान के समान ही है, अगर कम सही नहीं है कि अंतिम संस्करण
phuclv

2

मैंने इसे भी आजमाया और तेजी से समाधान के साथ आया (हालांकि केवल एक कारक के बारे में 3/4)। यह एक एकल विभाजन का उपयोग करता है

public static long avg(long a, long b, long c) {
    final long quarterSum = (a>>2) + (b>>2) + (c>>2);
    final long lowSum = (a&3) + (b&3) + (c&3);
    final long twelfth = quarterSum / 3;
    final long quarterRemainder = quarterSum - 3*twelfth;
    final long adjustment = smallDiv3(lowSum + 4*quarterRemainder);
    return 4*twelfth + adjustment;
}

जहां smallDiv33 multipliation का उपयोग कर और छोटे तर्क के लिए केवल काम करके प्रभाग है

private static long smallDiv3(long n) {
    assert -30 <= n && n <= 30;
    // Constants found rather experimentally.
    return (64/3*n + 10) >> 6;
}

यहाँ एक परीक्षण और एक बेंचमार्क सहित पूरा कोड है , परिणाम इतने प्रभावशाली नहीं हैं।


1

यह फ़ंक्शन दो डिवीजनों में परिणाम की गणना करता है। इसे अन्य विभाजकों और शब्द आकारों में अच्छी तरह से सामान्यीकृत करना चाहिए।

यह दोहरे-शब्द जोड़ परिणाम की गणना करके काम करता है, फिर विभाजन का काम करता है।

Int64 average(Int64 a, Int64 b, Int64 c) {
    // constants: 0x10000000000000000 div/mod 3
    const Int64 hdiv3 = UInt64(-3) / 3 + 1;
    const Int64 hmod3 = UInt64(-3) % 3;

    // compute the signed double-word addition result in hi:lo
    UInt64 lo = a; Int64 hi = a>=0 ? 0 : -1;
    lo += b; hi += b>=0 ? lo<b : -(lo>=UInt64(b));
    lo += c; hi += c>=0 ? lo<c : -(lo>=UInt64(c));

    // divide, do a correction when high/low modulos add up
    return hi>=0 ? lo/3 + hi*hdiv3 + (lo%3 + hi*hmod3)/3
                 : lo/3+1 + hi*hdiv3 + Int64(lo%3-3 + hi*hmod3)/3;
}

0

गणित

(x + y + z) / 3 = x/3 + y/3 + z/3

(a[1] + a[2] + .. + a[k]) / k = a[1]/k + a[2]/k + .. + a[k]/k

कोड

long calculateAverage (long a [])
{
    double average = 0;

    foreach (long x in a)
        average += (Convert.ToDouble(x)/Convert.ToDouble(a.Length));

    return Convert.ToInt64(Math.Round(average));
}

long calculateAverage_Safe (long a [])
{
    double average = 0;
    double b = 0;

    foreach (long x in a)
    {
        b = (Convert.ToDouble(x)/Convert.ToDouble(a.Length));

        if (b >= (Convert.ToDouble(long.MaxValue)-average))
            throw new OverflowException ();

        average += b;
    }

    return Convert.ToInt64(Math.Round(average));
}

{1,2,3}उत्तर के सेट के लिए है 2, लेकिन आपका कोड वापस आ जाएगा 1
उलुग्बे उमीरोव

@UlugbekUmirov कोड तय किया गया है, को प्रसंस्करण के लिए दोहरे प्रकारों का उपयोग करना चाहिए
खालिद.के।

1
यही कारण है कि मैं बचना चाहता हूं - का उपयोग double, क्योंकि हम ऐसे मामले में सटीक खो रहे हैं।
उलुगबेक उमिरोव

0

इसे इस्तेमाल करे:

long n = Array.ConvertAll(new[]{x,y,z},v=>v/3).Sum()
     +  (Array.ConvertAll(new[]{x,y,z},v=>v%3).Sum() / 3);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.