我正在尝试编写一个可以解决此问题的任何常规版本的函数:
如果我们列出10以下的所有自然数是3或5的倍数,我们得到3,5,6和9.这些倍数的总和是23.
查找低于1000的3或5的所有倍数的总和。
我解决这个问题的方法是:
multiples = Array(Int, 0)
[ (i % 3 == 0 || i % 5 == 0) && push!(multiples, i) for i in 1:1000 ]
sum(multiples)
我想编写一个函数,它将采用多个数组(在本例中为[3,5])和最终数字(在本例中为1000)。关键是阵列可以由任意多个数组成,而不仅仅是两个(例如,[3,5,6])。然后,该函数应为每个i % N == 0
运行N
。
如何最有效地完成这项工作?这可能涉及元编程吗? (代码不必是列表推导格式。)
谢谢!
答案 0 :(得分:3)
首先突然出现在我的脑海中,使用模数除法和功能样式:
v1(N,M) = sum(filter(k->any(j->k%j==0,M), 1:N))
但我想调查一些替代方案,因为这有两个问题:
所以这是最明显的选择,单线的 C风格版:
function v2(N,M)
sum_so_far = 0
for k in 1:N
for j in M
if k%j==0
sum_so_far += k
break
end
end
end
return sum_so_far
end
但后来我又考虑了一下,并记得在某处读取模数除法是一个缓慢的操作。我想看看IntSet
的执行情况 - 一个专门用于整数的集合。所以这是另一个单行, IntSet
s,没有使用任何模块划分,以及功能风格!
v3(N,M) = sum(union(map(j->IntSet(j:j:N), M)...))
将地图扩展为for
循环并重复将union!
应用于单个IntSet
并不是更好,因此我不会在此处包含此内容。打破这个:
IntSet(j:j:N)
是j和N之间的所有j的倍数j->IntSet(j:j:N)
是一个匿名函数,返回IntSet
map(j->IntSet(j:j:N), M)
将该函数应用于M中的每个j,并返回Vector{IntSet}
。...
" splats"向量输入union
union
创建一个IntSet
,它是其参数的并集 - 在本例中,是M的所有数字的倍数我用
对这些进行了基准测试N,M = 10000000, [3,4,5]
给你
2.857292874 seconds (826006352 bytes allocated, 10.49% gc time)
0.190581908 seconds (176 bytes allocated)
0.121820101 seconds (16781040 bytes allocated)
所以你绝对可以用更高级别的对象击败C风格的代码 - 模数我觉得很贵!关于无模数的巧妙之处在于它非常容易并行化:
addprocs(3)
@everywhere worker(j,N) = IntSet(j:j:N)
v4(N,M) = sum(union(pmap(j->worker(j,N),M)...))
@show v4(1000, [3,5])
@time v3(1000000000,[3,4,5]); # bigger N than before
@time v4(1000000000,[3,4,5]);
给出了
elapsed time: 12.279323079 seconds (2147831540 bytes allocated, 0.94% gc time)
elapsed time: 10.322364457 seconds (1019935752 bytes allocated, 0.71% gc time)
这不是更好,但我认为是这样。
答案 1 :(得分:1)
好的,这是我的最新答案。
根据@IainDunning答案中的基准,要击败的方法是他的v2
。我的下面的方法似乎很多更快,但我不够聪明,不能将它推广到长度大于2的输入向量。一位优秀的数学家应该能够改进我的答案。< / p>
快速直觉:对于length(M)=2
的情况,问题会减少到M[1]
到N
的所有倍数之和加到{{1}的所有倍数之和最多M[2]
,其中为了避免重复计算,我们需要将N
的所有倍数之和减去M[1]*M[2]
。可以为N
实现类似的算法,但重复计算问题会很快变得非常复杂很多。我怀疑这种通用算法肯定会存在(这是组合学领域一直存在的问题),但我不知道这一点。
以下是我的方法(M > 2
)与f1
的测试代码:
v2
时间是:
function f1(N, M)
if length(M) > 2
error("I'm not clever enough for this case")
end
runningSum = 0
for c = 1:length(M)
runningSum += sum(M[c]:M[c]:N)
end
for c1 = 1:length(M)
for c2 = c1+1:length(M)
temp1 = M[c1]*M[c2]
runningSum -= sum(temp1:temp1:N)
end
end
return(runningSum)
end
function v2(N, M)
sum_so_far = 0
for k in 1:N
for j in M
if k%j==0
sum_so_far += k
break
end
end
end
return sum_so_far
end
f1(1000, [3,5])
v2(1000, [3,5])
N = 10000000
M = [3,5]
@time f1(N, M)
@time v2(N, M)
抱歉,这是一个有趣的问题,但我担心我必须重新开始工作:-)如果我有机会,我会稍后再回来查看...