使用Total和阈值参数减少列表长度

时间:2012-10-01 14:44:51

标签: wolfram-mathematica

我正在寻找一种通过Total函数和阈值参数来减少巨大列表长度的方法。我想避免使用For和If(来自旧习惯)。

示例: 列出我想要“减少”:{1,5,3,8,11,3,4},其阈值为5

我想要的输出:{6,11,11,7} 这意味着我在列表的第一部分使用Total函数,并查看此函数的结果是否高于我的阈值。如果是这样,我使用Total函数的结果并转到列表的下一部分。

另一个示例是{1,1,1,1,1},其阈值为5。结果应为{5}

谢谢!

编辑:它正在运行,但速度很慢。任何想法,以便更快?

编辑2:循环内容(退出简单而不是智能)

For[i = 1, i < Length[mylist] + 1, i++,
sum = sum + mylist[[i]];
  If[sum > Threshold ,
result = Append[result , sum]; sum = 0;   ];   ];

编辑3:我现在有一件新事要做。 我现在必须使用像{{1,2}{4,9}{1,3}{0,5}{7,3}}这样的2D列表 它或多或少是相同的想法,但列表的第一和第二部分必须高于thresold的东西(两者都)。 示例:对于2D列表的每个部分,If lst[[1]] and lst[[2]] > threshold do the summuation。我试图调整Mr.Wizard的f2函数来解决这个问题,但是我没有成功。如果更容易,我可以提供2个独立列表,并使用此输入f3[lst1_,lst2_,thres_]:= Reap[Sow@Fold[If[Element of the lst1 > thr && Element of the lst2, Sow@#; #2, # + #2] &, 0, lst1]][[2, 1]]

编辑4: 你是对的,不是很清楚。但是使用Min@# > thr语句非常有效。

旧代码(丑陋且根本不聪明):

sumP = 0;
resP = {};
sumU = 0;
resU = {};
For[i = 1, i < Length[list1 + 1, i++,
 sumP = sumP + list1[[i]];
 sumU = sumU + list2[[i]];
 If[sumP > 5 && sumU > 5 ,
  resP = Append[resP, sumP]; sumP = 0;
  resU = Append[resU, sumU]; sumU = 0;
  ];
 ]
Mr.Wizard的快速新闻:

   f6[lst_, thr_] := 
 Reap[Sow@Fold[If[Min@# > thr  , Sow@#1; #2, #1 + #2] &, 0, lst]][[2, 
   1]]

那快了~40倍。非常感谢。

Thread[{resP, resU}] == f6[Thread[{list1,list2}], 5] True

3 个答案:

答案 0 :(得分:2)

我建议您使用Fold进行此类操作,并结合链接列表或SowReap来累积结果。 Append速度很慢,因为 Mathematica 中的列表是数组,每次附加元素时都必须重新分配。

从:

开始
lst = {2, 6, 4, 4, 1, 3, 1, 2, 4, 1, 2, 4, 0, 7, 4};

以下是链接列表版本:

Flatten @ Fold[If[Last@# > 5, {#, #2}, {First@#, Last@# + #2}] &, {{}, 0}, lst]
{8, 8, 7, 7, 11, 4}

这是Flatten之前的输出:

{{{{{{{}, 8}, 8}, 7}, 7}, 11}, 4}

以下是使用SowReap的方法:

Reap[Sow @ Fold[If[# > 5, Sow@#; #2, # + #2] &, 0, lst]][[2, 1]]
{8, 8, 7, 7, 11, 4}

适用于其他问题的类似方法:(1) (2)

Sow @外部的Fold有效地附加序列的最后一个元素,否则该算法会删除该元素。


以下是作为函数打包的方法,以及george,以便于比较:

f1[lst_, thr_] := 
  Flatten @ Fold[If[Last@# > thr, {#, #2}, {First@#, Last@# + #2}] &, {{}, 0}, lst]

f2[lst_, thr_] := 
  Reap[Sow@Fold[If[# > thr, Sow@#; #2, # + #2] &, 0, lst]][[2, 1]]

george[t_, thresh_] := Module[{i = 0, s},
  Reap[While[i < Length[t], s = 0; 
     While[++i <= Length[t] && (s += t[[i]]) < thresh]; Sow[s]]][[2, 1]]
  ]

时序:

big = RandomInteger[9, 500000];

george[big, 5] // Timing // First
  

1.279

f1[big, 5] // Timing // First

f2[big, 5] // Timing // First
  

0.593

     

0.468

答案 1 :(得分:1)

我将您的要求解释为:

  • 如果列表中的元素小于阈值,则将其添加到列表中的下一个元素;
  • 重复此过程,直到列表不再更改。

因此,对于阈值5和输入列表{1,5,3,8,11,3,4},你得到

{6,3,8,11,3,4}
{6,11,11,3,4}
{6,11,11,7}

修改

我现在已经针对你的问题测试了这个解决方案......

使用替换规则实施操作:

myList = {1,5,3,8,11,3,4}
threshold = 5
mylist = mylist //. {a___, b_ /; b < threshold, c_, d___} :> {a, b+c, d}

请注意使用ReplaceRepeated(符号//.)。

答案 2 :(得分:1)

这是一种显而易见的方法,速度提高了300倍......漂亮并不总是最好的。

t = Random[Integer, 10] & /@ Range[2000];
threshold = 4;
Timing[
  i = 0; 
  t0 = Reap[
     While[i < Length[t], s = 0; 
     While[++i <= Length[t] && (s += t[[i]]) < threshold ]; 
     Sow[s]]][[2, 1]]][[1]]
Total[t] == Total[t0]
Timing[ t1 = 
   t //. {a___, b_ /; b < threshold, c_, d___} -> {a, b + c, d} ][[1]]
t1 == t0