गोल प्रतिशत बनाने के लिए 100% तक कैसे जोड़ें


192

floatसंख्या के रूप में प्रतिनिधित्व नीचे चार प्रतिशत पर विचार करें :

    13.626332%
    47.989636%
     9.596008%
    28.788024%
   -----------
   100.000000%

मुझे पूरे प्रतिशत के रूप में इन प्रतिशतों का प्रतिनिधित्व करने की आवश्यकता है। यदि मैं बस उपयोग करता Math.round()हूं, तो मैं कुल 101% के साथ समाप्त होता हूं।

14 + 48 + 10 + 29 = 101

यदि मैं उपयोग करता हूं parseInt(), तो मैं कुल 97% के साथ समाप्त होता हूं।

13 + 47 + 9 + 28 = 97

कुल संख्या को 100% बनाए रखते हुए किसी भी संख्या को प्रतिशत के रूप में दर्शाने के लिए एक अच्छा एल्गोरिथ्म क्या है?


संपादित करें : कुछ टिप्पणियों और उत्तरों को पढ़ने के बाद, इसे हल करने के बारे में स्पष्ट रूप से कई तरीके हैं।

मेरे दिमाग में, संख्याओं के प्रति सही बने रहने के लिए, "सही" परिणाम वह है जो समग्र त्रुटि को कम करता है, वास्तविक मूल्य के सापेक्ष कितना त्रुटि गोलाई से परिभाषित होता है:

        value  rounded     error               decision
   ----------------------------------------------------
    13.626332       14      2.7%          round up (14)
    47.989636       48      0.0%          round up (48)
     9.596008       10      4.0%    don't round up  (9)
    28.788024       29      2.7%          round up (29)

एक टाई (3.33, 3.33, 3.33) के मामले में एक मनमाना निर्णय लिया जा सकता है (उदाहरण 3, 4, 3)।


21
मान लीजिए कि आपके पास 3.33, 3.33 और 3.33 हैं। आप 4 को कौन सा बनायेंगे?
रॉब

3
बिल्कुल सही। सवाल शब्दों में विरोधाभास का प्रतीक है।
२०:५० पर लोर्ने

4
यह रिपोर्टिंग में एक बहुत ही सामान्य परिदृश्य है - दशमलव मानों का "कुल" कैसे प्रदर्शित किया जाए जो हमेशा प्रदर्शित मूल्यों के योग से मेल नहीं खाता।
डी स्टेनली

1
आपके उदाहरण के मामले में "सही" परिणाम क्या है ? इससे असहमति का हल हो सकता है कि "सबसे अच्छा" समाधान क्या है।
डी स्टेनली

जवाबों:


35

चूँकि यहाँ कोई भी उत्तर ठीक से हल नहीं होता है, यहाँ अंडरस्कोरजेस का उपयोग करते हुए मेरा अर्ध-अस्पष्ट संस्करण है :

function foo(l, target) {
    var off = target - _.reduce(l, function(acc, x) { return acc + Math.round(x) }, 0);
    return _.chain(l).
            sortBy(function(x) { return Math.round(x) - x }).
            map(function(x, i) { return Math.round(x) + (off > i) - (i >= (l.length + off)) }).
            value();
}

foo([13.626332, 47.989636, 9.596008, 28.788024], 100) // => [48, 29, 14, 9]
foo([16.666, 16.666, 16.666, 16.666, 16.666, 16.666], 100) // => [17, 17, 17, 17, 16, 16]
foo([33.333, 33.333, 33.333], 100) // => [34, 33, 33]
foo([33.3, 33.3, 33.3, 0.1], 100) // => [34, 33, 33, 0]

6
अगर मैं गलत हूं तो मुझे सुधारें, लेकिन क्या यह मेरे उत्तर द्वारा प्रस्तावित एल्गोरिथम का कार्यान्वयन नहीं है? (अंडरस्कोरज पर स्पष्ट नहीं)
vvohra87

@VarunVohra क्षमा करें, मैंने अब तक इस पर ध्यान नहीं दिया, हाँ ऐसा लगता है कि आपका अल्गोरिथम ऐसा ही है :) सुनिश्चित नहीं है कि मेरी पोस्ट स्वीकार किए गए उत्तर के लिए क्यों नहीं है,
आपत्तिजनक

@yonilevy ने मेरी टिप्पणी हटा दी; मैंने अभी महसूस नहीं किया कि यह एक क्रमबद्ध सूची को वापस करना था। मैं क्षमाप्रार्थी हूं!
ज़ैक बर्ट

2
जब अंतिम तत्व 0 होता है और पिछले वाले 100 होते हैं, तो इस फ़ंक्शन के साथ एकरूपता होती है। उदाहरण के लिए [52.6813880126263, 5.941114616193481, 24.55310199789695, 8.723231335436383, 8.04416403785489, 0]। अंतिम एक तार्किक रिटर्न -1 है। मैंने निम्नलिखित समाधान के बारे में सोचा था कि यह वास्तव में जल्दी है, लेकिन शायद कुछ बेहतर है: jsfiddle.net/0o75bw43/1
क्रुकैक्स

1
@Cruclax यह सब 1 दिखाता है जब सभी प्रविष्टियाँ इनपुट ऐरे में शून्य हैं
tony.0919

158

ऐसा करने के कई तरीके हैं, बशर्ते आप मूल दशमलव डेटा पर निर्भरता के बारे में चिंतित न हों।

