2010-08-31 10 views
5

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

उदाहरण:

Description | Actual | Rounded 
=============================== 
Allocation A | 65.23% | 65% 
Allocation B | 25.40% | 25% 
Allocation C | 7.95% | 8% 
Allocation D | 1.42% | 1% 
=============================== 
Total  | 100% | 99% (Bad!) 

अनुरोध किया समाधान है, जो अपूर्ण है लेकिन क्या करेंगे, उच्चतम एक नीचे पूर्ण होना मिल रहा है, और यह दौर अप बजाय। ऊपर दिए गए उदाहरण में, गोलाकार होने पर 1.42% 2% हो जाएगा। संपादित करें: "सर्वोच्च व्यक्ति को गोल करने के लिए" मेरा मतलब है कि वह सबसे दूर है। इसलिए 1.42% 0.42 से नीचे गोल किया जा रहा है, जबकि 65.23 केवल कोड 0.23

पूर्णांक किया जा रहा है तो अब, मैं एक वर्ग

public class Allocation 
{ 
    public string Description {get;set;} 
    public doubel Percentage {get;set;} 
} 

है और ये एक IEnumerable<Allocation> में आयोजित की जाती हैं। तो, संभावित रूप से LINQ का उपयोग करके, मैं कैसे निर्धारित कर सकता हूं कि गोल करने वाला कौन सा है। या अधिक विशेष रूप से, मैं गोल संख्याओं के साथ एक नया IEnumerable<Allocation> कैसे उत्पन्न कर सकता हूं।

यदि किसी के पास हमेशा गोलाकार प्रतिशत बनाने के लिए कोई अन्य सुझाव है तो हमेशा 100% के बराबर होता है जो इससे भी बेहतर होगा!

+0

आवश्यकताओं और एल्गोरिथ्म पर एक टिप्पणी: ऐसा लगता है कि आप यह निर्धारित करने के लिए कितना अधिक या 100 के तहत अपने गोल मान रहे हैं, तो के रूप में कई मूल्यों के रूप में उन्हें दूसरी तरह गोलाई की जरूरत को समायोजित करना चाहते हैं। ऐसा करने पर, आप कम से कम त्रुटि पेश करना चाहते हैं। इसलिए, आपके द्वारा दिए गए डेटा के लिए, आवंटन बी को गोलाकार किया जाना चाहिए क्योंकि 42 उच्चतम आंशिक मान है जो गोल किया गया था। क्या यह सही लग रहा है? –

+0

ओह, और मुझे एक और चीज़ में टॉस करने दें: चूंकि ये वित्तीय मान दो दशमलव स्थानों पर रखे जाते हैं, इसलिए आपको 'दशमलव' का उपयोग करना चाहिए, न कि 'डबल'। –

+0

सुपरकैट का जवाब सवाल उठाता है कि हम कम से कम त्रुटि को कैसे परिभाषित करते हैं। क्या हमारा मतलब कम से कम पूर्ण त्रुटि या कम से कम सापेक्ष त्रुटि है? –

उत्तर

3

जैसा कि हो 1 ने संकेत दिया है, एक विशिष्ट पंक्ति में 1 जोड़ने का समाधान वास्तविक समस्या को हल नहीं करता है।

इन परिदृश्यों पर विचार करें:

3 items evenly divided, 100/3 = 33 ; 33 * 3 = 99 ; Error = -1 
7 items evenly divided, 100/7 = 14 ; 14 * 7 = 98 ; Error = -2 
66 items evenly divided, 100/66 = 2 ; 2 * 66 = 132 ; Error = 32 

यहाँ कुछ अपरीक्षित कोड है कि आप जहाँ आप जाने की जरूरत के करीब हो सकती है है। यहां शायद एक साइन त्रुटि है तो देखें।

public class AllocationRoundingWrapper 
{ 
    public Allocation Original {get;set;} 
    public double Rounded {get;set;} 
    public double IntroducedError() 
    { 
    return Rounded - Original.Percentage; 
    } 
} 

    //project the Allocations into Wrappers for rounding efforts. 

List<Allocation> source = GetAllocations(); 

List<AllocationRoundingWrapper> roundingWrappers = source 
    .Select(a => new AllocationRoundingWrapper() 
    { 
    Original = a, 
    Rounded = Math.Round(a.Percentage) 
    }).ToList(); 

