2013-04-02 23 views
5

Python'da bir liste veya ondalık sayı dizim var. Bunları parasal tutarlar olduğu için en yakın 2 ondalık basamağa yuvarlamaya ihtiyacım var. Ancak, korunacak toplam toplama ihtiyacım var, yani 2 ondalık basamağa yuvarlanmış orijinal dizinin toplamı, dizinin yuvarlatılmış elemanlarının toplamına eşit olmalıdır.Bir Python sayı listesi döndürün ve toplamı koruyun

İşte benim kod şimdiye kadar var:

myOriginalList = [27226.94982, 193.0595233, 1764.3094, 12625.8607, 26714.67907, 18970.35388, 12725.41407, 23589.93271, 27948.40386, 23767.83261, 12449.81318] 
originalTotal = round(sum(myOriginalList), 2) 
# Answer = 187976.61 

# Using numpy 
myRoundedList = numpy.array(myOriginalList).round(2) 
# New Array = [ 27226.95 193.06 1764.31 12625.86 26714.68 18970.35 12725.41 23589.93 27948.4 23767.83 12449.81] 

newTotal = myRoundedList.sum() 
# Answer = 187976.59 

Ben toplamı 187.976,61 de olduğunu benim yeni yuvarlatılmış dizi böyle değiştiren verimli bir yol gerekir. Yuvarlatılmış girişler ve orijinal girişler arasında en büyük farkın olması nedeniyle, 2 pens farkının 7 ve 6. maddelere uygulanması gerekir.

Herhangi bir fikrin var mı? kayan noktalı sayıların kullanılmasıyla ilgili tüm uyarılar ile

+2

Muhtemelen kayan noktalı sayıları kullanarak temsil etmek olmamalıdır parasal tutarlar. "0.10" gibi çok sayıda sayı vardır, bu "float" tam olarak temsil edemez. – NPE

+0

Numpy çözümünüzde neyin var? Eğer doğru bir şekilde anlarsam, aradığınız cevap budur ... – mgilson

+0

@NPE: Bu yuvarlak değil mi? Yani, 0.1 sayısındaki herhangi bir belirsizlik, makine hassasiyeti ile karşılaştırılabilir olmalı, değil mi? – BenDundee

cevap

1

: Eğer para saklamak için yüzen kullanmamalısınız tüm

delta_pence = int(np.rint((originalTotal - np.sum(myRoundedList))*100)) 
if delta_pence > 0: 
    idx = np.argsort(myOriginalList - myRoundedList)[-delta_pence:] 
    myRoundedList[idx] += 0.01 
elif delta_pence < 0: 
    idx = np.argsort(myOriginalList - myRoundedList)[:delta_pence] 
    myRoundedList[idx] -= 0.01 

>>> myRoundedList.sum() 
187976.60999999999 
+0

Örneğinizde, 8 * [0,001] + [0,009] 'seçeneğiniz olduğunda, çözümümde atadığınız aynı türde bir hata olan 8 * [0,0] + [0.2]' olsun. Yuvarlama yuvarlanıyor. – palooh

+0

@palooh Bu bir hataydı, onu gördüğünüz için teşekkürler! Şimdi çalıştırırsanız, sonuç '7 * [0.00] + 2 * [0.01] 'dır, bu sayede ekstra yuvarlama bireysel yuvarlama hatalarını en aza indirir. – Jaime

1

İlk (kullanım yerine ondalıklar). Fakat aşağıda bazı oldukça genel bir çözüm sunuyoruz - yuvarlamadaki farklılıkları toplamanız, biriktirmeniz ve kullanmanız gerekiyor. Numara Bazı çok pythonic ayrıntılı (olup ;-) örnek:

# define your accuracy 
decimal_positions = 2 

numbers = [27226.94982, 193.0595233, 1764.3094, 12625.8607, 26714.67907, 18970.35388, 12725.41407, 23589.93271, 27948.40386, 23767.83261, 12449.81318] 
print round(sum(numbers),decimal_positions) 
>>> 187976.61 

new_numbers = list() 
rest = 0.0 
for n in numbers: 
    new_n = round(n + rest,decimal_positions) 
    rest += n - new_n 
    new_numbers.append(new_n) 

print sum(new_numbers) 
>>> 187976.61 
+0

İlk iki sayınız 'x.0049' ve' y.0001' ise, yuvarlama yönteminiz bunları x.00’a ve y.01’e dönüştürecektir. Bu, yapılacak en iyi şey gibi görünmemektedir. – Jaime

+0

