在bash中解决一个(简单的)数字练习



你们中的一些人可能熟悉Project Euler,我目前正在尝试他们的一些问题,以教会自己更多的bash。它们比"script-y"更具数学性,但它有助于语法等。

目前要求我解决的问题:

如果我们列出所有10以下的自然数,它们是3或5的倍数,我们得到3、5、6和9。这些倍数之和是23。

求出1000以下所有3或5的倍数之和。

我的代码看起来是这样的:

#!/bin/bash
i="1"
for i in `seq 1 333`
do
    threes[$i]=`calc $i*3` # where 'calc' is a function written in bashrc
    #calc actually looks like: calc() {awk "BEGIN { print "$*"} }
    let "sumthrees = sumthrees + ${threes[$i]}"
done
for i in `seq 1 199`
do
    fives[$i]=`calc $i*5`
    let "sumfives = sumfives + ${fives[$i]}"
done
let "ans = $sumfives + $sumthrees"
echo "The sum of all 3 factors is $sumthrees and the sum of all five factors is $sumfives"
echo "The sum of both is $ans"
#So I can repeatedly run the script without bash remembering the variables between executions
unset i
unset fives
unset threes
unset sumfives
unset sumthrees
unset ans

到目前为止,我还没有得到正确的答案,但我对哪里出了问题已经没有什么想法了。(仅供参考,剧本目前给了我266333,我认为这个数字很接近,但我还不知道答案。)

有人能发现什么吗?就我自己的学习而言,如果有更优雅的解决方案可以让人们分享,那就太好了。

编辑

谢谢你的回答,内容丰富。既然这里有这么多有用的答案,我会接受我最喜欢的答案作为合适的线索答案。

  • 蓝月亮指出了你逻辑的实际问题。

  • 你不需要把所有的三和五都存储在数组中,因为你以后不需要它们了。

  • 如果使用./yourscriptbash script,则不需要在脚本末尾取消设置变量,因为它们将随shell实例(在任何情况下最好先初始化它们)。

  • 你不需要awk来做数学,bash做得很好。

  • seqlet不是在bash脚本中执行任何操作的最佳方式。

这是一个直接的版本:

#!/bin/bash
sum=0
for ((i=1; i<1000; i++))
do
  if (( i%3 == 0 || i%5 == 0 ))
  then
    (( sum += i ))
  fi
done
echo "$sum"

您的逻辑几乎是正确的,只是有一些数字被35除。所以你要把这些数字加两次。所以,你们得到了错误的答案。

使用另一个类似于现有循环的循环,并从结果中减去除以3和5的循环。

您可能会发现一些有用的提示:

在bash中,您使用let向shell提供一个提示,即一个变量应该被视为一个数字。所有bash变量都是字符串,但您可以对数字字符串进行算术运算。如果我说let i=1,那么我被设置为1,但如果我说let i="taco",那么$i将是0,因为它不能被读取为数字。在shell中进行数学运算时,可以实现少量的类型安全性。

Bash还有$((this))数学运算机制!您可以自己查看:echo $((2 + 2))->4,甚至与此问题更相关:echo $((6 % 3 == 0))->1

如果您不熟悉,%将第一个数除以第二个数,并返回余数;当余数为0时,意味着第一个可被第二个整除!==是一个检验两个事物是否相等的测试,对于像这样的逻辑测试,1表示true,0表示false。所以我在测试6是否可以被3整除,它就是,我得到的值是1。

测试括号[ ... ]有一个"相等性测试"标志-eq,您可以使用它来检查数学表达式是否具有特定值(有关详细信息,请参阅man test)!

$ let i=6
$ echo $((i % 3 == 0 || i % 5 == 0))
1
$ if [ $((i % 3 == 0 || i % 5 == 0)) -eq 1 ]; then echo "yes"; fi
yes

||是另一个逻辑测试-当a为trueb为true时,$((a || b))将为1(true))。

最后,您可以在for循环中执行,而不是对数字6执行此操作,并在每次找到3或5的倍数时增加一个sum变量:

let sum=0
for i in {1..1000}; do
    if [ $((i % 3 == 0 || i % 5 == 0)) -eq 1 ]; then
        let sum=$((sum + i))
    fi
done
echo $sum

这样你就有了一个可行的解决方案!

Bash有很多不错的小技巧(还有更多卑鄙丑陋的技巧),但将其用作脚本工具至少值得学习其中的一些技巧。

如何创造性地使用模函数&一些支票。那么你只有一个循环。

#!/bin/bash
i=1
while [ $i -lt 1000 ]
do 
    if [ $(($i % 3)) -eq 0 ] || [ $(($i % 5)) -eq 0 ]
        then
            sumall=$(($sumall+$i))
    fi
    i=$(($i+1))
done
echo "The sum of both is $sumall"

答案:233168

不同的解决方案:

#!/bin/bash
sum=0
for n in {1..999}; do [ $(((n%5) * (n%3))) -eq 0 ] && sum=$((sum+n)); done
echo $sum

脚本循环遍历1000以下的所有数字,测试数字mod 3和数字mod 5的乘积是否为0(只有其中一个为零,两个数字的乘积才能为零)。如果是这种情况,它会将当前数字添加到一个和中,然后打印出来。

顺便说一句,如果我是你,我会在脚本中包含calc函数的定义,以获得一个不需要特定配置的自包含解决方案。

最新更新