पहली और शायद सबसे लोकप्रिय विधि सबसे बड़ी विधि होगी

जो मूल रूप से है:

  1. सब कुछ गोल करके
  2. योग और 100 में अंतर प्राप्त करना
  3. उनके दशमलव भागों के घटते क्रम में 1 आइटम जोड़कर अंतर को वितरित करना

आपके मामले में, यह इस तरह होगा:

13.626332%
47.989636%
 9.596008%
28.788024%

यदि आप पूर्णांक भागों को लेते हैं, तो आप प्राप्त करते हैं

13
47
 9
28

जो 97 तक जोड़ता है, और आप तीन और जोड़ना चाहते हैं। अब, आप दशमलव भागों को देखते हैं, जो हैं

.626332%
.989636%
.596008%
.788024%

और जब तक कुल 100 नहीं पहुंच जाता, तब तक सबसे बड़ा ले लो। तो आपको मिलेगा:

14
48
 9
29

वैकल्पिक रूप से, आप पूर्णांक मानों के बजाय एक दशमलव स्थान दिखाने के लिए चुन सकते हैं। तो संख्या 48.3 और 23.9 होगी। यह बहुत से 100 से विचरण छोड़ देगा।


5
अमेरिकन मैथमैटिकल सोसाइटी की वेबसाइट पर यह "फ़ीचर कॉलम" - अपॉइंटमेंट II: अपॉइंटमेंट सिस्टम - समान कई 'अपॉर्चुनमेंट' विधियों का वर्णन करता है।
केनी एविट

1
यह लगभग मेरे उत्तर की एक प्रति और पेस्ट की तरह दिखता है, stackoverflow.com/questions/5227215/…
आरा

ध्यान दें कि, @DStanley के उत्तर पर आपकी टिप्पणी के विपरीत, आपके उत्तर में 9.596008% को 9% तक गोल किया गया था जो 0.5% से अधिक अंतर है। अभी भी एक अच्छा जवाब है, हालांकि।
रोलाजारो एज़वेयर्स

32

संभवतः ऐसा करने का "सबसे अच्छा" तरीका ("सर्वश्रेष्ठ" के बाद से उद्धृत किया गया) एक चालू (गैर-अभिन्न) टैली है जहाँ आप हैं, और उस मान को गोल रखना है ।

फिर उस मूल्य का उपयोग करने के लिए इतिहास के साथ-साथ उपयोग करें। उदाहरण के लिए, आपके द्वारा दिए गए मूल्यों का उपयोग करना:

Value      CumulValue  CumulRounded  PrevBaseline  Need
---------  ----------  ------------  ------------  ----
                                  0
13.626332   13.626332            14             0    14 ( 14 -  0)
47.989636   61.615968            62            14    48 ( 62 - 14)
 9.596008   71.211976            71            62     9 ( 71 - 62)
28.788024  100.000000           100            71    29 (100 - 71)
                                                    ---
                                                    100

प्रत्येक चरण में, आप स्वयं संख्या को गोल नहीं करते हैं। इसके बजाय, आप संचित मूल्य को गोल करते हैं और पिछले आधार रेखा से उस मूल्य तक पहुंचने वाले सर्वश्रेष्ठ पूर्णांक को काम करते हैं - वह आधार रेखा पिछली पंक्ति का संचयी मान (गोल) है।

यह काम करता है क्योंकि आप प्रत्येक चरण में जानकारी नहीं खो रहे हैं , बल्कि अधिक समझदारी से जानकारी का उपयोग कर रहे हैं। 'सही' गोल मान अंतिम कॉलम में हैं और आप देख सकते हैं कि वे 100 के योग हैं।

आप ऊपर के तीसरे मूल्य में, इसके और प्रत्येक मूल्य को नेत्रहीन रूप से गोल करने के बीच का अंतर देख सकते हैं। जबकि 9.596008सामान्य रूप से ऊपर की ओर गोल होगा 10, संचित 71.211976सही ढंग से नीचे की ओर घूमता है 71- इसका मतलब है कि केवल 9पिछले बेसलाइन में जोड़ने की आवश्यकता है 62


यह "समस्याग्रस्त" अनुक्रम के लिए भी काम करता है जैसे तीन मोटे तौर पर- मूल्य, जहां उनमें से एक को गोल किया जाना चाहिए:1/3

Value      CumulValue  CumulRounded  PrevBaseline  Need
---------  ----------  ------------  ------------  ----
                                  0
33.333333   33.333333            33             0    33 ( 33 -  0)
33.333333   66.666666            67            33    34 ( 67 - 33)
33.333333   99.999999           100            67    33 (100 - 67)
                                                    ---
                                                    100

1
दूसरा दृष्टिकोण उन दोनों समस्याओं को ठीक करता है। पहला देता है 26, 25, 26, 23, दूसरा देता है 1, 0, 1, 0, 1, 0, ...
paxdiablo

यह दृष्टिकोण छोटी संख्याओं को गोल करने के लिए भी अच्छी तरह से काम करता है क्योंकि यह नकारात्मक संख्या के पाप को रोकता है
Jonty5817

19

