我有以下代码解决下面的Project Euler问题:
2520是可以除以1到10之间的每个数字的最小数字,没有任何余数。
从1到20的所有数字均可被整除的最小正数是多少?
我的脚本运行正常,生成2520应该为1-10做,我也有1到12252240的答案,它看起来像这样:
#!/bin/bash
for ((i=1; i<10000000000; i++))
do
if (( i%2 == 0 )) && (( i%3 == 0 )) && (( i%4 == 0 )) && (( i%5 == 0 )) &&
(( i%6 == 0 )) && (( i%7 == 0 )) && (( i%8 == 0 )) && (( i%9 == 0 )) &&
(( i%10 == 0 )) && (( i%11 == 0 )) && (( i%12 == 0 )) && (( i%13 == 0 )) &&
(( i%14 == 0 )) && (( i%15 == 0 )) && (( i%16 == 0 )) && (( i%17 == 0 )); then
# remaning terms to factor && (( i%18 == 0 )) && (( i%19 == 0 )) && (( i%20 == 0 )); then
int=$i
fi
if [[ $int ]]; then
echo "Lowest integer = '$int'"
break
else
continue
fi
done
然而,从计算时间约12个术语(约3/4秒实时)到17因子(6分钟实时)的因子跳跃是巨大的。
我还没有让全部20个因素运行,但所有Project Euler问题应该可以在几分钟内在中等功率家用电脑上解决。
所以我的问题是2折:1)我是如何接近编程的,以及2)我怎么能/应该如何做到尽可能高效?
答案 0 :(得分:1)
所以我的问题是2折:
1)我是如何接近编程的,以及
我担心你不是。您正在使用错误的工具(即shell脚本语言)来解决数学问题,并想知道为什么效果不佳。 “在家用电脑上可以在几分钟内解决”并不意味着它应该是这样的,无论你选择的工具多么不寻常。
2)我怎么能/应该怎么做才能让它尽可能高效?
不要使用bash的算术。 Bash是一个shell,这意味着它是它的核心解释器。这意味着它将花费很少的时间计算,并且非常花时间了解它应该做什么。为了说明:首先必须将复杂的公式解析为一个树,告诉bash执行事务的顺序,然后必须识别这些事情,然后bash需要通过该树工作并将所有结果保存到下一级别那个树。在没有计算时间的情况下,它所花费的少数算术指令就会花费。
看看numpy,这是一个用于数学的python模块;它做得更快。如果你不害怕编译你的东西,那么看看C ++或C,两者都是非常快速的数学库。
答案 1 :(得分:1)
在不放弃蛮力方法的情况下,以相反的顺序运行内循环大致将运行时间减半。
for ((i=1; i<100000000; ++i)); do
for ((j=17; j>1; --j)); do
(( i%j != 0 )) && break
done
((j==1)) && echo "$i" && break
done
非正式地说,几乎没有数字可以被17整除,其中几乎没有数字可以被16整除。因此,以相反的顺序运行内部循环会删除内部循环的16次迭代,大多数数字,15为其余大部分都是。
其他优化显而易见;例如,内环可以在4处结束,因为2,3和4已经被它们各自的方块覆盖(所有可被9整除的数字也可以被3整除等)。然而,与主要优化相比,这是小土豆。
(你没有明确的内循环,事实上,像你所做的那样展开循环可能会获得很小的性能提升。我把它变成了一个显式循环,主要是出于懒惰以及美学原因。)< / p>
答案 2 :(得分:0)
算术条件支持逻辑运算符。速度增益并不大,但有一些:
if (( i % 2 == 0 && i % 3 == 0 && ... ))
另请注意,当您知道不需要i % 10 == 0
和i % 2 == 0
时,会对i % 5 == 0
进行测试。
如何在不迭代所有数字的情况下获得数字的速度要快得多。
答案 3 :(得分:0)
答案不是更快的编程语言。答案是一个更聪明的算法。
你知道你的最终答案必须能被所有数字整除,所以从你的最大数字开始,只检查它的倍数。找到两个最大数字的倍数的最小数字,然后只检查下一个数字的倍数。
让我们看一下1到10的工作原理:
10 // not divisible by 9, keep adding 10's until divisible by 9
20
30
40
50
60
70
80
90 // divisible by 9, move on to 8, not divisible by 8, keep adding 90's
180
270
360 // divisible by 8, not divisible by 7, keep adding 360's
720
1080
1440
1800
2160
2520 // divisible by 7, 6, 5, 4, 3, 2, 1 so you're done!
所以只需17步,就可以得到答案。
这个在Ruby中实现的算法(以其速度而闻名)在中等速度的笔记本电脑上在4.5秒内找到了1-5000的答案。