Rozważ cztery poniższe wartości procentowe przedstawione w postaci float
liczb:
13.626332%
47.989636%
9.596008%
28.788024%
-----------
100.000000%
Muszę przedstawić te wartości procentowe jako liczby całkowite. Jeśli po prostu użyję Math.round()
, w sumie otrzymam 101%.
14 + 48 + 10 + 29 = 101
Jeśli użyję parseInt()
, otrzymam w sumie 97%.
13 + 47 + 9 + 28 = 97
Jaki jest dobry algorytm do reprezentowania dowolnej liczby procentowej jako liczb całkowitych przy jednoczesnym zachowaniu w sumie 100%?
Edycja : po przeczytaniu niektórych komentarzy i odpowiedzi istnieje wyraźnie wiele sposobów rozwiązania tego problemu.
Moim zdaniem, aby pozostać wiernym liczbom, „właściwy” wynik to taki, który minimalizuje ogólny błąd, określony przez to, ile zaokrągleń błędu wprowadziłoby w stosunku do rzeczywistej wartości:
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)
W przypadku remisu (3.33, 3.33, 3.33) można podjąć dowolną decyzję (np. 3, 4, 3).
źródło
Odpowiedzi:
Ponieważ żadna z odpowiedzi tutaj nie wydaje się poprawnie rozwiązać, oto moja częściowo zaciemniona wersja przy użyciu podkreślenia :
źródło
Jest na to wiele sposobów, pod warunkiem, że nie martwisz się o poleganie na oryginalnych danych dziesiętnych.
Pierwszą i być może najbardziej popularną metodą byłaby metoda największej pozostałej
Co jest w zasadzie:
W twoim przypadku wyglądałoby to tak:
Jeśli weźmiesz części całkowite, otrzymasz
co daje w sumie 97, a chcesz dodać jeszcze trzy. Teraz patrzysz na części dziesiętne, które są
i bierz największe, aż suma osiągnie 100. Otrzymasz:
Alternatywnie możesz po prostu wybrać wyświetlanie jednego miejsca po przecinku zamiast wartości całkowitych. Tak więc liczby wynosiłyby 48,3 i 23,9 itd. To znacznie zmniejszyłoby wariancję ze 100.
źródło
Prawdopodobnie „najlepszym” sposobem na to (cytowanym, ponieważ „najlepszy” jest terminem subiektywnym) jest prowadzenie bieżącej (niezintegrowanej) oceny tego, gdzie jesteś, i robienie tego wartości.
Następnie użyj tego wraz z historią, aby dowiedzieć się, jaką wartość należy zastosować. Na przykład używając podanych wartości:
Na każdym etapie nie zaokrąglasz samej liczby. Zamiast tego zaokrąglasz nagromadzone wartość i obliczasz najlepszą liczbę całkowitą, która osiąga tę wartość z poprzedniej linii bazowej - ta linia bazowa jest skumulowaną wartością (zaokrągloną) z poprzedniego wiersza.
To działa, ponieważ jesteś nie tracisz informacji na każdym etapie, ale bardziej inteligentnie je wykorzystujesz. „Prawidłowe” zaokrąglone wartości znajdują się w ostatniej kolumnie i widać, że sumują się do 100.
Możesz zobaczyć różnicę między tym a ślepym zaokrągleniem każdej wartości, w trzeciej wartości powyżej. Podczas gdy
9.596008
normalnie zaokrąglaby w górę10
, zgromadzone71.211976
poprawnie zaokrągla w dół do71
- oznacza to, że wystarczy tylko9
dodać do poprzedniej linii bazowej62
.Działa to również dla „problematycznej” sekwencji, takiej jak trzy z grubsza wartości, przy czym jedną z nich należy zaokrąglić w górę:
1/3
źródło
26, 25, 26, 23
, drugi1, 0, 1, 0, 1, 0, ...
.Celem zaokrąglania jest wygenerowanie najmniejszej ilości błędów. Gdy zaokrąglasz jedną wartość, proces ten jest prosty i bezpośredni, a większość ludzi łatwo to rozumie. Gdy zaokrąglasz wiele liczb w tym samym czasie, proces staje się trudniejszy - musisz zdefiniować sposób łączenia błędów, tj. Co należy zminimalizować.
Dobrze głosowało odpowiedź przez Varun Vohrą minimalizuje sumę błędów bezwzględnych, i to jest bardzo proste do wykonania. Są jednak przypadki brzegowe, których nie obsługuje - co powinno wynikać z zaokrąglania
24.25, 23.25, 27.25, 25.25
? Jeden z nich należy zaokrąglić w górę zamiast w dół. Prawdopodobnie wybrałbyś pierwszy lub ostatni z listy.Być może lepiej jest użyć błędu względnego zamiast bezwzględnego błędu . Zaokrąglenie do 23,25 do 24 powoduje zmianę o 3,2%, natomiast zaokrąglenie do 27,25 do 28 zmienia tylko o 2,8%. Teraz jest wyraźny zwycięzca.
Można to jeszcze bardziej ulepszyć. Jedną z powszechnych technik jest wyrównywanie każdego błędu, dzięki czemu duże błędy liczą się nieproporcjonalnie więcej niż małe. Użyłbym również nieliniowego dzielnika, aby uzyskać błąd względny - nie wydaje się właściwe, aby błąd przy 1% był 99 razy ważniejszy niż błąd przy 99%. W poniższym kodzie użyłem pierwiastka kwadratowego.
Kompletny algorytm wygląda następująco:
Na przykład nadal możesz mieć więcej niż jedną kombinację z tą samą sumą błędów
33.3333333, 33.3333333, 33.3333333
. Jest to nieuniknione, a wynik będzie całkowicie arbitralny. Podany poniżej kod woli zaokrąglać w górę wartości po lewej stronie.Złożenie tego wszystkiego razem w Pythonie wygląda następująco.
Jak widać na ostatnim przykładzie, algorytm ten nadal może dostarczać nieintuicyjne wyniki. Mimo że 89,0 nie wymaga zaokrąglania, jedną z wartości z tej listy trzeba zaokrąglić w górę; najniższy błąd względny wynika z zaokrąglenia w górę tej dużej wartości, a nie ze znacznie mniejszych alternatyw.
Ta odpowiedź początkowo opowiadała się za każdą możliwą kombinacją zaokrąglania w górę / zaokrąglania w dół, ale jak wskazano w komentarzach, prostsza metoda działa lepiej. Algorytm i kod odzwierciedlają to uproszczenie.
źródło
if actual == 0: return 0
doerror_gen
działa świetnie.isclose
metoda na początkuround_to_100
?NIE sumuj zaokrąglonych liczb. Będziesz miał niedokładne wyniki. Suma może być znacznie mniejsza w zależności od liczby terminów i rozkładu części ułamkowych.
Wyświetl zaokrąglone liczby, ale zsumuj wartości rzeczywiste. W zależności od tego, jak prezentujesz liczby, rzeczywisty sposób na zrobienie tego może się różnić. W ten sposób dostajesz
Jakkolwiek pójdziesz, będziesz mieć rozbieżności. W twoim przykładzie nie ma sposobu na pokazanie liczb, które sumują się do 100 bez „zaokrąglenia” jednej wartości w niewłaściwy sposób (najmniejszy błąd zmieniłby się z 9,596 na 9)
EDYTOWAĆ
Musisz wybrać jedną z następujących opcji:
Przez większość czasu, gdy masz do czynienia z odsetkami # 3, najlepsza jest opcja, ponieważ bardziej oczywiste jest, gdy suma równa się 101% niż wtedy, gdy poszczególne pozycje nie sumują się do 100, a ty zachowujesz dokładność poszczególnych pozycji. „Zaokrąglanie” 9,596 do 9 jest moim zdaniem niedokładne.
Aby to wyjaśnić, czasami dodaję przypis wyjaśniający, że poszczególne wartości są zaokrąglone i mogą nie sumować 100% - każdy, kto rozumie zaokrąglanie, powinien być w stanie zrozumieć to wyjaśnienie.
źródło
Napisałem pomocnika zaokrąglania wersji C #, algorytm jest taki sam jak odpowiedź Varun Vohra , mam nadzieję, że to pomoże.
Przeszedł następujący test jednostkowy:
źródło
Możesz spróbować śledzić swój błąd z powodu zaokrąglania, a następnie zaokrąglać względem ziarna, jeśli skumulowany błąd jest większy niż ułamkowa część bieżącej liczby.
Nie jestem pewien, czy to w ogóle zadziałałoby, ale wydaje się działać podobnie, jeśli kolejność zostanie odwrócona:
Jestem pewien, że istnieją przypadki skrajne, w których może się to zepsuć, ale każde podejście będzie przynajmniej nieco arbitralne, ponieważ zasadniczo modyfikujesz swoje dane wejściowe.
źródło
Kiedyś napisałem nieziemskie narzędzie, aby znaleźć minimalne zaburzenie dla zestawu liczb odpowiadających celowi. To był inny problem, ale teoretycznie można tu zastosować podobny pomysł. W tym przypadku mamy do wyboru.
Tak więc dla pierwszego elementu możemy go zaokrąglić w górę do 14 lub w dół do 13. Koszt (w binarnym programowaniu liczb całkowitych) zrobienia tego jest mniejszy w przypadku zaokrąglania w górę niż zaokrąglania w dół, ponieważ zaokrąglanie w dół wymaga przesuń tę wartość na większą odległość. Podobnie możemy zaokrąglać każdą liczbę w górę lub w dół, więc mamy do wyboru 16 opcji.
Zwykle rozwiązałbym ogólny problem w MATLAB, tutaj za pomocą bintprog, binarnego narzędzia do programowania liczb całkowitych, ale jest tylko kilka opcji do przetestowania, więc wystarczy proste pętle, aby przetestować każdą z 16 alternatyw. Załóżmy na przykład, że mamy zaokrąglić ten zestaw jako:
Całkowity wykonany błąd bezwzględny wynosi 1,25266. Można go nieco zmniejszyć, stosując następujące alternatywne zaokrąglenie:
W rzeczywistości będzie to optymalne rozwiązanie pod względem błędu bezwzględnego. Oczywiście, jeśli będzie 20 terminów, przestrzeń wyszukiwania będzie miała rozmiar 2 ^ 20 = 1048576. W przypadku 30 lub 40 haseł przestrzeń będzie miała znaczny rozmiar. W takim przypadku należy użyć narzędzia, które może skutecznie przeszukiwać przestrzeń, być może używając schematu rozgałęzienia i powiązania.
źródło
Myślę, że następujące rzeczy osiągną to, czego szukasz
I ostatnią rzeczą, uruchomiłem funkcję używając liczb podanych pierwotnie w pytaniu, aby porównać z pożądanym wyjściem
Różniło się to od tego, czego chciało pytanie => [48, 29, 14, 9]. Nie mogłem tego zrozumieć, dopóki nie spojrzałem na całkowity margines błędu
Zasadniczo wynik mojej funkcji wprowadza najmniej błędu.
Fiddle tutaj
źródło
Nie jestem pewien, jakiego poziomu dokładności potrzebujesz, ale chciałbym po prostu dodać 1 pierwsze
n
liczby, con
jest pułapem całkowitej sumy dziesiętnej. W takim przypadku3
dodam 1 do pierwszych 3 przedmiotów, a resztę wyłożę na podłogę. Oczywiście nie jest to zbyt dokładne, niektóre liczby mogą być zaokrąglane w górę lub w dół, gdy nie powinno, ale działa dobrze i zawsze daje 100%.Tak
[ 13.626332, 47.989636, 9.596008, 28.788024 ]
byłoby,[14, 48, 10, 28]
ponieważMath.ceil(.626332+.989636+.596008+.788024) == 3
Zawsze możesz poinformować użytkowników, że liczby są zaokrąglone i mogą nie być zbyt dokładne ...
źródło
Jeśli zaokrąglasz go, nie ma dobrego sposobu na uzyskanie tego samego we wszystkich przypadkach.
Możesz wziąć dziesiętną część N procentów, które masz (w podanym przykładzie jest to 4).
Dodaj części dziesiętne. W twoim przykładzie masz całkowitą część ułamkową = 3.
Sufituj 3 liczby z najwyższymi ułamkami, a resztę podłogę.
(Przepraszamy za zmiany)
źródło
Jeśli naprawdę musisz je zaokrąglić, istnieją już bardzo dobre sugestie (największa reszta, najmniejszy błąd względny itd.).
Jest też jeden dobry powód, aby nie zaokrąglać (dostaniesz co najmniej jedną liczbę, która „wygląda lepiej”, ale jest „zła”), i jak to rozwiązać (ostrzeż swoich czytelników) i to właśnie robię.
Pozwól mi dodać „niewłaściwą” część liczbową.
Załóżmy, że masz trzy zdarzenia / byty / ... z pewnymi wartościami procentowymi, które przybliżasz jako:
Później wartości nieznacznie się zmieniają na
Pierwszy stół ma już wspomniany problem z „niewłaściwą” liczbą: 33,34 jest bliższy 33 niż 34.
Ale teraz masz większy błąd. Porównując dzień 2 z dniem 1, rzeczywista wartość procentowa dla A wzrosła o 0,01%, ale przybliżenie pokazuje spadek o 1%.
Jest to błąd jakościowy, prawdopodobnie znacznie gorszy niż początkowy błąd ilościowy.
Można opracować przybliżenie dla całego zestawu, ale być może będziesz musiał opublikować dane pierwszego dnia, więc nie będziesz wiedział o drugim dniu. Tak więc, chyba że naprawdę naprawdę musisz się zbliżyć, prawdopodobnie lepiej nie.
źródło
sprawdź, czy jest to prawidłowe, czy nie, o ile w moich testowych przypadkach mogę to uruchomić.
powiedzmy, że liczba to k;
źródło
Wdrożyłem metodę z odpowiedzi Varun Vohra tutaj zarówno dla list, jak i nagrań.
źródło
Oto prostsza implementacja w języku Python odpowiedzi @ varun-vohra:
Trzeba
math
,itertools
,operator
.źródło
Dla tych, którzy mają wartości procentowe w serii pand, oto moja implementacja metody największej reszty (jak w odpowiedzi Varuna Vohry ), w której możesz nawet wybrać ułamki dziesiętne, do których chcesz zaokrąglić.
źródło
Jest to przypadek zaokrąglania przez bankiera, zwanego również „okrągłym pół-parzystym”. Jest obsługiwany przez BigDecimal. Jego celem jest zapewnienie, że zaokrąglanie się równoważy, tzn. Nie faworyzuje ani banku, ani klienta.
źródło