गोलाई का लक्ष्य कम से कम त्रुटि उत्पन्न करना है। जब आप किसी एकल मान को राउंड कर रहे होते हैं, तो यह प्रक्रिया सरल और सीधी होती है और अधिकांश लोग इसे आसानी से समझ लेते हैं। जब आप एक ही समय में कई नंबरों को राउंड कर रहे होते हैं, तो प्रक्रिया पेचीदा हो जाती है - आपको यह परिभाषित करना होगा कि कैसे गलतियाँ गठबंधन करने जा रही हैं, यानी कम से कम क्या होना चाहिए।

वरुण वोहरा द्वारा अच्छी तरह से मतदान जवाब निरपेक्ष त्रुटियों की राशि को कम करता है, और इसे लागू करने के लिए बहुत आसान है। हालांकि ऐसे किनारे मामले हैं जो इसे नहीं संभालते हैं - गोलाई का परिणाम क्या होना चाहिए 24.25, 23.25, 27.25, 25.25? उनमें से एक को नीचे के बजाय गोल करने की आवश्यकता है। आप शायद सूची में पहले या अंतिम एक को मनमाने ढंग से चुनेंगे।

शायद पूर्ण त्रुटि के बजाय सापेक्ष त्रुटि का उपयोग करना बेहतर है । २४.२५ को २४ तक गोल करने पर २.२% तक बदल जाता है जबकि २ up.२५ को २ up को गोल करने से २.%% तक ही बदल जाता है। अब एक स्पष्ट विजेता है।

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

पूरा एल्गोरिथ्म इस प्रकार है:

  1. उन सभी को पूरा करने के बाद प्रतिशत घटाएं, और 100 से घटाएं। इससे आपको पता चलता है कि उन प्रतिशत में से कितने के बजाय गोल होना चाहिए।
  2. प्रत्येक प्रतिशत के लिए दो त्रुटि स्कोर उत्पन्न करें, जब एक राउंड डाउन और एक राउंड अप होने पर। दोनों के बीच अंतर करें।
  3. ऊपर उत्पन्न त्रुटि अंतर को सॉर्ट करें।
  4. उन प्रतिशत की संख्या के लिए जिन्हें गोल करने की आवश्यकता होती है, क्रमबद्ध सूची से एक आइटम लेते हैं और 1 के साथ गोल नीचे प्रतिशत बढ़ाते हैं।

उदाहरण के लिए, आपके पास एक ही त्रुटि योग के साथ एक से अधिक संयोजन हो सकते हैं 33.3333333, 33.3333333, 33.3333333। यह अपरिहार्य है, और परिणाम पूरी तरह से मनमाना होगा। कोड मैं नीचे दिए गए मानों को बाईं ओर गोल करने के लिए पसंद करता हूं।

अजगर में यह सब एक साथ रखना इस तरह दिखता है।

def error_gen(actual, rounded):
    divisor = sqrt(1.0 if actual < 1.0 else actual)
    return abs(rounded - actual) ** 2 / divisor

def round_to_100(percents):
    if not isclose(sum(percents), 100):
        raise ValueError
    n = len(percents)
    rounded = [int(x) for x in percents]
    up_count = 100 - sum(rounded)
    errors = [(error_gen(percents[i], rounded[i] + 1) - error_gen(percents[i], rounded[i]), i) for i in range(n)]
    rank = sorted(errors)
    for i in range(up_count):
        rounded[rank[i][1]] += 1
    return rounded

>>> round_to_100([13.626332, 47.989636, 9.596008, 28.788024])
[14, 48, 9, 29]
>>> round_to_100([33.3333333, 33.3333333, 33.3333333])
[34, 33, 33]
>>> round_to_100([24.25, 23.25, 27.25, 25.25])
[24, 23, 28, 25]
>>> round_to_100([1.25, 2.25, 3.25, 4.25, 89.0])
[1, 2, 3, 4, 90]

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

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


1
मुझे नहीं लगता कि आपको सभी संयोजनों पर विचार करने की आवश्यकता है: भारित त्रुटि में कमी के क्रम में प्रक्रिया शून्य से गोल तक अनंत तक जा रही है (बहुत अधिक बस वेरुन वोहरास और योनिल्वी के "समान" जवाबों में वजन का परिचय देते हुए)।
ग्रेबर्ड

@greybeard तुम सही हो, मैं इसे उखाड़ फेंक रहा था। मैं सिर्फ त्रुटि पर हल नहीं कर सकता क्योंकि प्रत्येक मान के लिए दो त्रुटियां हैं, लेकिन अंतर को हल करने से उस समस्या का समाधान हो गया। मैंने जवाब अपडेट कर दिया है।
मार्क रैनसम

मैं हमेशा 0% रखना पसंद करता हूँ जब वास्तविक संख्या 0% होती है। तो महान काम if actual == 0: return 0करने के लिए जोड़ना error_gen
निकोले बालुक

1
iscloseकी शुरुआत में विधि क्या है round_to_100?
टोटो_टिको


7

गोल संख्याओं का योग न करें। आपको गलत परिणाम मिलने वाले हैं। कुल पदों की संख्या और आंशिक भागों के वितरण के आधार पर महत्वपूर्ण रूप से बंद हो सकता है।

गोल संख्याएँ प्रदर्शित करें लेकिन वास्तविक मानों को योग करें। आप संख्याओं को कैसे प्रस्तुत कर रहे हैं, इसके आधार पर, ऐसा करने का वास्तविक तरीका अलग-अलग होगा। इस तरह आप प्राप्त करते हैं

 14
 48
 10
 29
 __
100

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

संपादित करें

