我有一个函数将整数值返回到整数输入。输出值相对稀疏;该函数仅返回2 ^ 14个唯一输出,用于输入值1 .... 2 ^ 16。我想创建一个数据集,让我快速找到产生任何给定输出的输入。
目前,我将我的数据集存储在列表地图中,每个输出值都作为输入值列表的键。这似乎很慢,似乎使用了整个堆栈空间。有没有更有效的方法来创建/存储/访问我的数据集?
加了: 事实证明,我的sparesearray()函数所花费的时间在输出值(即键)与输入值(存储在列表中的值)之间变化很大。这是一个需要许多列表的函数所需的时间,每个列表只有几个值:
? sparsearray(2^16,x->x\7);
time = 126 ms.
这是一个只需要几个列表的函数所需的时间,每个列表都有很多值:
? sparsearray(2^12,x->x%7);
time = 218 ms.
? sparsearray(2^13,x->x%7);
time = 892 ms.
? sparsearray(2^14,x->x%7);
time = 3,609 ms.
如您所见,时间呈指数增长!
这是我的代码:
\\ sparsearray takes two arguments, an integer "n" and a closure "myfun",
\\ and returns a Map() in which each key a number, and each key is associated
\\ with a List() of the input numbers for which the closure produces that output.
\\ E.g.:
\\ ? sparsearray(10,x->x%3)
\\ %1 = Map([0, List([3, 6, 9]); 1, List([1, 4, 7, 10]); 2, List([2, 5, 8])])
sparsearray(n,myfun=(x)->x)=
{
my(m=Map(),output,oldvalue=List());
for(loop=1,n,
output=myfun(loop);
if(!mapisdefined(m,output),
/* then */
oldvalue=List(),
/* else */
oldvalue=mapget(m,output));
listput(oldvalue,loop);
mapput(m,output,oldvalue));
m
}
答案 0 :(得分:1)
在某种程度上,您所看到的行为是可以预期的。 PARI似乎按值而不是引用传递列表和映射,除了用于操作它们的特殊内置函数。这可以通过创建像mylistput(list,item)=listput(list,item);
这样的包装函数来看出。当您尝试使用此功能时,您会发现它不起作用,因为它在列表的副本上运行。可以说,这是PARI中的一个错误,但也许它们有其原因。这种行为的结果是每次你向地图中存储的一个列表中添加一个元素,整个列表被复制,可能是两次。
以下是避免此问题的解决方案。
sparsearray(n,myfun=(x)->x)=
{
my(vi=vector(n, i, i)); \\ input values
my(vo=vector(n, i, myfun(vi[i]))); \\ output values
my(perm=vecsort(vo,,1)); \\ obtain order of output values as a permutation
my(list=List(), bucket=List(), key);
for(loop=1, #perm,
if(loop==1||vo[perm[loop]]<>key,
if(#bucket, listput(list,[key,Vec(bucket)]);bucket=List()); key=vo[perm[loop]]);
listput(bucket,vi[perm[loop]])
);
if(#bucket, listput(list,[key,Vec(bucket)]));
Mat(Col(list))
}
输出是与地图格式相同的矩阵 - 如果您更愿意使用地图,那么可以使用Map(...)
进行转换,但您可能需要一个矩阵进行处理,因为没有内置函数获取密钥列表的地图。
我做了一些上面的改造,尝试在C#中创建类似于GroupBy的东西。 (一个可以用于许多事情的功能)
VecGroupBy(v, f)={
my(g=vector(#v, i, f(v[i]))); \\ groups
my(perm=vecsort(g,,1));
my(list=List(), bucket=List(), key);
for(loop=1, #perm,
if(loop==1||g[perm[loop]]<>key,
if(#bucket, listput(list,[key,Vec(bucket)]);bucket=List()); key=g[perm[loop]]);
listput(bucket, v[perm[loop]])
);
if(#bucket, listput(list,[key,Vec(bucket)]));
Mat(Col(list))
}
您可以使用此VecGroupBy([1..300],i->i%7)
。
答案 1 :(得分:0)
在使用mapput
创建的地图上使用mapget
,mapisdefined
和Map()
方法。如果需要多个维度,则使用多项式或向量键。
我想这就是你已经在做的事情,我不确定有更好的方法。你有一些代码吗?根据个人经验,2 ^ 16值和2 ^ 14键不应该是速度或内存方面的问题 - 在您的实现中可能会进行一些不必要的复制。