Ответ 1
Еще короче, немного слаще:
(x[1:] + x[:-1]) / 2
-
Это быстрее:
>>> python -m timeit -s "import numpy; x = numpy.random.random(1000000)" "x[:-1] + numpy.diff(x)/2" 100 loops, best of 3: 6.03 msec per loop >>> python -m timeit -s "import numpy; x = numpy.random.random(1000000)" "(x[1:] + x[:-1]) / 2" 100 loops, best of 3: 4.07 msec per loop
-
Это абсолютно точно:
Рассмотрим каждый элемент из
x[1:] + x[:-1]
. Поэтому рассмотримx₀
иx₁
, первый и второй элементы.x₀ + x₁
рассчитывается с точностью до точности, а затем округляется в соответствии с IEEE. Поэтому это был бы правильный ответ, если бы это было все, что было необходимо.(x₀ + x₁) / 2
составляет лишь половину этого значения. Это почти всегда можно сделать, уменьшив показатель на единицу, за исключением двух случаев:-
x₀ + x₁
переполнения. Это приведет к бесконечности (любого знака). Это не то, что нужно, поэтому расчет будет неправильным. -
x₀ + x₁
Недостатки. По мере уменьшения размера округление будет идеальным и, следовательно, вычисление будет правильным.
Во всех остальных случаях расчет будет правильным.
Теперь рассмотрим
x[:-1] + numpy.diff(x) / 2
. Это, проверяя источник, оценивает непосредственно наx[:-1] + (x[1:] - x[:-1]) / 2
поэтому рассмотрим еще раз
x₀
иx₁
.x₁ - x₀
будет иметь серьезные "проблемы" с недополнением для многих значений. Это также потеряет точность при больших отмене. Не сразу понятно, что это не имеет значения, если знаки одинаковы, хотя ошибка эффективно отменяется при добавлении. Важно то, что происходит округление.(x₁ - x₀) / 2
будет не менее округлым, но затемx₀ + (x₁ - x₀) / 2
включает другое округление. Это означает, что ошибки будут ползать. Доказательство:import numpy wins = draws = losses = 0 for _ in range(100000): a = numpy.random.random() b = numpy.random.random() / 0.146 x = (a+b)/2 y = a + (b-a)/2 error_mine = (a-x) - (x-b) error_theirs = (a-y) - (y-b) if x != y: if abs(error_mine) < abs(error_theirs): wins += 1 elif abs(error_mine) == abs(error_theirs): draws += 1 else: losses += 1 else: draws += 1 wins / 1000 #>>> 12.44 draws / 1000 #>>> 87.56 losses / 1000 #>>> 0.0
Это показывает, что для тщательно выбранной константы
1.46
полные 12-13% ответов неверны с вариантомdiff
! Как и ожидалось, моя версия всегда правильная.Теперь рассмотрим underflow. Хотя мой вариант имеет проблемы с переполнением, это гораздо менее значительная сделка, чем проблемы с отменой. Должно быть очевидно, почему двойное округление от вышеуказанной логики очень проблематично. Доказательство:
... a = numpy.random.random() b = -numpy.random.random() ... wins / 1000 #>>> 25.149 draws / 1000 #>>> 74.851 losses / 1000 #>>> 0.0
Да, он ошибается на 25%!
На самом деле, это не займет много времени, чтобы получить это до 50%:
... a = numpy.random.random() b = -a + numpy.random.random()/256 ... wins / 1000 #>>> 49.188 draws / 1000 #>>> 50.812 losses / 1000 #>>> 0.0
Ну, это не так уж плохо. Я думаю, что это всего лишь 1 наименее значимый бит, пока знаки одинаковы.
-
Итак, у вас это есть. Мой ответ лучший, если вы не найдете среднее значение двух значений, сумма которых превышает 1.7976931348623157e+308
или меньше -1.7976931348623157e+308
.