- Полагаем, что x = исходное число.
- Находим целую (i) и дробную (f) часть x.
- Целую часть сохраняем как градусы, минуты или секунды.
- Если найдены три числа, конец.
- Полагаем x = f * 60 и возвращаемся к шагу 2.
Понятно, что эти вычисления производятся в цикле, однако подробности организации цикла пока не важны.
Как быть с отрицательными числами? В этом случае алгоритм немного усложняется:
- Полагаем, что x = исходное число.
- Находим целую (i) и дробную (f) часть от абсолютного значения x.
- Если x положительное, переходим к шагу 5.
- Если i не равно 0, полагаем i = -i. В противном случае, f = -f
- Целую часть сохраняем как градусы, минуты или секунды.
- Если найдены три числа, конец.
- Полагаем x = f * 60 и возвращаемся к шагу 2.
Теперь при отрицательном исходном числе, первое значащее число будет отрицательным. Так,
- 11.75 -- 11:45:0
- -11.75 -- -11:45:0
- -0.75 -- 0:-45:0
Кроме того, удобнее будет секунды возвращать как вещественное, а не целое число, потому что в противном случае при обратном пересчете (градусов, минут и секунд в десятичные градусы) не будет получаться исходное число.
- Полагаем, что x = исходное число.
- Если при текущем повторении вычисляются секунды, сохраняем x как секунды и выходим из цикла.
- Находим целую (i) и дробную (f) часть от абсолютного значения x.
- Если x положительное, переходим к шагу 6
- Если i не равно 0, полагаем i = -i. В противном случае, f = -f
- Целую часть сохраняем как градусы или минуты.
- Полагаем x = f * 60 и возвращаемся к шагу 2.
Когда в твоем распоряжении язык высокого уровня можно потратить немало времени в поисках наиболее красивой, эффективной и органичной для языка реализации этого алгоритма. В Python-е имеются как минимум три способа:
- рекурсивно;
- при помощи циклов: for и while;
- с использованием генераторов.
def dms1(x, num_vals=3):
def mul_60(x, count=1):
if count == num_vals:
return [x]
f, i = math.modf(abs(x))
if x < 0:
if i == 0:
f = -f
else:
i = -i
res = mul_60(f*60.0, count+1)
res.append(int(i))
return res
return tuple(reversed(mul_60(x)))
Функция возвращает кортеж из шестидесятиричных значений: (градусы, минуты, секунды). Именованный параметр num_vals указывает на то, сколько значений необходимо вернуть. Если секунды не нужны, num_vals=2.
Рассмотрим альтернативные способы. Чтобы не повторять один и тот же код, вынесем общую часть в отдельную функцию:
def _next_fi(f):
negative = f < 0
f, i = modf(abs(f))
if negative:
if i == 0:
f = -f
else:
i = -i
return (f * 60.0, int(i))
Функция, делающая то же самое при помощи for-цикла, выглядит менее изящно:
def dms2(x, num_vals=3):
arr = []
last_j = num_vals - 1
f = x
for j in range(0, num_vals):
if j == last_j:
arr.append(f)
else:
f, i = _next_fi(f)
arr.append(i)
return tuple(arr)
А вот вариант с циклом while:
def dms3(x, num_vals=3):
arr = []
j = 1
f = x
while(j <= num_vals):
if j == num_vals:
arr.append(f)
else:
f, i = _next_fi(f)
arr.append(i)
j += 1
return tuple(arr)
На очереди -- генераторы. Первый из них использует for, второй -- while
def dms4(x, num_vals=3):
def mul_60(f):
last_j = num_vals - 1
for j in range(0, num_vals):
if j == last_j:
yield f
else:
f, i = _next_fi(f)
yield i
return tuple([v for v in mul_60(x)])
def dms5(x, num_vals=3):
def mul_60(f):
j = 1
while(j <= num_vals):
if j == num_vals:
yield f
else:
f, i = _next_fi(f)
yield i
j += 1
return tuple([v for v in mul_60(x)])
Какая из функций работает быстрее всех? Для измерения я воспользовался стандартным методом Timer.timeit, которая по умолчанию запускает заданный код миллион раз. Получилось вот что:
Benchmarking X -> D,M,S
06.465 сек.: 'while'-цикл
07.600 сек.: 'while'-генератор
07.768 сек.: 'for'-цикл
08.544 сек.: 'for'-генератор
09.995 сек.: рекурсия
Увы, самый изящный, на мой взгляд, способ оказался самым медленным! А выиграла самая примитивная функция -- даже не генераторы.
Benchmarking X -> D,M,S
06.465 сек.: 'while'-цикл
07.600 сек.: 'while'-генератор
07.768 сек.: 'for'-цикл
08.544 сек.: 'for'-генератор
09.995 сек.: рекурсия
Комментариев нет:
Отправить комментарий