int error = (int) roundingWrappers.Sum(x => x.IntroducedError()); 

    //distribute the rounding errors across the 
    // items with the absolute largest error. 

List<RoundingWrapper> orderedItems = error > 0 ? 
    roundingWrappers.OrderByDescending(x => x.IntroducedError()).ToList() : 
    roundingWrappers.OrderBy(x => x.IntroducedError()).ToList(); 

IEnumerator<RoundingWrapper> enumerator = orderedItems.GetEnumerator(); 

while(error > 0) 
{ 
    enumerator.MoveNext(); 
    enumerator.Current.Rounded += 1.0; 
    error -= 1; 
} 
while(error < 0) 
{ 
    enumerator.MoveNext(); 
    enumerator.Current.Rounded -= 1.0; 
    error += 1; 
} 

    //project back into Allocations for the result 
List<Allocation> result = roundingWrappers 
    .Select(x => new Allocation() 
    { 
    Description = x.Original.Description, 
    Percentage = x.Rounded 
    }).ToList(); 

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

+0

+1, हालांकि, ओपी ने नोट किया कि वास्तविक, @ 2 डीपी 100% की गारंटी है, इसलिए आपके परिदृश्य असंभव हैं। –

+0

सबसे खराब 67 बराबर उपाय है: '67 आइटम समान रूप से विभाजित, 100/67 = 1; 1 * 67 = 67; त्रुटि = -33' –

+0

@ डेविड बी ~ मेरा बुरा, जब मैंने +1 कहा, मेरा मतलब था ... बस देरी हुई :) क्षमा करें। इसके अलावा 100/7 = ~ 14.2857 और 14.2 9 * 7 = 100.03 –

2

100% प्राप्त करने के संबंध में, मूल गणना को पहले क्यों न चलाएं और देखें कि आपको कौन सा प्रतिशत मिलता है और फिर आप जानते हैं कि 100% से कितने प्रतिशत अंक अलग-अलग हैं, यह देखते हुए आपको कितनी बार बनाम नीचे की आवश्यकता है।

तो यदि आप 97% के साथ समाप्त होते हैं, तो नीचे की बजाय 3 संख्याएं ऊपर। या यदि आप 102% के साथ समाप्त होते हैं, तो दो संख्याओं को नीचे की ओर सबसे कम दशमलव (0.5 से अधिक) के साथ नीचे रखें।

+0

बस स्पष्ट करने के लिए, यह केवल 99% हो सकता है या 100% - और यह निर्धारित करना कि मेरे पास 99% की स्थिति कोई समस्या नहीं है - यह जानकर कि सेट में कौन सा आइटम नीचे की ओर बढ़ रहा है। – Jamiec

+0

@ जैमिक: पर्याप्त मेला, लेकिन जिज्ञासा से बाहर, क्या आप वास्तव में सुनिश्चित हैं कि यह केवल 99% या 100% हो सकता है? क्या आपके पास यह सुनिश्चित करने के लिए कुछ और जांच है कि मूल्य कुछ ऐसा नहीं हो सकता था: 65.47, 25.51, 7.51 और 1.51? –

+2

हो 1 के साथ सहमत हैं। क्या आप कह रहे हैं कि यह केवल 99 हो सकता है क्योंकि आप सब कुछ सामना कर चुके हैं या वास्तव में इसे रोकने का व्यवसाय नियम है? एक 101 मामले के लिए 50.50 + 49.50 देखें। इस मामले के अभ्यास के लिए थोड़ा सा डोमेन ज्ञान जोड़ने के लिए, जहां 100%/16 को गोलाकार = 100 और अनगिनत = 96. –

