Двоичный поиск с ручным кодированием
Если кто-то готов пожертвовать краткостью ради производительности, тогда подход императивного двоичного поиска работает хорошо:
stepifyWithBinarySearch[data_] :=
With[{sortedData = SortBy[data, First], len = Length @ data}
, Module[{min = 1, max = len, i, x, list = sortedData}
, While[min <= max
, i = Floor[(min + max) / 2]
; x = list[[i, 1]]
; Which[
x == #, min = max = i; Break[]
, x < #, min = i + 1
, True, max = i - 1
]
]
; If[0 == max, 0, list[[max, 2]]]
]&
]
Оборудованнекоторые испытательные леса ...
test[s_, count_] :=
Module[{data, f}
, data = Table[{n, n^2}, {n, count}]
; f = s[data]
; Timing[Plot[f[x], {x, -5, count + 5}]]
]
... мы можем протестировать и рассчитать различные решения:
test[stepifyWithBinarySearch, 10]
На моей машине следующеевремя получено:
test[stepify (*<a href="/6410513/vozmi-nekotorye-dannye-verni-chistuy-kusochnuy-funktsiy#6410535">version 1</a>*), 100000] 57.034 s
test[stepify (*<a href="https://stackoverflow.com/questions/6853787/take-some-data-return-a-pure-piecewise-function/6853819#6853819">version 2</a>*), 100000] 40.903 s
test[stepifyWithBinarySearch, 100000] 2.902 s
Я ожидаю, что дальнейшее повышение производительности может быть получено путем компиляции различных функций, но я оставлю это в качестве упражнения для читателя.
Еще лучше: предварительно вычисленная интерполяция (ответ на комментарий дривов)
Удивительно, что некомпилированный двоичный поиск с ручным кодированием превзошел бы встроенную функцию Mathematica.Возможно, это не так удивительно для Piecewise
, поскольку, за исключением оптимизаций, на самом деле это просто прославленные выражения цепочки тестирования IF-THEN-ELSEIF произвольной сложности.Тем не менее, можно ожидать, что Interpolation
будет намного лучше, так как он специально создан для этой задачи.
Хорошая новость заключается в том, что Interpolation
действительно обеспечивает очень быстрое решение,при условии, что кто-то организует вычисление интерполяции только один раз:
stepifyWithInterpolation[data_] :=
With[{f=Interpolation[
{-1,1}*#& /@ Join[{{-9^99,0}}, data, {{9^99, data[[-1,2]]}}]
, InterpolationOrder->0 ]}
, f[-#]&
]
Это невероятно быстро, для выполнения test[stepifyWithInterpolation, 100000]
.
на моей машине требуется всего 0,016 секунды.