Определение скорости поиска: проблема с производительностью

У меня есть следующая проблема.

Мне нужно построить очень большое количество определений (*), таких как

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. может быть экспоненциально многими такими конфигурациями.

Ответ 1

Не могли бы вы подробно рассказать о том, как вы это разработали, время поиска является экспоненциальным?

Если это действительно экспоненциально, возможно, вы могли бы ускорить работу, используя Hash на ваших ключах (конфигурациях), а затем сохраните пары "ключ-значение" в списке типа {{key1,value1},{key2,value2}}, отсортированные по key, а затем используя двоичный поиск (это должно быть время регистрации). Это должно быть очень быстрым, чтобы кодировать, но не оптимально с точки зрения скорости.

Если это не достаточно быстро, можно подумать о настройке правильной реализации хэш-таблицы (что, как я думал, было тем, что сделал f[{0,1,0,1}]=3, не проверив).

Но какой-то примерный пример замедления был бы полезен для дальнейшего развития...

EDIT: Я просто попробовал

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};
[email protected]

enter image description here

(также для больших тиражей). Так что здесь кажется постоянным.

Ответ 2

Предположим, что вы вводите свою информацию не так, как

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
  ]
]

дает

DownValues approachMatrix approach

Таким образом, для более крупных наборов информации матричный подход кажется явно предпочтительным.

Конечно, если у вас действительно большие данные, скажите больше ГБ, чем у вас есть ОЗУ, тогда вы просто должны использовать базу данных и DatabaseLink.