我有以下问题。
我需要构建大量的定义(*),例如
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}描述了系统的特定配置,表示存在类型1的n1个粒子,类型2的n2个粒子,......,类型为T的nT粒子。可以指数级很多这样的配置。
答案 0 :(得分:9)
你能否详细介绍一下你如何确定查找时间是指数级的?
如果它确实是指数级的,也许您可以通过在键(配置)上使用Hash
来加快速度,然后将键值对存储在{{key1,value1},{key2,value2}}
之类的列表中,并按{{ 1}}然后使用二进制搜索(应该是日志时间)。这应该非常快速地编码,但在速度方面不是最佳的。
如果这还不够快,可以考虑设置一个正确的哈希表实现(我认为这是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]]
(也适用于较大的跑步)。所以这里似乎是不变的时间。
答案 1 :(得分:3)
假设您输入的信息不像
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
]
]
给出
因此,对于较大的设置信息,矩阵方法似乎显然是可取的。
当然,如果你有真正的大数据,比你有更多的GB,那么你就是 必须使用数据库和DatabaseLink。