1
var HighestDown = 
     allocation.Where(a=>Math.Round(a.Percentage) == Math.Floor(a.Percentage) 
       .Max(a=>a.Percentage - Math.Floor(a.Percentage)); 

    HighestDown.Percentage = Math.Ceiling(HighestDown.Percentage); 

    var roundedAllocations = for a in allocation 
          select new Allocation 
            { 
             Description = a.Description, 
             Percentage = Math.Round(a.Percentage) 
            }; 
+0

उत्तर के लिए धन्यवाद, यह आशाजनक और स्थानीय रूप से कोशिश करने के समान दिखता है, सिवाय इसके कि आपको सबसे दूर तक गोल करने के बजाए सबसे ज्यादा संख्या मिलती है। ओपी में मेरा संपादन देखें। – Jamiec

+0

@Jamiec: मेरे जवाब में मेरा संपादन देखें (पीएस: बढ़ रहा था मैं "जेमी सी" था) –

+0

Curren - मेरा आखिरी नाम कोहेन है। मेरा असली नाम जेम्स है। मैं आपको बता नहीं सकता कि टेलीफोन पर "जेम्स Curran" के रूप में कितनी बार मेरा नाम सुना है। Spoooky! – Jamiec

3

मैं हमेशा गोल करने का सुझाव देता हूं, और फिर यदि परिणाम 100-एन है, तो 'एन' के सबसे बड़े अवशेषों के साथ संख्याओं को गोल करें। यह किसी भी डेटा के लिए काम करेगा। दृष्टिकोण जो निकटतम दौर में हैं और फिर परिणाम को समायोजित करने का प्रयास अधिक जटिल होने के लिए उपयुक्त हैं। मुझे लगता है कि आवंटन 0.01% तक गोलाकार, 100.00% तक जोड़ता है, जो कुछ होने जा रहा है, जब वे निकटतम 0.1% या 1% तक गोल होते हैं।

एक और दृष्टिकोण राउंड-टू-नजदीक के साथ प्रारंभिक गणना करना होगा, और फिर परिणाम 100% प्रत्येक प्रतिशत को कुल प्रतिशत से विभाजित नहीं करेगा और फिर से प्रयास करें। तो यदि अंतिम प्रतिशत 101% था, तो सभी (आस-पास) संख्याओं को 1 से विभाजित करें।01 और राउंड-एंड-कुल अनुक्रम दोहराएं। यह थोड़ा अलग परिणाम देगा जो किसी को कम या ज्यादा वांछनीय मिल सकता है। मान लीजिए कि संख्या 1.3 1.3 1.3 96.1 है। गोल होने पर, उन कुल 99. 1.3 के ऊपर 2 में से एक को गोल करने से कुल 100 मिल जाएगा, लेकिन गोल करने से मूल्य 23% के बजाय 53% तक विकृत हो जाएगा; इसके विपरीत, 96.1 तक 97 तक गोल करने से उसके मूल्य के 0.95% विरूपण (9 7 बनाम 96.1) का प्रतिनिधित्व होगा।

+0

मुझे यह पसंद है। पहला विचार एक स्वच्छ, समझने योग्य समाधान है जो सही ढंग से कार्यान्वित करना आसान होगा। –

+0

मैं दूसरा विचार फिर से पढ़ रहा हूं, और जब यह थोड़ा जटिल लगता है, तो यह एक अच्छा बिंदु उठाता है: क्या यह 1.4 से 2.0 या 60.3 से 60.1 तक बेहतर है? दूसरे शब्दों में, क्या हम प्रति आवंटन आधार पर विरूपण को कम करने की कोशिश कर रहे हैं? –

+0

यहां दूसरा विचार सभी डेटा सेट के लिए काम नहीं करता है। इन नंबरों का उपयोग करें और इसे आजमाएं: [1 9 .6,19.7,19.7,20.5,20.5] उनका योग 100 है, लेकिन उनके चारों ओर है और वे 102 में जोड़ते हैं। उन्हें नीचे और गोल करें, वे 97 में जोड़ें। – Mnebuerquo

0

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

static List<double> Round2(List<double> actualAllocations) 
    { 
     List<double> actual = new List<double>(); 
     actual.AddRange(actualAllocations); 

     List<double> rounded = new List<double>(); 
     foreach (var a in actual) 
      rounded.Add(Math.Round(a)); 

     if (rounded.Sum() == 100) 
     { 
      return rounded; 
     } 
     else 
     { 
      bool roundUp = rounded.Sum() < 100; 

      for (int i = 0; i < Math.Abs(100 - rounded.Sum()); i++) 
      { 
       var index = actual.IndexOf(
        (from a in actual 
        orderby Math.Abs(Math.Round(a) - a) descending 
        select a).First()); 

       if (roundUp) 
        actual[index]++; 
       else 
        actual[index]--; 
      } 
     } 

     rounded.Clear(); 
     foreach (var a in actual) 
      rounded.Add(Math.Round(a)); 

     return rounded; 
    } 
संबंधित मुद्दे