आपको निम्नलिखित में से एक को चुनने की आवश्यकता है:

  1. वस्तुओं की शुद्धता
  2. योग की सटीकता (यदि आप गोल मानों को जोड़ रहे हैं)
  3. गोल आइटम और गोल राशि के बीच संगति)

जब प्रतिशत # 3 के साथ काम करते समय अधिकांश समय सबसे अच्छा विकल्प होता है क्योंकि यह अधिक स्पष्ट होता है जब कुल 101% के बराबर होता है जब व्यक्तिगत आइटम कुल 100 नहीं होते हैं, और आप अलग-अलग वस्तुओं को सटीक रखते हैं। "गोलाई" 9.596 से 9 मेरी राय में गलत है।

इसकी व्याख्या करने के लिए मैं कभी-कभी एक फुटनोट जोड़ता हूं जो बताता है कि व्यक्तिगत मान गोल हैं और कुल 100% नहीं हो सकते हैं - जो कोई भी दौर को समझता है, उस स्पष्टीकरण को समझने में सक्षम होना चाहिए।


6
यह बहुत उपयोगी नहीं है क्योंकि मुद्रित मूल्य 100 तक नहीं जुड़ेंगे। सवाल का उद्देश्य उपयोगकर्ताओं को यह सोचने से रोकने के लिए था कि मूल्य गलत हैं, जो इस मामले में, ज्यादातर लोग देखते हैं और कुल की तुलना करते हैं। ।
vvohra87

@VarunVohra ने मेरा संपादन पढ़ा, आप अपनी संख्या को ऐसे प्रदर्शित नहीं कर सकते हैं कि वे 0.5 से अधिक "राउंडिंग" के बिना 100 तक जोड़ दें।
डी स्टेनली

1
@DStanley वास्तव में, एक सेट को छोड़कर जहां सभी नंबर 0.5 से शर्मीले हैं, आप कर सकते हैं। मेरे जवाब की जाँच करें - LRM ठीक यही करता है।
vvohra87

3
@VarunVohra मूल उदाहरण में LRM में 14, 48, 9 और 29 होंगे, जो कि "राउंड" 9.596 से 9. होगा। यदि हम पूरी संख्या के आधार पर आवंटन कर रहे हैं तो LRM सबसे सटीक होगा, लेकिन यह अभी भी एक परिणाम को और बदल रहा है एक आधी इकाई से।
डी स्टेनली

7

मैंने एक C # संस्करण राउंडिंग हेल्पर लिखा है, एल्गोरिथ्म वरुण वोहरा के उत्तर के समान है , आशा है कि यह मदद करता है।

public static List<decimal> GetPerfectRounding(List<decimal> original,
    decimal forceSum, int decimals)
{
    var rounded = original.Select(x => Math.Round(x, decimals)).ToList();
    Debug.Assert(Math.Round(forceSum, decimals) == forceSum);
    var delta = forceSum - rounded.Sum();
    if (delta == 0) return rounded;
    var deltaUnit = Convert.ToDecimal(Math.Pow(0.1, decimals)) * Math.Sign(delta);

    List<int> applyDeltaSequence; 
    if (delta < 0)
    {
        applyDeltaSequence = original
            .Zip(Enumerable.Range(0, int.MaxValue), (x, index) => new { x, index })
            .OrderBy(a => original[a.index] - rounded[a.index])
            .ThenByDescending(a => a.index)
            .Select(a => a.index).ToList();
    }
    else
    {
        applyDeltaSequence = original
            .Zip(Enumerable.Range(0, int.MaxValue), (x, index) => new { x, index })
            .OrderByDescending(a => original[a.index] - rounded[a.index])
            .Select(a => a.index).ToList();
    }

    Enumerable.Repeat(applyDeltaSequence, int.MaxValue)
        .SelectMany(x => x)
        .Take(Convert.ToInt32(delta/deltaUnit))
        .ForEach(index => rounded[index] += deltaUnit);

    return rounded;
}

यह निम्नलिखित यूनिट टेस्ट पास करता है:

[TestMethod]
public void TestPerfectRounding()
{
    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> {3.333m, 3.334m, 3.333m}, 10, 2),
        new List<decimal> {3.33m, 3.34m, 3.33m});

    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> {3.33m, 3.34m, 3.33m}, 10, 1),
        new List<decimal> {3.3m, 3.4m, 3.3m});

    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> {3.333m, 3.334m, 3.333m}, 10, 1),
        new List<decimal> {3.3m, 3.4m, 3.3m});


    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> { 13.626332m, 47.989636m, 9.596008m, 28.788024m }, 100, 0),
        new List<decimal> {14, 48, 9, 29});
    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> { 16.666m, 16.666m, 16.666m, 16.666m, 16.666m, 16.666m }, 100, 0),
        new List<decimal> { 17, 17, 17, 17, 16, 16 });
    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> { 33.333m, 33.333m, 33.333m }, 100, 0),
        new List<decimal> { 34, 33, 33 });
    CollectionAssert.AreEqual(Utils.GetPerfectRounding(
        new List<decimal> { 33.3m, 33.3m, 33.3m, 0.1m }, 100, 0),
        new List<decimal> { 34, 33, 33, 0 });
}

अच्छा! मुझे शुरू करने के लिए एक आधार प्रदान किया गया था। अनगिनत मेरे पास नहीं है, हालांकि मुझे विश्वास है
Jack0fshad0ws

4