Eh, listeyi her zaman "abs (n% 0.01)" azalarak sıralayabilirsiniz, ancak _rounding_ göz önünde bulundurularak belki de önemli değildir ... – palooh

4

ilk aşama, istenen sonucu gerçek toplamı arasındaki hatayı hesaplamak için:

>>> error = originalTotal - sum(myRoundedList) 
>>> error 
0.01999999996041879 

Bu da olabilir olumlu veya olumsuz. myRoundedList'daki her öğe gerçek değerin 0,005'inde olduğundan, bu hata orijinal dizinin öğesinin başına 0.01'den az olacaktır.

>>> n = int(round(error/0.01)) 
>>> n 
2 

Şimdi geriye bütün bu ayarlanmalıdır öğeleri seçmek için geçerli: Sadece ayarlanması gerekir öğe sayısını almak için 0,01 ve yuvarlak ile bölebilirsiniz. Optimal sonuçlar, ilk etapta sınıra en yakın olan değerleri ayarlamaktan kaynaklanır. Orijinal değer ile yuvarlanmış değer arasındaki fark ile sıralayarak bunları bulabilirsiniz. Uzun bir liste varsa onlar O (n * log (n)) ( n elemanlarının sıralama) çünkü

>>> myNewList = myRoundedList[:] 
>>> for _,i in sorted(((myOriginalList[i] - myRoundedList[i], i) for i in range(len(myOriginalList))), reverse=n>0)[:abs(n)]: 
    myNewList[i] += math.copysign(0.01, n) 

>>> myRoundedList 
[27226.95, 193.06, 1764.31, 12625.86, 26714.68, 18970.35, 12725.41, 23589.93, 27948.4, 23767.83, 12449.81] 
>>> myNewList 
[27226.95, 193.06, 1764.31, 12625.86, 26714.68, 18970.359999999997, 12725.42, 23589.93, 27948.4, 23767.83, 12449.81] 
>>> sum(myNewList) 
187976.61 
0

, yukarıdaki yöntemler yetersizdir. Şanslar yüksekse, bu indekslerden sadece bir kaçını (veya birini) değiştirmeniz gerekiyorsa, bir yığın kullanabilirsiniz (veya değişecek tek bir yer varsa, bir min/maks).

Bir piton kodlayıcı değilim, ama burada göz önünde bulundurarak bir çözüm yukarıdaki (ama zaten başkaları tarafından bahsedilen kayan nokta gösterimi yanlışlığı() düşünmediğini).

import math 
import heapq 

def roundtosum(l, r): 
    q = 10**(-r) 
    d = int((round(sum(l),r) - sum([ round(x, r) for x in l ])) * (10**r)) 
    if d == 0: 
     return l 
    elif d in [ -1, 1 ]: 
     c, _ = max(enumerate(l), key=lambda x: math.copysign(1,d) * math.fmod(x[1] - 0.5*q, q)) 
     return [ round(x, r) + q * math.copysign(1,d) if i == c else round(x, r) for (i, x) in enumerate(l) ] 
    else: 
     c = [ i for i, _ in heapq.nlargest(abs(d), enumerate(l), key=lambda x: math.copysign(1,d) * math.fmod(x[1] - 0.5*q, q)) ] 
     return [ round(x, r) + q * math.copysign(1,d) if i in c else round(x, r) for (i, x) in enumerate(l) ] 

d yuvarlak toplamı ve mermi toplamı arasındaki sayısal fark, bu bizim yuvarlama nasıl değiştirmeniz gerektiğini birçok yerde söyler olduğunu. d sıfır ise, kesinlikle yapacak bir şeyimiz yoktur. d1 veya -1 ise en iyi yer min veya max ile kolayca bulunabilir. Rasgele bir sayı için, en iyi D=abs(d) yerleri bulmak için heapq.nlargest'u kullanabiliriz. nlargest yapardın

Öyleyse neden, bir max var ?!min ve max, bundan daha verimli bir şekilde uygulandığından. Bu şekilde, algoritma O (n + D * log (n)) şeklindedir.

Not: Bir yığınla, bir O (n + D^2 * günlük (D)) algoritması yaratabilirsiniz; bunun nedeni, üstteki D öğelerinin yığının üst D düzeylerinde bulunması ve sipariş verilebilmesidir. O (D^2 * log (D)) adımlarında listelenir. n çok büyük ve D çok küçükse, bu çok anlamlı olabilir.

(Hak yeniden değerlendirilmesi için ayrılmış (o gece yarısını geçti çünkü).)