Скорость поиска определения: проблема с производительностью
У меня следующая проблема.
Мне нужно построить очень большое количество определений (*), таких как
f[{1,0,0,0}] = 1
f[{0,1,0,0}] = 2
f[{0,0,1,0}] = 3
f[{0,0,0,1}] = 2
...
f[{2,3,1,2}] = 4
...
f[{n1,n2,n3,n4}] = some integer
...
Это всего лишь пример. Длина списка аргументов не обязательно должна быть 4, но может быть любой. Я понял, что поиск каждого значения замедляется с экспоненциальной сложностью, когда длина списка аргументов увеличивается. Возможно, это не так странно, поскольку ясно, что в принципе существует много комбинаторных взрывов в количестве определений, которые Mathematica должна хранить.
Тем не менее, я ожидал, что Mathematica будет умной, и что значение извлекается с постоянной сложностью времени. Видимо это не так.
Есть ли способ ускорить время поиска? Это, вероятно, связано с тем, как Mathematica внутренне обрабатывает поиск определения символов. Это формулирует список, пока не найдет совпадение? Кажется, это так.
Все предложения высоко ценятся. С наилучшими пожеланиями, Зоран
(*) Я работаю над программным обеспечением для стохастического моделирования, которое генерирует все конфигурации системы и должно хранить количество повторений каждой конфигурации. В этом смысле список {n1, n2, ..., nT} описывает конкретную конфигурацию системы, говоря, что есть n1 частицы типа 1, n2 частицы типа 2,..., nT частицы типа T. может быть экспоненциально много таких конфигураций.
2 ответа
Не могли бы вы рассказать подробнее о том, как вы выяснили, что время поиска является экспоненциальным?
Если это действительно экспоненциально, возможно, вы могли бы ускорить процесс с помощью Hash
на ваши ключи (конфигурации), а затем хранить пары ключ-значение в списке, как {{key1,value1},{key2,value2}}
, отсортировано по key
а затем с помощью бинарного поиска (который должен быть время журнала). Код должен быть очень быстрым, но не оптимальным с точки зрения скорости.
Если это не достаточно быстро, можно подумать о настройке правильной реализации с хеш-таблицей f[{0,1,0,1}]=3
подход делал, не проверив).
Но какой-то игрушечный пример замедления было бы полезно продолжить...
РЕДАКТИРОВАТЬ: я только что попробовал
test[length_] := Block[{f},
Do[
f[RandomInteger[{0, 10}, 100]] = RandomInteger[0, 10];,
{i, 1, length}
];
f[{0, 0, 0, 0, 1, 7, 0, 3, 7, 8, 0, 4, 5, 8, 0, 8, 6, 7, 7, 0, 1, 6,
3, 9, 6, 9, 2, 7, 2, 8, 1, 1, 8, 4, 0, 5, 2, 9, 9, 10, 6, 3, 6,
8, 10, 0, 7, 1, 2, 8, 4, 4, 9, 5, 1, 10, 4, 1, 1, 3, 0, 3, 6, 5,
4, 0, 9, 5, 4, 6, 9, 6, 10, 6, 2, 4, 9, 2, 9, 8, 10, 0, 8, 4, 9,
5, 5, 9, 7, 2, 7, 4, 0, 2, 0, 10, 2, 4, 10, 1}] // timeIt
]
с timeIt
Определено точное время даже для коротких трасс следующим образом:
timeIt::usage = "timeIt[expr] gives the time taken to execute expr,
repeating as many times as necessary to achieve a total time of \
1s";
SetAttributes[timeIt, HoldAll]
timeIt[expr_] := Module[{t = Timing[expr;][[1]], tries = 1},
While[t < 1.,
tries *= 2;
t = Timing[Do[expr, {tries}];][[1]];
];
Return[t/tries]]
а потом
out = {#, test[#]} & /@ {10, 100, 1000, 10000, 100000, 100000};
ListLogLogPlot@out
(также для больших пробегов). Так что, кажется, здесь постоянное время.
Предположим, вы вводите свою информацию не как
f[{1,0,0,0}] = 1
f[{0,1,0,0}] = 2
но в матрицу n1 x n2 x n3 x n4 m
лайк
m[[2,1,1,1]] = 1
m[[1,2,1,1]] = 2
и т.п.
(вы могли бы даже ввести значения не как f[{1,0,0,0}]=1
, но, как f[{1,0,0,0},1]
с
f[li_List, i_Integer] := Part[m, Apply[Sequence, li + 1]] = i;
f[li_List] := Part[m, Apply[Sequence, li + 1]];
где вы должны инициализировать m
например, от m = ConstantArray[0, {4, 4, 4, 4}];
)
Давайте сравним время:
testf[z_] :=
(
Do[ f[{n1, n2, n3, n4}] = RandomInteger[{1,100}], {n1,z}, {n2,z}, {n3,z},{n4,z}];
First[ Timing[ Do[ f[{n2, n4, n1, n3}], {n1, z}, {n2, z}, {n3, z}, {n4, z} ] ] ]
);
Framed[
ListLinePlot[
Table[{z, testf[z]}, {z, 22, 36, 2}],
PlotLabel -> Row[{"DownValue approach: ",
Round[MemoryInUse[]/1024.^2],
" MB needed"
}],
AxesLabel -> {"n1,n2,n3,n4", "time/s"},ImageSize -> 500
]
]
Clear[f];
testf2[z_] :=
(
m = RandomInteger[{1, 100}, {z, z, z, z}];
f2[ni__Integer] := m[[Sequence @@ ({ni} + 1)]];
First[ Timing[ Do[ f2[{n2, n4, n1, n3}], {n1, z}, {n2, z}, {n3, z}, {n4, z}] ] ]
)
Framed[
ListLinePlot[
Table[{z, testf2[z]}, {z, 22, 36, 2}],
PlotLabel -> Row[{"Matrix approach: ",
Round[MemoryInUse[]/1024.^2],
" MB needed"
}],
AxesLabel -> {"n1,n2,n3,n4", "time/s"}, ImageSize -> 500
]
]
дает
Таким образом, для больших наборов информации матричный подход кажется явно предпочтительным.
Конечно, если у вас действительно большие данные, скажем, больше ГБ, чем у вас есть ОЗУ, то вам просто нужно использовать базу данных и DatabaseLink.