Dlaczego w bibliotece standardowej nie ma implementacji zakresu zmiennoprzecinkowego?
Jak wyjaśniono we wszystkich zamieszczonych tutaj postach, nie ma wersji zmiennoprzecinkowej range(). To powiedziawszy, pominięcie ma sens, jeśli weźmiemy pod uwagę, że range()funkcja jest często używana jako generator indeksu (i oczywiście oznacza to akcesor ). Tak więc, kiedy wywołujemy range(0,40), w efekcie mówimy, że chcemy 40 wartości zaczynających się od 0, do 40, ale bez wartości 40.
Kiedy weźmiemy pod uwagę, że generowanie indeksu dotyczy w takim samym stopniu liczby indeksów, jak ich wartości, użycie implementacji typu float range()w bibliotece standardowej ma mniejszy sens. Na przykład, jeśli nazwiemy funkcjęfrange(0, 10, 0.25) , spodziewalibyśmy się uwzględnienia zarówno 0, jak i 10, ale dałoby to wektor z 41 wartościami.
Zatem frange()funkcja zależna od jej użycia zawsze będzie wykazywać sprzeczne z intuicją zachowanie; albo ma zbyt wiele wartości postrzeganych z perspektywy indeksowania, albo nie obejmuje liczby, która powinna zostać zwrócona z matematycznego punktu widzenia.
Matematyczny przypadek użycia
Powiedziawszy to, jak omówiono, numpy.linspace()ładnie wypada generowanie z perspektywy matematycznej:
numpy.linspace(0, 10, 41)
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75,
2. , 2.25, 2.5 , 2.75, 3. , 3.25, 3.5 , 3.75,
4. , 4.25, 4.5 , 4.75, 5. , 5.25, 5.5 , 5.75,
6. , 6.25, 6.5 , 6.75, 7. , 7.25, 7.5 , 7.75,
8. , 8.25, 8.5 , 8.75, 9. , 9.25, 9.5 , 9.75, 10.
])
Przypadek użycia indeksowania
A jeśli chodzi o perspektywę indeksowania, napisałem nieco inne podejście z pewną sztuczną magią ciągów, która pozwala nam określić liczbę miejsc dziesiętnych.
# Float range function - string formatting method
def frange_S (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield float(("%0." + str(decimals) + "f") % (i * skip))
Podobnie możemy również skorzystać z funkcji wbudowanej roundi określić liczbę miejsc po przecinku:
# Float range function - rounding method
def frange_R (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield round(i * skip, ndigits = decimals)
Szybkie porównanie i wydajność
Oczywiście, biorąc pod uwagę powyższe omówienie, funkcje te mają dość ograniczony przypadek użycia. Niemniej jednak, oto krótkie porównanie:
def compare_methods (start, stop, skip):
string_test = frange_S(start, stop, skip)
round_test = frange_R(start, stop, skip)
for s, r in zip(string_test, round_test):
print(s, r)
compare_methods(-2, 10, 1/3)
Wyniki są identyczne dla każdego:
-2.0 -2.0
-1.67 -1.67
-1.33 -1.33
-1.0 -1.0
-0.67 -0.67
-0.33 -0.33
0.0 0.0
...
8.0 8.0
8.33 8.33
8.67 8.67
9.0 9.0
9.33 9.33
9.67 9.67
I niektóre czasy:
>>> import timeit
>>> setup = """
... def frange_s (start, stop, skip = 1.0, decimals = 2):
... for i in range(int(start / skip), int(stop / skip)):
... yield float(("%0." + str(decimals) + "f") % (i * skip))
... def frange_r (start, stop, skip = 1.0, decimals = 2):
... for i in range(int(start / skip), int(stop / skip)):
... yield round(i * skip, ndigits = decimals)
... start, stop, skip = -1, 8, 1/3
... """
>>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000))
0.024284090992296115
>>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000))
0.025324633985292166
Wygląda na to, że metoda formatowania ciągów wygrywa w moim systemie.
Ograniczenia
Na koniec zademonstrowanie punktu z powyższej dyskusji i ostatnie ograniczenie:
# "Missing" the last value (10.0)
for x in frange_R(0, 10, 0.25):
print(x)
0.25
0.5
0.75
1.0
...
9.0
9.25
9.5
9.75
Ponadto, gdy skipparametr nie jest podzielny przez stopwartość, może wystąpić luka ziewania, biorąc pod uwagę ten drugi problem:
# Clearly we know that 10 - 9.43 is equal to 0.57
for x in frange_R(0, 10, 3/7):
print(x)
0.0
0.43
0.86
1.29
...
8.14
8.57
9.0
9.43
Istnieją sposoby rozwiązania tego problemu, ale ostatecznie najlepszym podejściem byłoby po prostu użycie Numpy.