在 bash 中解决一个(简单的)数字练习
Solving a (simple) numeric exercise in bash
你们中的一些人可能熟悉 Project Euler,我目前正在尝试他们的一些问题来自学更多 bash。它们比 'script-y' 更数学,但它有助于语法等
目前找我解决的问题:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
我的代码是这样的:
#!/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,我认为这很接近,但我还不知道答案。)
有人能发现什么吗?对于我自己的学习,如果人们可能愿意分享更优雅的解决方案,那就太好了。
编辑
感谢您提供的所有答案,内容非常丰富。由于这里有很多有用的答案,我会接受我最喜欢的作为正确的主题答案。
你的逻辑几乎是正确的,只是有些数字被 和 3 和 5 除。所以你将这些数字相加两次.因此,你会得到错误的答案。
使用另一个与您的循环类似的循环,并从结果中减去同时除以 3 和 5 的循环。
不同的解决方案:
#!/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
函数的定义,以获得不需要你的特定配置的独立解决方案。
蓝月亮指出了你逻辑上的实际问题
您不需要将所有的三和五存储在数组中,因为您以后不需要它们。
如果您使用 ./yourscript
或 bash script
,则无需在脚本末尾取消设置变量,因为它们会随着
shell 个实例(无论如何最好先初始化它们)。
你不需要 awk
来做数学,bash
就可以了。
seq
和 let
并不是在 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"
一些您可能会觉得有用的提示:
在 bash 中,您使用 let
向 shell 提示变量应被视为数字。所有 bash 变量都是字符串,但您可以对数字字符串进行算术运算。如果我说 let i=1
然后 i 设置为 1,但是如果我说 let i="taco"
那么 $i
将是 0,因为它不能被读取为数字。在 shell.
中进行数学运算时可以实现少量类型安全
Bash 也有 $((this))
做数学的机制!大家可以自己查一下:echo $((2 + 2))
-> 4
,和这个问题更相关的是:echo $((6 % 3 == 0))
-> 1
如果你不熟悉,%
用第一个数字除以第二个数字,然后返回 余数 ;当余数为0时,表示第一个被第二个整除! ==
是一个测试,看两个东西是否相等,对于像这样的逻辑测试,1 代表真,0 代表假。所以我正在测试 6 是否可以被 3 整除,结果是,我得到的值是 1。
测试括号 [ ... ]
有一个 "test for equality" 标志 -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 为真 或 b 为真时,$((a || b))
将为 1(真)。
最后,您可以在 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 有很多不错的小技巧(还有很多丑陋的小技巧),但至少值得学习其中的一些技巧,以便将其用作脚本工具。
如何创造性地使用模函数和一些检查。那么你只有 1 个循环。
#!/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
你们中的一些人可能熟悉 Project Euler,我目前正在尝试他们的一些问题来自学更多 bash。它们比 'script-y' 更数学,但它有助于语法等
目前找我解决的问题:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
我的代码是这样的:
#!/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,我认为这很接近,但我还不知道答案。)
有人能发现什么吗?对于我自己的学习,如果人们可能愿意分享更优雅的解决方案,那就太好了。
编辑
感谢您提供的所有答案,内容非常丰富。由于这里有很多有用的答案,我会接受我最喜欢的作为正确的主题答案。
你的逻辑几乎是正确的,只是有些数字被 和 3 和 5 除。所以你将这些数字相加两次.因此,你会得到错误的答案。
使用另一个与您的循环类似的循环,并从结果中减去同时除以 3 和 5 的循环。
不同的解决方案:
#!/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
函数的定义,以获得不需要你的特定配置的独立解决方案。
蓝月亮指出了你逻辑上的实际问题
您不需要将所有的三和五存储在数组中,因为您以后不需要它们。
如果您使用
./yourscript
或bash script
,则无需在脚本末尾取消设置变量,因为它们会随着 shell 个实例(无论如何最好先初始化它们)。你不需要
awk
来做数学,bash
就可以了。seq
和let
并不是在 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"
一些您可能会觉得有用的提示:
在 bash 中,您使用 let
向 shell 提示变量应被视为数字。所有 bash 变量都是字符串,但您可以对数字字符串进行算术运算。如果我说 let i=1
然后 i 设置为 1,但是如果我说 let i="taco"
那么 $i
将是 0,因为它不能被读取为数字。在 shell.
Bash 也有 $((this))
做数学的机制!大家可以自己查一下:echo $((2 + 2))
-> 4
,和这个问题更相关的是:echo $((6 % 3 == 0))
-> 1
如果你不熟悉,%
用第一个数字除以第二个数字,然后返回 余数 ;当余数为0时,表示第一个被第二个整除! ==
是一个测试,看两个东西是否相等,对于像这样的逻辑测试,1 代表真,0 代表假。所以我正在测试 6 是否可以被 3 整除,结果是,我得到的值是 1。
测试括号 [ ... ]
有一个 "test for equality" 标志 -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 为真 或 b 为真时,$((a || b))
将为 1(真)。
最后,您可以在 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 有很多不错的小技巧(还有很多丑陋的小技巧),但至少值得学习其中的一些技巧,以便将其用作脚本工具。
如何创造性地使用模函数和一些检查。那么你只有 1 个循环。
#!/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