2

我正在寻找一种使用 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:我现在有一件新事情要做。我现在必须使用 2D 列表,例如{{1,2}{4,9}{1,3}{0,5}{7,3}} 它或多或少是相同的想法,但列表的第一和第二部分必须高于阈值(两者)。示例:If lst[[1]] and lst[[2]] > threshold do the summuation对于 2D 列表的每个部分。我试图将 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;
  ];
 ]

向导先生的新快速:

   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
4

3 回答 3

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}

这是使用Sowand的方法Reap

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

于 2012-10-02T17:53:35.997 回答
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(符号化//.)的使用。

于 2012-10-01T15:03:08.587 回答
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
于 2012-10-02T14:54:06.177 回答