गोलाई के कारण आप अपनी त्रुटि का ट्रैक रखने का प्रयास कर सकते हैं, और फिर दाने के खिलाफ गोलाई यदि संचित त्रुटि वर्तमान संख्या के आंशिक भाग से अधिक है।

13.62 -> 14 (+.38)
47.98 -> 48 (+.02 (+.40 total))
 9.59 -> 10 (+.41 (+.81 total))
28.78 -> 28 (round down because .81 > .78)
------------
        100

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

28.78 -> 29 (+.22)
 9.59 ->  9 (-.37; rounded down because .59 > .22)
47.98 -> 48 (-.35)
13.62 -> 14 (+.03)
------------
        100

मुझे यकीन है कि ऐसे किनारे मामले हैं जहां यह टूट सकता है, लेकिन कोई भी दृष्टिकोण कम से कम कुछ हद तक मनमाना होने वाला है क्योंकि आप मूल रूप से अपने इनपुट डेटा को संशोधित कर रहे हैं।


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

कैरोलिन के ने एक्सेस वीबी 2007 में इस कार्यान्वयन का सुझाव दिया: <code> 'राउंड रिफंड डॉलर का प्रयोग "कैरी शेष" विधि Ref1 = rsQry! [Refund P $ $ $] * rsQry! [संपत्ति मान] / प्रोपवैलट ref2 = ref1 + ref5! 'किया गया शेष भाग, शून्य को Ref3 = Ref2 * 100 शुरू करने के लिए, एक पूर्णांक संख्या ref4 = ref3 / 100 में 100 से गुणा करें। 100 को दशमलव संख्या में विभाजित करें rsTbl! [Refund Paid $ $ $ = = ref4' डाल दें "! शेष "गोल संख्या में ref5 = Ref2 - Ref4 'नया शेष ले </ code>
जेफ ग्रिग

2

मैंने एक बार एक अनिर्धारित उपकरण लिखा था, एक लक्ष्य से मिलान करने के लिए संख्याओं के एक सेट के लिए न्यूनतम गड़बड़ी का पता लगाने के लिए। यह एक अलग समस्या थी, लेकिन सिद्धांत रूप में एक व्यक्ति यहां एक समान विचार का उपयोग कर सकता है। इस मामले में, हमारे पास विकल्पों का एक समूह है।

इस प्रकार, पहले तत्व के लिए, हम या तो इसे 14 तक गोल कर सकते हैं, या 13. से नीचे कर सकते हैं। ऐसा करने की लागत (एक द्विआधारी पूर्णांक प्रोग्रामिंग अर्थ में) गोल नीचे की तुलना में गोल के लिए कम है, क्योंकि गोल नीचे की आवश्यकता है उस मूल्य को एक बड़ी दूरी पर ले जाएं। इसी तरह, हम प्रत्येक संख्या को ऊपर या नीचे गोल कर सकते हैं, इसलिए कुल 16 विकल्प हैं जिन्हें हमें चुनना चाहिए।

  13.626332
  47.989636
   9.596008
+ 28.788024
-----------
 100.000000

मैं आमतौर पर MATLAB में सामान्य समस्या को हल करूंगा, यहाँ बेंटप्रोग, एक द्विआधारी पूर्णांक प्रोग्रामिंग टूल का उपयोग किया जाएगा, लेकिन परीक्षण किए जाने के लिए कुछ ही विकल्प हैं, इसलिए यह 16 विकल्पों में से प्रत्येक का परीक्षण करने के लिए सरल छोरों के साथ पर्याप्त आसान है। उदाहरण के लिए, मान लें कि हम इस सेट को गोल कर रहे थे:

 Original      Rounded   Absolute error
   13.626           13          0.62633
    47.99           48          0.01036
    9.596           10          0.40399
 + 28.788           29          0.21198
---------------------------------------
  100.000          100          1.25266

कुल पूर्ण त्रुटि 1.25266 है। इसे निम्नलिखित वैकल्पिक गोलाई से थोड़ा कम किया जा सकता है:

 Original      Rounded   Absolute error
   13.626           14          0.37367
    47.99           48          0.01036
    9.596            9          0.59601
 + 28.788           29          0.21198
---------------------------------------
  100.000          100          1.19202

वास्तव में, यह पूर्ण त्रुटि के संदर्भ में इष्टतम समाधान होगा। बेशक, यदि 20 शब्द थे, तो खोज स्थान 2 ^ 20 = 1048576 आकार का होगा। 30 या 40 शब्दों के लिए, वह स्थान महत्वपूर्ण आकार का होगा। उस मामले में, आपको एक उपकरण का उपयोग करने की आवश्यकता होगी जो कुशलता से अंतरिक्ष की खोज कर सकता है, शायद एक शाखा और बाध्य योजना का उपयोग कर।


बस भविष्य के संदर्भ के लिए: "सबसे बड़ी शेष" एल्गोरिदम को आपकी मीट्रिक के अनुसार कुल पूर्ण त्रुटि को कम करना होगा (देखें @ वरुणवोहरा का उत्तर)। प्रमाण सरल है: मान लीजिए कि यह त्रुटि को कम नहीं करता है। फिर मूल्यों का कुछ सेट होना चाहिए जो इसे नीचे गोल करता है जिसे ऊपर गोल किया जाना चाहिए, और इसके विपरीत (दो सेट समान आकार हैं)। लेकिन हर राउंड डाउन यह अगले पूर्णांक से आगे है किसी भी मूल्य की तुलना में यह राउंड अप (और वीवी) होता है इसलिए नई त्रुटि राशि अधिक होनी चाहिए। QED। हालाँकि, यह सभी त्रुटि मैट्रिक्स के लिए काम नहीं करता है; अन्य एल्गोरिदम की जरूरत है।
रिसी

2

मुझे लगता है कि निम्नलिखित आपको प्राप्त होगा जो आप के बाद हैं

function func( orig, target ) {

    var i = orig.length, j = 0, total = 0, change, newVals = [], next, factor1, factor2, len = orig.length, marginOfErrors = [];

    // map original values to new array
    while( i-- ) {
        total += newVals[i] = Math.round( orig[i] );
    }

    change = total < target ? 1 : -1;

    while( total !== target ) {

        // Iterate through values and select the one that once changed will introduce
        // the least margin of error in terms of itself. e.g. Incrementing 10 by 1
        // would mean an error of 10% in relation to the value itself.
        for( i = 0; i < len; i++ ) {

            next = i === len - 1 ? 0 : i + 1;

            factor2 = errorFactor( orig[next], newVals[next] + change );
            factor1 = errorFactor( orig[i], newVals[i] + change );

            if(  factor1 > factor2 ) {
                j = next; 
            }
        }

        newVals[j] += change;
        total += change;
    }


    for( i = 0; i < len; i++ ) { marginOfErrors[i] = newVals[i] && Math.abs( orig[i] - newVals[i] ) / orig[i]; }

    // Math.round() causes some problems as it is difficult to know at the beginning
    // whether numbers should have been rounded up or down to reduce total margin of error. 
    // This section of code increments and decrements values by 1 to find the number
    // combination with least margin of error.
    for( i = 0; i < len; i++ ) {
        for( j = 0; j < len; j++ ) {
            if( j === i ) continue;

            var roundUpFactor = errorFactor( orig[i], newVals[i] + 1)  + errorFactor( orig[j], newVals[j] - 1 );
            var roundDownFactor = errorFactor( orig[i], newVals[i] - 1) + errorFactor( orig[j], newVals[j] + 1 );
            var sumMargin = marginOfErrors[i] + marginOfErrors[j];

            if( roundUpFactor < sumMargin) { 
                newVals[i] = newVals[i] + 1;
                newVals[j] = newVals[j] - 1;
                marginOfErrors[i] = newVals[i] && Math.abs( orig[i] - newVals[i] ) / orig[i];
                marginOfErrors[j] = newVals[j] && Math.abs( orig[j] - newVals[j] ) / orig[j];
            }

            if( roundDownFactor < sumMargin ) { 
                newVals[i] = newVals[i] - 1;
                newVals[j] = newVals[j] + 1;
                marginOfErrors[i] = newVals[i] && Math.abs( orig[i] - newVals[i] ) / orig[i];
                marginOfErrors[j] = newVals[j] && Math.abs( orig[j] - newVals[j] ) / orig[j];
            }

        }
    }

    function errorFactor( oldNum, newNum ) {
        return Math.abs( oldNum - newNum ) / oldNum;
    }

    return newVals;
}


func([16.666, 16.666, 16.666, 16.666, 16.666, 16.666], 100); // => [16, 16, 17, 17, 17, 17]
func([33.333, 33.333, 33.333], 100); // => [34, 33, 33]
func([33.3, 33.3, 33.3, 0.1], 100); // => [34, 33, 33, 0] 
func([13.25, 47.25, 11.25, 28.25], 100 ); // => [13, 48, 11, 28]
func( [25.5, 25.5, 25.5, 23.5], 100 ); // => [25, 25, 26, 24]

एक आखिरी बात, मैंने वांछित आउटपुट की तुलना में मूल रूप से प्रश्न में दिए गए नंबरों का उपयोग करके फ़ंक्शन चलाया

func([13.626332, 47.989636, 9.596008, 28.788024], 100); // => [48, 29, 13, 10]

यह वही था जो सवाल करना चाहता था => [48, 29, 14, 9]। जब तक मैंने त्रुटि के कुल मार्जिन को नहीं देखा, मैं इसे समझ नहीं पाया

-------------------------------------------------
| original  | question | % diff | mine | % diff |
-------------------------------------------------
| 13.626332 | 14       | 2.74%  | 13   | 4.5%   |
| 47.989636 | 48       | 0.02%  | 48   | 0.02%  |
| 9.596008  | 9        | 6.2%   | 10   | 4.2%   |
| 28.788024 | 29       | 0.7%   | 29   | 0.7%   |
-------------------------------------------------
| Totals    | 100      | 9.66%  | 100  | 9.43%  |
-------------------------------------------------

अनिवार्य रूप से, मेरे फ़ंक्शन का परिणाम वास्तव में त्रुटि की न्यूनतम मात्रा का परिचय देता है।

इधर उधर करना


मेरे मन में जो कुछ भी था, वह इस अंतर के साथ है कि त्रुटि को मान के सापेक्ष मापा जाना चाहिए (9.8 से 10 तक गोलाई 19.8 से 20 के दौर की तुलना में एक बड़ी त्रुटि है)। यह आसानी से इसे कॉलबैक में प्रतिबिंबित करके किया जा सकता है, हालांकि।
poezn

यह [33.33, 33.33, 33.33, 0.1] के लिए गलत है, यह अधिक सटीक [34, 33, 33, 0] के बजाय [1, 33, 33, 33]
लौटाता है

@yonilevy इसके लिए धन्यवाद। अभी तय किया है।
ब्रूनो

अभी तक नहीं, [१६.६६६, १६.६६६, १६.६६६, १६.६६६, १६.६६६, १६.६६६] के लिए यह [१५, १ 16, १ 17, १ 16, १ 16, १ than] के बजाय [१६, १६, १ 16, १ 16, १ 16, १ 16] को लौटाता है - देखें मेरी उत्तर
yonilevy

2

मुझे यकीन नहीं है कि आपको किस स्तर की सटीकता की आवश्यकता है, लेकिन मैं जो करूँगा वह केवल 1 पहली nसंख्या को जोड़ना है , nदशमलव के कुल योग का छत होने के नाते। इस मामले में 3, इसलिए मैं पहली 3 वस्तुओं में 1 जोड़ दूंगा और बाकी को फर्श कर दूंगा। बेशक यह सुपर सटीक नहीं है, कुछ संख्याओं को ऊपर या नीचे गोल किया जा सकता है जब यह नहीं होना चाहिए लेकिन यह ठीक काम करता है और हमेशा 100% परिणाम देगा।

इसलिए [ 13.626332, 47.989636, 9.596008, 28.788024 ]होगा [14, 48, 10, 28]क्योंकिMath.ceil(.626332+.989636+.596008+.788024) == 3

function evenRound( arr ) {
  var decimal = -~arr.map(function( a ){ return a % 1 })
    .reduce(function( a,b ){ return a + b }); // Ceil of total sum of decimals
  for ( var i = 0; i < decimal; ++i ) {
    arr[ i ] = ++arr[ i ]; // compensate error by adding 1 the the first n items
  }
  return arr.map(function( a ){ return ~~a }); // floor all other numbers
}

var nums = evenRound( [ 13.626332, 47.989636, 9.596008, 28.788024 ] );
var total = nums.reduce(function( a,b ){ return a + b }); //=> 100

आप उपयोगकर्ताओं को हमेशा सूचित कर सकते हैं कि संख्याएँ गोल हैं और सुपर-सटीक नहीं हो सकती हैं ...


1

यदि आप इसे राउंड कर रहे हैं, तो इसे सभी मामलों में समान रूप से प्राप्त करने का कोई अच्छा तरीका नहीं है।

आप अपने पास मौजूद N प्रतिशत के दशमलव भाग को ले सकते हैं (उदाहरण में आपने जो दिया है वह 4 है)।

दशमलव भागों को जोड़ें। आपके उदाहरण में आपके पास कुल अंश = 3 है।

उच्चतम अंशों के साथ 3 नंबरों को सीवन करें और बाकी हिस्सों को फर्श करें।

(संपादन के लिए क्षमा करें)


1
जबकि यह संख्या प्रदान कर सकता है जो 100 में जोड़ता है, आप अंत में 3.9 को 3 और 25.1 को 26 में बदल सकते हैं।
रॉब

नहीं। 3.9 4 होगा और 25.1 25 होगा। मैंने कहा कि उच्चतम संख्या वाले 3 नंबरों को उच्चतम मूल्य नहीं है।

2
अगर रास्ते में बहुत सारे अंश समाप्त हो रहे हैं। 9 9.9% के 9 मान और 10.9 के एक मान का एक मूल्य है जो 9%, 8 के रूप में 10% और एक के रूप में 11% तक समाप्त होगा।
अरुणलालम

1

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

गोल न करने का एक अच्छा कारण पहले से ही है (आपको कम से कम एक नंबर मिलेगा जो "बेहतर दिखता है" लेकिन "गलत" है), और इसे कैसे हल करें (अपने पाठकों को चेतावनी दें) और यही मैं करता हूं।

मुझे "गलत" नंबर भाग पर जोड़ने दें।

मान लीजिए कि आपके पास कुछ घटनाओं के साथ तीन घटनाएँ / संस्थाएँ / ... हैं:

DAY 1
who |  real | app
----|-------|------
  A | 33.34 |  34
  B | 33.33 |  33
  C | 33.33 |  33

बाद में मूल्यों में थोड़ा परिवर्तन होता है

DAY 2
who |  real | app
----|-------|------
  A | 33.35 |  33
  B | 33.36 |  34
  C | 33.29 |  33

पहली तालिका में "गलत" संख्या होने की पहले से ही उल्लेखित समस्या है: 33.34 34 के मुकाबले 33 के करीब है।

लेकिन अब आपके पास एक बड़ी त्रुटि है। दिन 2 से दिन 1 की तुलना में, A के लिए वास्तविक प्रतिशत मूल्य में 0.01% की वृद्धि हुई, लेकिन सन्निकटन में 1% की कमी देखी गई।

यह एक गुणात्मक त्रुटि है, संभवतः काफी खराब है कि प्रारंभिक मात्रात्मक त्रुटि।

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


किसी को भी पता है कि कैसे बेहतर तालिकाओं को बनाने के लिए कृपया या तो मुझे संपादित करें या मुझे बताएं कि कैसे / कहाँ
रॉलाज़ो एज़वेयर्स

0

जांचें कि क्या यह वैध है या नहीं जहां तक ​​मेरे परीक्षण के मामले हैं, मैं यह काम पाने में सक्षम हूं।

मान लीजिए कि संख्या k है;

  1. ओडर उतरते हुए सॉर्ट प्रतिशत।
  2. अवरोही क्रम से प्रत्येक प्रतिशत पर पुनरावृति।
  3. पहले प्रतिशत के लिए k के प्रतिशत की गणना करें। आउटपुट का गणित लें।
  4. अगला k = k-1
  5. सभी प्रतिशत खपत होने तक पुनरावृति।

0

मैंने वरुण वोहरा के जवाब से विधि को सूचियों और डिकेट दोनों के लिए यहाँ लागू किया है।

import math
import numbers
import operator
import itertools


def round_list_percentages(number_list):
    """
    Takes a list where all values are numbers that add up to 100,
    and rounds them off to integers while still retaining a sum of 100.

    A total value sum that rounds to 100.00 with two decimals is acceptable.
    This ensures that all input where the values are calculated with [fraction]/[total]
    and the sum of all fractions equal the total, should pass.
    """
    # Check input
    if not all(isinstance(i, numbers.Number) for i in number_list):
        raise ValueError('All values of the list must be a number')

    # Generate a key for each value
    key_generator = itertools.count()
    value_dict = {next(key_generator): value for value in number_list}
    return round_dictionary_percentages(value_dict).values()


def round_dictionary_percentages(dictionary):
    """
    Takes a dictionary where all values are numbers that add up to 100,
    and rounds them off to integers while still retaining a sum of 100.

    A total value sum that rounds to 100.00 with two decimals is acceptable.
    This ensures that all input where the values are calculated with [fraction]/[total]
    and the sum of all fractions equal the total, should pass.
    """
    # Check input
    # Only allow numbers
    if not all(isinstance(i, numbers.Number) for i in dictionary.values()):
        raise ValueError('All values of the dictionary must be a number')
    # Make sure the sum is close enough to 100
    # Round value_sum to 2 decimals to avoid floating point representation errors
    value_sum = round(sum(dictionary.values()), 2)
    if not value_sum == 100:
        raise ValueError('The sum of the values must be 100')

    # Initial floored results
    # Does not add up to 100, so we need to add something
    result = {key: int(math.floor(value)) for key, value in dictionary.items()}

    # Remainders for each key
    result_remainders = {key: value % 1 for key, value in dictionary.items()}
    # Keys sorted by remainder (biggest first)
    sorted_keys = [key for key, value in sorted(result_remainders.items(), key=operator.itemgetter(1), reverse=True)]

    # Otherwise add missing values up to 100
    # One cycle is enough, since flooring removes a max value of < 1 per item,
    # i.e. this loop should always break before going through the whole list
    for key in sorted_keys:
        if sum(result.values()) == 100:
            break
        result[key] += 1

    # Return
    return result

0

यहाँ @ वरुण-वोहरा उत्तर का एक सरल पायथन कार्यान्वयन है:

def apportion_pcts(pcts, total):
    proportions = [total * (pct / 100) for pct in pcts]
    apportions = [math.floor(p) for p in proportions]
    remainder = total - sum(apportions)
    remainders = [(i, p - math.floor(p)) for (i, p) in enumerate(proportions)]
    remainders.sort(key=operator.itemgetter(1), reverse=True)
    for (i, _) in itertools.cycle(remainders):
        if remainder == 0:
            break
        else:
            apportions[i] += 1
            remainder -= 1
    return apportions

आप की जरूरत है math, itertools, operator


0

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

import numpy as np

def largestRemainderMethod(pd_series, decimals=1):

    floor_series = ((10**decimals * pd_series).astype(np.int)).apply(np.floor)
    diff = 100 * (10**decimals) - floor_series.sum().astype(np.int)
    series_decimals = pd_series - floor_series / (10**decimals)
    series_sorted_by_decimals = series_decimals.sort_values(ascending=False)

    for i in range(0, len(series_sorted_by_decimals)):
        if i < diff:
            series_sorted_by_decimals.iloc[[i]] = 1
        else:
            series_sorted_by_decimals.iloc[[i]] = 0

    out_series = ((floor_series + series_sorted_by_decimals) / (10**decimals)).sort_values(ascending=False)

    return out_series

-1

यह बैंकर की गोलाई के लिए एक मामला है, उर्फ ​​'राउंड हाफ-सम'। यह BigDecimal द्वारा समर्थित है। इसका उद्देश्य यह सुनिश्चित करना है कि राउंडिंग शेष हो, यानी बैंक या ग्राहक का पक्ष न लें।


5
यह सुनिश्चित नहीं करता है कि राउंडिंग शेष है - यह सिर्फ और विषम संख्याओं के बीच आधा-गोल वितरण करके त्रुटि की मात्रा को कम करता है। अभी भी ऐसे परिदृश्य हैं जहां बैंकरों के चक्कर लगाना गलत परिणाम देता है।
D स्टेनली

@DStanley सहमत। मैंने अन्यथा नहीं कहा। मैंने इसका उद्देश्य बताया । बहूत सावधानी से।
लोर्ने

2
पर्याप्त रूप से उचित - मैंने यह गलत बताया कि आप क्या कहना चाह रहे थे। किसी भी मामले में मुझे नहीं लगता कि यह समस्या को हल करता है क्योंकि बैंकरों के चक्कर लगाने से उदाहरण में परिणाम नहीं बदलेंगे।
डी स्टेनली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.